2009-12-16 4 views
5

Я хотел бы использовать pyPdf для разделения PDF-файла на основе контура, где каждый пункт назначения в контуре ссылается на другую страницу в pdf.разделить pdf на основе контура

пример очертание:

 
main  --> points to page 1 
    sect1 --> points to page 1 
    sect2 --> points to page 15 
    sect3 --> points to page 22 

легко в pyPdf перебрать каждую страницу документа или каждое назначение в общих чертах документа; однако, я не могу понять, как получить номер страницы, где точки назначения.

Кто-нибудь знает, как найти ссылочный номер страницы для каждого пункта назначения в контуре?

ответ

6

я понял это:

 
    class Darrell(pyPdf.PdfFileReader): 

     def getDestinationPageNumbers(self): 
      def _setup_outline_page_ids(outline, _result=None): 
       if _result is None: 
        _result = {} 
       for obj in outline: 
        if isinstance(obj, pyPdf.pdf.Destination): 
         _result[(id(obj), obj.title)] = obj.page.idnum 
        elif isinstance(obj, list): 
         _setup_outline_page_ids(obj, _result) 
       return _result 

      def _setup_page_id_to_num(pages=None, _result=None, _num_pages=None): 
       if _result is None: 
        _result = {} 
       if pages is None: 
        _num_pages = [] 
        pages = self.trailer["/Root"].getObject()["/Pages"].getObject() 
       t = pages["/Type"] 
       if t == "/Pages": 
        for page in pages["/Kids"]: 
         _result[page.idnum] = len(_num_pages) 
         _setup_page_id_to_num(page.getObject(), _result, _num_pages) 
       elif t == "/Page": 
        _num_pages.append(1) 
       return _result 

      outline_page_ids = _setup_outline_page_ids(self.getOutlines()) 
      page_id_to_page_numbers = _setup_page_id_to_num() 

      result = {} 
      for (_, title), page_idnum in outline_page_ids.iteritems(): 
       result[title] = page_id_to_page_numbers.get(page_idnum, '???') 
      return result 

    pdf = Darrell(open(PATH-TO-PDF, 'rb')) 
    template = '%-5s %s' 
    print template % ('page', 'title') 
    for p,t in sorted([(v,k) for k,v in pdf.getDestinationPageNumbers().iteritems()]): 
     print template % (p+1,t) 
1

класс Даррелла может быть немного изменен, чтобы произвести таблицу многоуровневую содержимого для PDF

(в виде pdftoc в Pdftk инструментария.) Мое изменение добавляет еще один параметр _setup_page_id_to_num, целочисленный «уровень», который по умолчанию равен 1. Каждое обращение увеличивает уровень. Вместо того, чтобы хранить только номер страницы в результате, мы сохраняем пару номеров страниц и уровня. При использовании возвращаемого результата следует применять соответствующие изменения.

Я использую это для реализации браузера на основе браузера на основе браузера «PDF Hacks» с заголовком страницы боковой панели, который отражает закладки LaTeX, подсекции и т. Д. Я работаю над общей системой, где pdftk не может быть установлен, но где доступен python.

0

Это именно то, что я искал. Добавления Даррелла к PdfFileReader должны быть частью PyPDF2.

Я написал небольшой рецепт, который использует PyPDF2 и sejda-console для разделения PDF по закладкам. В моем случае есть несколько разделов уровня 1, которые я хочу сохранить вместе. Этот скрипт позволяет мне сделать это и дать итоговые файлы значимым именам.

import operator 
import os 
import subprocess 
import sys 
import time 

import PyPDF2 as pyPdf 

# need to have sejda-console installed 
# change this to point to your installation 
sejda = 'C:\\sejda-console-1.0.0.M2\\bin\\sejda-console.bat' 

class Darrell(pyPdf.PdfFileReader): 
    ... 

