Python 3 fdfgen Unicode TypeError

Я использую Python 3.3 и пытаюсь использовать замечательный скрипт fgfgen / forge_fdf (спасибо, ребята, кстати).

Когда я пытаюсь запустить пример теста fdfgen, я возвращаю следующую ошибку.

        safe = utf16.replace('\x00)', '\x00\\)').replace('\x00(', '\x00\\(')
TypeError: expected bytes, bytearray or buffer compatible object

После некоторого осмотра, это, кажется, результат того, что Python 3 обрабатывает кодировку Unicode? но я не уверен Вот пример выполненного кода fdfgen, за которым следует прекрасный код fdfgen. Заранее спасибо:

>>> from fdfgen import forge_fdf
>>> fields = [('last_name', u'Spencer')]
>>> fdf = forge_fdf('SMBRPython.pdf', fields, [], [], [])

    # -*- coding: utf-8 -*-
"""
Port of the PHP forge_fdf library by Sid Steward
(http://www.pdfhacks.com/forge_fdf/)

Anders Pearson <anders@columbia.edu> at Columbia Center For New Media Teaching
and Learning <http://ccnmtl.columbia.edu/>
"""

__author__ = "Anders Pearson <anders@columbia.edu>"
__credits__ = ("Sébastien Fievet <zyegfryed@gmail.com>,"
               "Brandon Rhodes <brandon@rhodesmill.org>")

import codecs

def smart_encode_str(s):
    """Create a UTF-16 encoded PDF string literal for `s`."""
    utf16 = s.encode('utf_16_be')
    safe = utf16.replace('\x00)', '\x00\\)').replace('\x00(', '\x00\\(')
    return ('%s%s' % (codecs.BOM_UTF16_BE, safe))


def handle_hidden(key, fields_hidden):
    if key in fields_hidden:
        return "/SetF 2"
    else:
        return "/ClrF 2"


def handle_readonly(key, fields_readonly):
    if key in fields_readonly:
        return "/SetFf 1"
    else:
        return "/ClrFf 1"


def handle_data_strings(fdf_data_strings, fields_hidden, fields_readonly):
    for (key, value) in fdf_data_strings:
        if type(value) is bool:
            if value:
                yield "<<\n/V/Yes\n/T (%s)\n%s\n%s\n>>\n" % (
                    smart_encode_str(key),
                    handle_hidden(key, fields_hidden),
                    handle_readonly(key, fields_readonly),
                )
            else:
                yield "<<\n/V/Off\n/T (%s)\n%s\n%s\n>>\n" % (
                    smart_encode_str(key),
                    handle_hidden(key, fields_hidden),
                    handle_readonly(key, fields_readonly),
                )
        else:
            yield "<<\n/V (%s)\n/T (%s)\n%s\n%s\n>>\n" % (
                smart_encode_str(value),
                smart_encode_str(key),
                handle_hidden(key, fields_hidden),
                handle_readonly(key, fields_readonly),
            )


def handle_data_names(fdf_data_names, fields_hidden, fields_readonly):
    for (key, value) in fdf_data_names:
        yield "<<\n/V /%s\n/T (%s)\n%s\n%s\n>>\n" % (
            smart_encode_str(value),
            smart_encode_str(key),
            handle_hidden(key, fields_hidden),
            handle_readonly(key, fields_readonly),
        )


def forge_fdf(pdf_form_url="", fdf_data_strings=[], fdf_data_names=[], fields_hidden=[], fields_readonly=[]):

    """Generates fdf string from fields specified

    pdf_form_url is just the url for the form fdf_data_strings and
    fdf_data_names are arrays of (key,value) tuples for the form fields. FDF
    just requires that string type fields be treated seperately from boolean
    checkboxes, radio buttons etc. so strings go into fdf_data_strings, and
    all the other fields go in fdf_data_names. fields_hidden is a list of
    field names that should be hidden fields_readonly is a list of field names
    that should be readonly

    The result is a string suitable for writing to a .fdf file.

    """
    fdf = ['%FDF-1.2\n%\xe2\xe3\xcf\xd3\r\n']
    fdf.append("1 0 obj\n<<\n/FDF\n")

    fdf.append("<<\n/Fields [\n")
    fdf.append(''.join(handle_data_strings(fdf_data_strings, fields_hidden, fields_readonly)))
    fdf.append(''.join(handle_data_names(fdf_data_names, fields_hidden, fields_readonly)))
    fdf.append("]\n")

    if pdf_form_url:
        fdf.append("/F (" + smart_encode_str(pdf_form_url) + ")\n")

    fdf.append(">>\n")
    fdf.append(">>\nendobj\n")
    fdf.append("trailer\n\n<<\n/Root 1 0 R\n>>\n")
    fdf.append('%%EOF\n\x0a')

    return ''.join(fdf)

3 ответа

Решение

Кодирование создает байтовые значения, но вы используете строковые значения, чтобы попытаться заменить вещи. Вместо этого используйте байтовые литералы:

safe = utf16.replace(b'\x00)', b'\x00\\)').replace(b'\x00(', b'\x00\\(')
return (b'%s%s' % (codecs.BOM_UTF16_BE, safe))

Fdfgen был портирован на Python 3, в основном просто путем явного превращения всех строк в байтовые литералы, как упоминал Мартжин Питерс.

На fdfgen 0.11.0 с python 3.4.2 на Vista, при записи данных fdf в файл, я получил эту ошибку изначально:

    fdf_file.write(fdf)
TypeError: must be str, not bytes

и я также получил это первоначально, но не могу воспроизвести это сейчас:

can't convert 'bytes' object to str implicitly

Оказывается, единственное изменение, которое я должен был сделать, это добавить "двоичный" режим в команду открытия файла. Вместо:

fdf_file=open("testForm.fdf","w")

использовать этот:

fdf_file=open("testForm.fdf","wb")

все остальные строки были такими же, как в его примере на веб-сайте fdfgen. Надеюсь, это поможет кому-то.

Другие вопросы по тегам