Получения номера страниц из документа с pyPDF
на данный момент я рассматриваю некоторые PDF-слияния с pyPdf, но иногда входные данные не в правильном порядке, поэтому я рассматриваю выскабливание каждой страницы для ее номера страницы, чтобы определить порядок, в котором она должна идти (например, если кто-то разделил книгу на 20 10-страничных PDF-файлов, и я хочу собрать их вместе).
у меня есть два вопроса-1.) Я знаю, что иногда номер страницы хранится в данных документа где-то, так как я видел PDF-файлы, которые отображаются в Adobe как что-то как [1243] (10 из 150), Но я читал документы такого рода в pyPDF, и я не могу найти никакой информации, указывающей номер страницы - где это хранится?
2.) Если avenue #1 недоступна, я думаю, что могу перебрать объекты на данной странице, чтобы попытаться найти номер страницы - вероятно, это будет его собственный объект, в котором есть один номер. Однако я не могу найти четкого способа определить содержимое объектов. Если я побегу:
pdf.getPage(0).getContents()
это обычно либо возвращается:
{'/Filter': '/FlateDecode'}
или возвращает список объектов IndirectObject (num, num). Я действительно не знаю, что делать с любым из них, и, насколько я могу судить, нет никакой реальной документации. Кто-нибудь знаком с такими вещами, которые могут указать мне в правильном направлении?
4 ответов
для получения полной документации см. Adobe 978-page PDF Reference. :-)
более конкретно, файл PDF содержит метаданные, которые указывают, как физические страницы PDF сопоставляются с логическими номерами страниц и как номера страниц должны быть отформатированы. Здесь вы идете за каноническими результатами. Пример 2 этой странице показывает, как это выглядит в разметке PDF. Вам придется выловить это, проанализировать и выполнить картографирование самостоятельно.
In PyPDF, чтобы получить эту информацию, попробуйте в качестве отправной точки:
pdf.trailer["/Root"]["/PageLabels"]["/Nums"]
кстати, когда вы видите IndirectObject
экземпляр, вы можете вызвать его getObject()
метод для извлечения фактического объекта, на который указывают.
ваша альтернатива, как вы говорите, проверить текстовые объекты и попытаться выяснить, какой номер страницы. Вы могли бы использовать extractText()
объекта страницы для этого, но вы получите одну строку обратно и должны попытаться выудить номер страницы из этого. (И конечно номер страницы может быть римским или алфавитным, а не числовым, и некоторые страницы не могут быть пронумерованы.) Вместо этого посмотрите, как extractText()
фактически выполняет свою работу-PyPDF написан на Python, в конце концов-и использует его в качестве основы процедуры, которая проверяет каждый текстовый объект на странице отдельно, чтобы увидеть, похож ли он на номер страницы. Будьте осторожны с TOC / индексными страницами, на которых есть много номеров страниц!
для меня сработало следующее:
from PyPDF2 import PdfFileReader
pdf = PdfFileReader(open('path/to/file.pdf','rb'))
pdf.getNumPages()
другие ответы используют PyPDF / PyPDF2, который, кажется, читает весь файл. Для больших файлов это занимает много времени.
тем временем я написал что-то быстрое и грязное, что не занимает почти столько же времени. Он делает вызов оболочки, но я не знал другого способа сделать это. Он может очень быстро получить количество страниц для PDF-файлов, которые составляют ~5000 страниц.
он работает, просто вызывая команду оболочки "pdfinfo", поэтому он, вероятно, работает только в linux. Я только проверил это на ubuntu до сих пор.
одно странное поведение, которое я видел, заключается в том, что окружающее это в блоке try/except не ловит ошибки, вы должны кроме подпроцесса.CalledProcessError.
from subprocess import check_output
def get_num_pages(pdf_path):
output = check_output(["pdfinfo", pdf_path]).decode()
pages_line = [line for line in output.splitlines() if "Pages:" in line][0]
num_pages = int(pages_line.split(":")[1])
return num_pages
ответ kindall очень хорош. Однако, поскольку образец рабочего кода был запрошен позже (dreamer) и поскольку у меня была та же проблема сегодня, я хотел бы добавить некоторые заметки.
структура pdf неоднородна; есть довольно мало вещей, на которые вы можете положиться, поэтому любой образец рабочего кода вряд ли будет работать для всех. Очень хорошее объяснение можно найти в ответ.
Как объяснил kindall, вы скорее всего, вам нужно будет изучить, с каким pdf вы имеете дело.
вот так:
import sys
import PyPDF2 as pyPdf
"""Open your pdf"""
pdf = pyPdf.PdfFileReader(open(sys.argv[1], "rb"))
"""Explore the /PageLabels (if it exists)"""
try:
page_label_type = pdf.trailer["/Root"]["/PageLabels"]
print(page_label_type)
except:
print("No /PageLabel object")
"""Select the item that is most likely to contain the information you desire; e.g.
{'/Nums': [0, IndirectObject(42, 0)]}
here, we only have "/Num". """
try:
page_label_type = pdf.trailer["/Root"]["/PageLabels"]["/Nums"]
print(page_label_type)
except:
print("No /PageLabel object")
"""If you see a list, like
[0, IndirectObject(42, 0)]
get the correct item from it"""
try:
page_label_type = pdf.trailer["/Root"]["/PageLabels"]["/Nums"][1]
print(page_label_type)
except:
print("No /PageLabel object")
"""If you then have an indirect object, like
IndirectObject(42, 0)
use getObject()"""
try:
page_label_type = pdf.trailer["/Root"]["/PageLabels"]["/Nums"][1].getObject()
print(page_label_type)
except:
print("No /PageLabel object")
"""Now we have e.g.
{'/S': '/r', '/St': 21}
meaning roman numerals, starting with page 21, i.e. xxi. We can now also obtain the two variables directly."""
try:
page_label_type = pdf.trailer["/Root"]["/PageLabels"]["/Nums"][1].getObject()["/S"]
print(page_label_type)
start_page = pdf.trailer["/Root"]["/PageLabels"]["/Nums"][1].getObject()["/St"]
print(start_page)
except:
print("No /PageLabel object")
- как вы можете видеть из спецификации ISO pdf 1.7 (соответствующий раздел здесь) есть много возможностей, как пометить страницы. В качестве простого рабочего примера рассмотрим этот скрипт, который будет иметь дело как минимум с десятичными (арабскими) и римскими цифрами:
сценарий:
import sys
import PyPDF2 as pyPdf
def arabic_to_roman(arabic):
roman = ''
while arabic >= 1000:
roman += 'm'
arabic -= 1000
diffs = [900, 500, 400, 300, 200, 100, 90, 50, 40, 30, 20, 10, 9, 5, 4, 3, 2, 1]
digits = ['cm', 'd', 'cd', 'ccc', 'cc', 'c', 'xc', 'l', 'xl', 'xxx', 'xx', 'x', 'ix', 'v', 'iv', 'iii', 'ii', 'i']
for i in range(len(diffs)):
if arabic >= diffs[i]:
roman += digits[i]
arabic -= diffs[i]
return(roman)
def get_page_labels(pdf):
try:
page_label_type = pdf.trailer["/Root"]["/PageLabels"]["/Nums"][1].getObject()["/S"]
except:
page_label_type = "/D"
try:
page_start = pdf.trailer["/Root"]["/PageLabels"]["/Nums"][1].getObject()["/St"]
except:
page_start = 1
page_count = pdf.getNumPages()
##or, if you feel fancy, do:
#page_count = pdf.trailer["/Root"]["/Pages"]["/Count"]
page_stop = page_start + page_count
if page_label_type == "/D":
page_numbers = list(range(page_start, page_stop))
for i in range(len(page_numbers)):
page_numbers[i] = str(page_numbers[i])
elif page_label_type == '/r':
page_numbers_arabic = range(page_start, page_stop)
page_numbers = []
for i in range(len(page_numbers_arabic)):
page_numbers.append(arabic_to_roman(page_numbers_arabic[i]))
print(page_label_type)
print(page_start)
print(page_count)
print(page_numbers)
pdf = pyPdf.PdfFileReader(open(sys.argv[1], "rb"))
get_page_labels(pdf)