if __name__ == '__main__': 
    t0= time.time() 

    # get the name of the file to split as a command line arg 
    pdfname = sys.argv[1] 

    # open up the pdf 
    pdf = Darrell(open(pdfname, 'rb')) 

    # build list of (pagenumbers, newFileNames) 
    splitlist = [(1,'FrontMatter')] # Customize name of first section 

    template = '%-5s %s' 
    print template % ('Page', 'Title') 
    print '-'*72 
    for t,p in sorted(pdf.getDestinationPageNumbers().iteritems(), 
         key=operator.itemgetter(1)): 

     # Customize this to get it to split where you want 
     if t.startswith('Chapter') or \ 
      t.startswith('Preface') or \ 
      t.startswith('References'): 

      print template % (p+1, t) 

      # this customizes how files are renamed 
      new = t.replace('Chapter ', 'Chapter')\ 
        .replace(': ', '-')\ 
        .replace(': ', '-')\ 
        .replace(' ', '_') 
      splitlist.append((p+1, new)) 

    # call sejda tools and split document 
    call = sejda 
    call += ' splitbypages' 
    call += ' -f "%s"'%pdfname 
    call += ' -o ./' 
    call += ' -n ' 
    call += ' '.join([str(p) for p,t in splitlist[1:]]) 
    print '\n', call 
    subprocess.call(call) 
    print '\nsejda-console has completed.\n\n' 

    # rename the split files 
    for p,t in splitlist: 
     old ='./%i_'%p + pdfname 
     new = './' + t + '.pdf' 
     print 'renaming "%s"\n  to "%s"...'%(old, new), 

     try: 
      os.remove(new) 
     except OSError: 
      pass 

     try: 
      os.rename(old, new) 
      print' succeeded.\n' 
     except: 
      print' failed.\n' 

    print '\ndone. Spliting took %.2f seconds'%(time.time() - t0) 
0

Небольшое обновление для @darrell класса, чтобы иметь возможность анализировать UTF-8 контуров, которые я отправляю в качестве ответа, потому что комментарий будет трудно читать.

Проблема в pyPdf.pdf.Destination.title, которые могут быть возвращены в двух вариантах:

  • pyPdf.generic.TextStringObject
  • pyPdf.generic.ByteStringObject

так, что выходной сигнал из _setup_outline_page_ids() функции возвращает также два различных типа для title объекта, который терпит неудачу с UnicodeDecodeError, если заголовок заголовка содержит что-нибудь, кроме ASCII.

Я добавил этот код, чтобы решить эту проблему:

if isinstance(title, pyPdf.generic.TextStringObject): 
    title = title.encode('utf-8') 

всего класса:

class PdfOutline(pyPdf.PdfFileReader): 

    def getDestinationPageNumbers(self): 

     def _setup_outline_page_ids(outline, _result=None): 
      if _result is None: 
       _result = {} 
      for obj in outline: 
       if isinstance(obj, pyPdf.pdf.Destination): 
        _result[(id(obj), obj.title)] = obj.page.idnum 
       elif isinstance(obj, list): 
        _setup_outline_page_ids(obj, _result) 
      return _result 

     def _setup_page_id_to_num(pages=None, _result=None, _num_pages=None): 
      if _result is None: 
       _result = {} 
      if pages is None: 
       _num_pages = [] 
       pages = self.trailer["/Root"].getObject()["/Pages"].getObject() 
      t = pages["/Type"] 
      if t == "/Pages": 
       for page in pages["/Kids"]: 
        _result[page.idnum] = len(_num_pages) 
        _setup_page_id_to_num(page.getObject(), _result, _num_pages) 
      elif t == "/Page": 
       _num_pages.append(1) 
      return _result 

     outline_page_ids = _setup_outline_page_ids(self.getOutlines()) 
     page_id_to_page_numbers = _setup_page_id_to_num() 

     result = {} 
     for (_, title), page_idnum in outline_page_ids.iteritems(): 
      if isinstance(title, pyPdf.generic.TextStringObject): 
       title = title.encode('utf-8') 
      result[title] = page_id_to_page_numbers.get(page_idnum, '???') 
     return result 
Смежные вопросы