Статьи

Преобразование слайдов презентации в блог-пост HTML с изображениями

Вот скрипт Python для преобразования PDF в серию тегов HTML <img> с альтернативными текстами. Это делает презентацию подходящей встроенной для публикации в блоге и чтения на мобильном устройстве и тому подобное.

Мотивация для создания этого сценария заключается в том, что сервисы, создающие встроенные средства просмотра слайд-шоу (SlideShare, Speaker Deck), создают вывод на основе <iframe>, который не подходит для чтения RSS. Например, службы агрегации блогов и вскоре перестал работать Google Reader могут исключить презентации <iframe> из RSS-каналов. Кроме того, созданные средства просмотра <iframe> не подходят для мобильных устройств. Если вы хотите поделиться своими слайдами в блоге, используя старый HTML и изображения, это самый надежный способ сделать это.

Этот сценарий также демонстрирует мощь сценариев Python в сочетании с такими инструментами командной строки UNIX, как Ghostscript PDF Renderer, и высокой доступностью готовых библиотек Python из репозитория PyPi .

Пример рабочего процесса:

  • Экспорт презентации из Apple Keynote в файл PDF. В диалоговом окне «Экспорт» снимите отметку с даты и добавьте границы вокруг слайдов .
  • Запустите сценарий для созданного файла PDF, чтобы преобразовать его в серию файлов JPEG и фрагмент HTML с тегами <img>.
  • При желании сценарии добавляют полный префикс URL-адреса к <img src>, поэтому вам не нужно вручную связывать изображения с абсолютным URL-адресом вашего хостинга.
  • Скопируйте и вставьте сгенерированный HTML в ваш блог

Протестировано с Apple Keynote, экспортировало PDF-файлы, но подход должен работать для любого содержимого PDF.

Смотрите пример сообщения в блоге и презентацию .

slide7

Исходный код ниже. Полный исходный код с README и инструкциями по использованию доступен на Github .

"""
    PDF to HTML converter.

"""

import os
import sys

import pyPdf
from pyPdf.pdf import ContentStream
from pyPdf.pdf import TextStringObject

SLIDE_TEMPLATE = u'<p><img src="{prefix}{src}" alt="{alt}" /></p>'

# You can pass Ghostscript binary to the script as an environment variable.
GHOSTSCRIPT = os.environ.get("GHOSTSCRIPT", "gs")

def create_images(src, target, width=620, height=480):
    """ Create series of images from slides.

    http://right-sock.net/linux/better-convert-pdf-to-jpg-using-ghost-script/

    :param src: Source PDF file

    :param target: Target folder
    """

    if target.endswith("/"):
        target = target[0:-1]

    # Generated filenames
    ftemplate = "%(target)s/slide%%d.jpg" % locals()

    # gs binary
    ghostscript = GHOSTSCRIPT

    # Export magic of doing
    # Note: Ghostscript 9.06 crashed for me
    # had to upgrade 9.07
    # This command does magic of anti-aliasing text and settings output JPEG dimensions correctly
    cmd = "%(ghostscript)s -dNOPAUSE -dPDFFitPage -dTextAlphaBits=4 -sDEVICE=jpeg -sOutputFile=%(ftemplate)s -dJPEGQ=80 -dDEVICEWIDTH=%(width)d -dDEVICEHEIGHT=%(height)d  %(src)s -c quit"
    cmd = cmd % locals()  # Apply templating

    if os.system(cmd):
        raise RuntimeError("Command failed: %s" % cmd)

def extract_text(self):
    """ Patched extractText() from pyPdf to put spaces between different text snippets.
    """
    text = u""
    content = self["/Contents"].getObject()
    if not isinstance(content, ContentStream):
        content = ContentStream(content, self.pdf)
    # Note: we check all strings are TextStringObjects.  ByteStringObjects
    # are strings where the byte->string encoding was unknown, so adding
    # them to the text here would be gibberish.
    for operands, operator in content.operations:
        if operator == "Tj":
            _text = operands[0]
            if isinstance(_text, TextStringObject):
                text += _text
        elif operator == "T*":
            text += "\n"
        elif operator == "'":
            text += "\n"
            _text = operands[0]
            if isinstance(_text, TextStringObject):
                text += operands[0]
        elif operator == '"':
            _text = operands[2]
            if isinstance(_text, TextStringObject):
                text += "\n"
                text += _text
        elif operator == "TJ":
            for i in operands[0]:
                if isinstance(i, TextStringObject):
                    text += i

        if text and not text.endswith(" "):
            text += " "  # Don't let words concatenate

    return text

def scrape_text(src):
    """ Read a PDF file and return plain text of each page.

    http://stackoverflow.com/questions/25665/python-module-for-converting-pdf-to-text

    :return: List of plain text unicode strings
    """

    pages = []

    pdf = pyPdf.PdfFileReader(open(src, "rb"))
    for page in pdf.pages:
        text = extract_text(page)
        pages.append(text)

    return pages

def create_index_html(target, slides, prefix):
    """ Generate HTML code for `<img>` tags.
    """

    out = open(target, "wt")

    print >> out, "<!doctype html>"
    for i in xrange(0, len(slides)):
        alt = slides[i]  # ALT text for this slide
        params = dict(src=u"slide%d.jpg" % (i+1), prefix=prefix, alt=alt)
        line = SLIDE_TEMPLATE.format(**params)
        print >> out, line.encode("utf-8")

    out.close()

def main():
    """ Entry point. """

    if len(sys.argv) < 3:
        sys.exit("Usage: pdf2html.py mypresentation.pdf targetfolder [image path prefix]")

    src = sys.argv[1]
    folder = sys.argv[2]

    if len(sys.argv) > 3:
        prefix = sys.argv[3]
    else:
        prefix = ""

    if not os.path.exists(folder):
        os.makedirs(folder)

    alt_texts = scrape_text(src)

    target_html = os.path.join(folder, "index.html")

    create_index_html(target_html, alt_texts, prefix)

    create_images(src, folder)

if __name__ == "__main__":
    main()