# This is a port of forge_fdf.php by Sid Steward (www.pdfhacks.com/forge_fdf/)
# Created by Timothy Stebbing  in python.
# Updated by Thomas Heetderks (2006-01-11) and Sid Steward
# Fixed by Maciej Litwiniuk (2008-04-24) GaldoMedia.pl
# Use at your own risk!

def escapePDFString(string):
    rv = ''
    back = chr(0x5c)
    for c in string:
        if c in ['(',')','\\']:
            rv += '\\'+c # escape the character
        elif ord(c) < 32 or 126 < ord(c):
            rv += '\\%s3o' % (ord(c),) # use an octal code
        else:
            rv += c # let it pass through
    return rv

def escapePDFName(string):
    rv = ''
    for c in string:
        if ord(c) < 33 or 126 < ord(c) or c == '#':
            rv += '#%s2x' % (ord(c),) # use a hex code
        else:
            rv += c # let it pass through
    return rv

def burstDotsIntoDicts(FDFData):
    rv = {}
    for k, v in FDFData.items():
        if '.' in k:
            lhs, rhs = k.split('.',2)
            if not rv.has_key(lhs):
                rv[lhs] = {}
            if type(rv[lhs]) is dict:
                rv[lhs] = {'' : lhs} #apparently this should not happen
            rv[lhs][rhs] = v
        else:
            if rv.has_key(k) and type(rv[k]) is dict:
                rv[k][''] = v
            else:
                rv[k] = v   
    for k, v in rv.items():
        if type(v) is dict:
            rv[k] = burstDotsIntoDicts(v)
    return rv

def forgeFDFFieldFlags(fdf, fieldName, fieldsHidden, fieldsReadonly):
    if fieldName in fieldsHidden:
        fdf += '/SetF 2 ' #set
    else:
        fdf += '/ClrF 2 ' #clear
    if fieldName in fieldsReadonly:
        fdf += '/SetFf 1' #set
    else:
        fdf += '/ClrFf 1' #clear
    return fdf

def forgeFDFFields(fdf, fdfData, fieldsHidden, 
    fieldsReadonly, accumulatedName, fdfDataIsStrings):
    if len(accumulatedName) > 0:
        accumulatedName += '.' #append period seperator
    for k, v in fdfData.items():
        fdf += "<< ";
        if v is dict:
            fdf += "/T ("+escapePDFString( k )+") "
            fdf += "/Kids [ "
            fdf = forgeFDFFieldFlags(fdf, v, fieldsHidden, fieldsReadonly, "%s%s" % (accumulatedName, k), fdfDataIsStrings)
            fdf += "] "
        else:
            if fdfDataIsStrings:
                fdf += "/V ("+escapePDFString( str(v) )+")"
            else:
                fdf += "/V /"+escapePDFName( v )+" "
            fdf += "/T ("+escapePDFString( k )+") "
            fdf = forgeFDFFieldFlags(fdf, v, fieldsHidden, fieldsReadonly)
        
        fdf += '>> \x0d' #close dictionary
    return fdf 

def forgeFDFFieldsStrings(fdf, fdfDataStrings, fieldsHidden, fieldsReadonly):
    return forgeFDFFields(fdf, fdfDataStrings, fieldsHidden, fieldsReadonly,
        '', True)

def forgeFDFFieldsNames(fdf, fdfDataStrings, fieldsHidden, fieldsReadonly):
    return forgeFDFFields(fdf, fdfDataStrings, fieldsHidden, fieldsReadonly,
        '', False)

def forgeFDF(PDFForumURL, FDFDataStrings, FDFDataNames, fieldsHidden, fieldsReadonly):
    fdf = ""
    fdf = "%FDF-1.2\x0d%\xe2\xe3\xcf\xd3\x0d\x0a"
    fdf=fdf+ "1 0 obj\x0d<< " 
    fdf=fdf+ "\x0d/FDF << "
    fdf=fdf+ "/Fields [ "
    fdf_data_strings= burstDotsIntoDicts( FDFDataStrings );
    print fdf_data_strings
    fdf = forgeFDFFieldsStrings(fdf, fdf_data_strings, fieldsHidden, fieldsReadonly)
    fdf_data_names= burstDotsIntoDicts( FDFDataNames );
    fdf = forgeFDFFieldsNames(fdf, fdf_data_names, fieldsHidden, fieldsReadonly)
    fdf=fdf+ "] \x0d"
    if len(PDFForumURL):
        fdf=fdf+ "/F (" + escapePDFString(PDFForumURL)+") \x0d"
    fdf=fdf+ ">> \x0d"; #// close the FDF dictionary
    fdf=fdf+ ">> \x0dendobj\x0d"#; // close the Root dictionary
    #// trailer; note the "1 0 R" reference to "1 0 obj" above
    fdf=fdf+ "trailer\x0d<<\x0d/Root 1 0 R \x0d\x0d>>\x0d"#;
    fdf=fdf+ "%%EOF\x0d\x0a"#;
    import os
    return fdf#;
