Functional qpdfmerge with PyPDF2 for DocumentInfo block

Tests mostly passing. For the moment this is the new default.

Although PyPDF2 produces a PDF-1.3 which will be wrong for some contents
and possible should be repaired with qpdf. Again.

Looks like it could work better to merge PyPDF2 and fix everything
with qpdf.
This commit is contained in:
James R. Barlow
2016-08-02 16:48:13 -07:00
parent b17c6a146d
commit ebe68de4ff
2 changed files with 109 additions and 47 deletions
+104 -42
View File
@@ -126,6 +126,9 @@ parser.add_argument(
parser.add_argument(
'--image-dpi', metavar='DPI', type=int,
help="for input image instead of PDF, use this DPI instead of file's")
parser.add_argument(
'--output-type', choices=['pdfa', 'pdf'], default='pdf',
help="choose output type")
metadata = parser.add_argument_group(
"Metadata options",
@@ -1031,6 +1034,40 @@ def tesseract_ocr_and_render_pdf(
log=log)
def get_pdfmark(base_pdf):
def from_document_info(key):
# pdf.documentInfo.get() DOES NOT behave as expected for a dict-like
# object, so call with precautions. TypeError may occur if the PDF
# is missing the optional document info section.
try:
s = base_pdf.documentInfo[key]
return str(s)
except (KeyError, TypeError):
return ''
pdfmark = {
'/Title': from_document_info('/Title'),
'/Author': from_document_info('/Author'),
'/Keywords': from_document_info('/Keywords'),
'/Subject': from_document_info('/Subject'),
}
if options.title:
pdfmark['/Title'] = options.title
if options.author:
pdfmark['/Author'] = options.author
if options.keywords:
pdfmark['/Keywords'] = options.keywords
if options.subject:
pdfmark['/Subject'] = options.subject
pdfmark['/Creator'] = '{0} {1} / Tesseract OCR{2} {3}'.format(
parser.prog, VERSION,
'+PDF' if options.pdf_renderer == 'tesseract' else '',
tesseract.version())
return pdfmark
@active_if(options.output_type == 'pdfa')
@transform(
input=repair_pdf,
filter=formatter(r'\.repaired\.pdf'),
@@ -1042,37 +1079,7 @@ def generate_postscript_stub(
log):
pdf = pypdf.PdfFileReader(input_file)
def from_document_info(key):
# pdf.documentInfo.get() DOES NOT behave as expected for a dict-like
# object, so call with precautions. TypeError may occur if the PDF
# is missing the optional document info section.
try:
s = pdf.documentInfo[key]
return str(s)
except (KeyError, TypeError):
return ''
pdfmark = {
'title': from_document_info('/Title'),
'author': from_document_info('/Author'),
'keywords': from_document_info('/Keywords'),
'subject': from_document_info('/Subject'),
}
if options.title:
pdfmark['title'] = options.title
if options.author:
pdfmark['author'] = options.author
if options.keywords:
pdfmark['keywords'] = options.keywords
if options.subject:
pdfmark['subject'] = options.subject
pdfmark['creator'] = '{0} {1} / Tesseract OCR{2} {3}'.format(
parser.prog, VERSION,
'+PDF' if options.pdf_renderer == 'tesseract' else '',
tesseract.version())
pdfmark = get_pdfmark(pdf)
generate_pdfa_def(output_file, pdfmark)
@@ -1093,12 +1100,13 @@ def skip_page(
re_symlink(input_file, output_file, log)
@active_if(options.output_type == 'pdfa')
@merge(
input=[add_text_layer, render_hocr_debug_page, skip_page,
tesseract_ocr_and_render_pdf, generate_postscript_stub],
output=os.path.join(work_folder, 'merged.pdf'),
extras=[_log, _pdfinfo, _pdfinfo_lock])
def merge_pages(
def merge_pages_ghostscript(
input_files,
output_file,
log,
@@ -1119,23 +1127,77 @@ def merge_pages(
return key
pdf_pages = sorted(input_files, key=input_file_order)
#log.debug("Final pages: " + "\n".join(pdf_pages))
#ghostscript.generate_pdfa(pdf_pages, output_file, options.jobs or 1)
pdf_pages.pop()
qpdf.merge(pdf_pages, output_file)
log.debug("Final pages: " + "\n".join(pdf_pages))
ghostscript.generate_pdfa(pdf_pages, output_file, options.jobs or 1)
@transform(
input=merge_pages,
filter=formatter(),
output=options.output_file,
@active_if(options.output_type == 'pdf')
@merge(
input=[add_text_layer, render_hocr_debug_page, skip_page,
tesseract_ocr_and_render_pdf],
output=os.path.join(work_folder, 'merged_nometadata.pdf'),
extras=[_log, _pdfinfo, _pdfinfo_lock])
def copy_final(
input_file,
def merge_pages_qpdf(
input_files,
output_file,
log,
pdfinfo,
pdfinfo_lock):
def input_file_order(s):
'''Sort order: All rendered pages followed
by their debug page.'''
key = int(os.path.basename(s)[0:6]) * 10
if 'debug' in os.path.basename(s):
key += 1
return key
pdf_pages = sorted(input_files, key=input_file_order)
log.debug("Final pages: " + "\n".join(pdf_pages))
qpdf.merge(pdf_pages, output_file)
@active_if(options.output_type == 'pdf')
@merge(
input=[merge_pages_qpdf, repair_pdf],
output=os.path.join(work_folder, 'merged.pdf'),
extras=[_log, _pdfinfo, _pdfinfo_lock])
def copy_metadata(
input_files,
output_file,
log,
pdfinfo,
pdfinfo_lock):
merged_file = next(
(ii for ii in input_files if ii.endswith('merged_nometadata.pdf')))
metadata_file = next(
(ii for ii in input_files if ii.endswith('.repaired.pdf')))
metadata_pdf = pypdf.PdfFileReader(metadata_file)
pdfmark = get_pdfmark(metadata_pdf)
pdfmark['/Producer'] = 'PyPDF2 ' + pypdf.__version__
merged_pdf = pypdf.PdfFileReader(merged_file)
output_pdf = pypdf.PdfFileWriter()
output_pdf.appendPagesFromReader(merged_pdf)
output_pdf.addMetadata(pdfmark)
with open(output_file, 'wb') as f:
output_pdf.write(f)
@merge(
input=[merge_pages_ghostscript, copy_metadata],
output=options.output_file,
extras=[_log, _pdfinfo, _pdfinfo_lock])
def copy_final(
input_files,
output_file,
log,
pdfinfo,
pdfinfo_lock):
input_file = next((ii for ii in input_files if ii.endswith('.pdf')))
shutil.copy(input_file, output_file)
+5 -5
View File
@@ -88,11 +88,11 @@ def _get_pdfa_def(icc_profile, icc_identifier, pdfmark):
t = Template(pdfa_def_template)
result = t.substitute(icc_profile=icc_profile,
icc_identifier=icc_identifier,
title=pdfmark_utf16.get('title', ''),
author=pdfmark_utf16.get('author', ''),
subject=pdfmark_utf16.get('subject', ''),
creator=pdfmark_utf16.get('creator', ''),
keywords=pdfmark_utf16.get('keywords', ''))
title=pdfmark_utf16.get('/Title', ''),
author=pdfmark_utf16.get('/Author', ''),
subject=pdfmark_utf16.get('/Subject', ''),
creator=pdfmark_utf16.get('/Creator', ''),
keywords=pdfmark_utf16.get('/Keywords', ''))
return result