From c7210f2b787fe636cd0aadc5c23c6e292f9f508c Mon Sep 17 00:00:00 2001 From: Roland Date: Mon, 19 Dec 2022 11:27:17 -0700 Subject: [PATCH] Getting title/author from PDF metadata. --- test/example.pdf | Bin 8130 -> 11901 bytes wrolpi/files/indexers.py | 41 +++++++++++++++++++++++++++++----- wrolpi/files/test/test_lib.py | 12 +++++----- 3 files changed, 42 insertions(+), 11 deletions(-) diff --git a/test/example.pdf b/test/example.pdf index bc2b9cd2bac8ca8c23e6fb5d3723508e96cf61ec..bbcda4bbd0f32742840804b49ad56ab7a72fe711 100644 GIT binary patch delta 3848 zcmchaOK;mo5XX^TS}f3WFLt{?A<#i9?Jgz01WJY;BoZKQ4BKhb*MXMS5^ISh$W=xA zqH}Kr^hE;Xm_vbnjiR5S$DWFImXZP*38m0bNt0Cd*>1SBO(xfZ_EeR$!v-VH5^x^rpq+V?7Mo$W&NIE zb{tiAy8XUsLV73>UM$iDlmZzB)ljA4Jati}%SV3Tr2f@K7*3u=Lr-wTG)!@L`4h5j zL@sV3j!DlG-Xt6$a_WtEf(i}1n<`aRrf#}grG`SO0s(erVhpE`;&9>x!-ulin*e3RcMG}629fjG zrO0ag{NxGb%!82_W7RPARkt99>Bq=EP9tw{z2E|0%1*VEzh3>OV1L+BzR^zTNz~(G z|IuyC&u_oF7~EbDtf6uY-%#32r_mHDhtugKNSbL&@t|hB3AU7pg0myyx}`jxLQk8` z%}0QlT4TBbPU7KJ^Q`ZJi-q(7w=HEXMARf?KA+d-^;#H@2(v7UP?e}^6;4!>n?QJJ zHAp@O7YJug`aBuLew2ME(y4bD&O}R55MGBDNsxv#UzbICD{(DKoXsCCN6Ei=*WS+G7v(N9e<%6xpHd9j zo_{{lcd|>TaxZ>qN^$I4_Ki-RtBQcNj$0p#-h`%udB_e&|@1EhTZ z1-^>qt*Pvdzm-xPTuJ^tDJA)Pr@W27f|ShvZH~5TyHeQ?e;1`V#4`N*Q_Aq)2jva^ zYe<>=-|(_a*bKQa_AvjaH58?8}?T|r(s&7e6=#GXIlakiM8`?nl36Opqp6j*LU%frt;*}b}9o7Z=2 rcjZ?%0^94)7*@8rCKrQ>6AU+#`f6RnrrI#q>zynSW169_*Sr4!nexRu delta 180 zcmewxbI5+fU75*TvdWXoWIH$vEDa4!fM_$DTq+}zq0!`AdE0tZa}!5XV*@uQM;CJ- zVdiM&WMpCJWMpXI=4#|(>}1DfLr{rAESJ7ZezJ2$YI1gQX|964qlKw~n}w^3xrLLZ xk(rB|v9U4GcvmMwQzutbS4$TzJ3Fr8lEk8t3jU(hG%j;f3v(`2RabvEE&w1CE{^~J diff --git a/wrolpi/files/indexers.py b/wrolpi/files/indexers.py index 5b13695a..865c2b91 100644 --- a/wrolpi/files/indexers.py +++ b/wrolpi/files/indexers.py @@ -1,3 +1,4 @@ +import dataclasses import pathlib import re from abc import ABC @@ -122,6 +123,12 @@ class TextIndexer(Indexer, ABC): return words +@dataclasses.dataclass +class PDFMetadata: + title: str = None + author: str = None + + @register_indexer('application/pdf') class PDFIndexer(Indexer, ABC): """Uses PyPDF2 to extract text from a PDF.""" @@ -129,24 +136,47 @@ class PDFIndexer(Indexer, ABC): @classmethod def create_index(cls, file): path: pathlib.Path = file.path.path if hasattr(file.path, 'path') else file.path - a = cls.get_title(file) + + file_title = super().get_title(file) + + reader = None + if PdfReader is not None: + reader = PdfReader(path) + + data = cls.get_pdf_metadata(reader, file) if path.stat().st_size > FILE_MAX_PDF_SIZE: logger.warning(f'PDF too large to fully index: {path}') - return a, None, None, None + return data.title, data.author, file_title, None words = '' try: # PDFs are complex, don't fail to create title index if text extraction fails. - words = '\n'.join(truncate_generator_bytes(cls.get_words(path), FILE_MAX_TEXT_SIZE)) + words = '\n'.join(truncate_generator_bytes(cls.get_words(reader, path), FILE_MAX_TEXT_SIZE)) except Exception as e: logger.error(f'Failed to index {path}', exc_info=e) if PYTEST: raise - return a, None, None, words + return data.title, data.author, file_title, words @classmethod - def get_words(cls, path: pathlib.Path) -> Generator[str, None, None]: + def get_pdf_metadata(cls, reader: PdfReader, file) -> PDFMetadata: + """Extract title/author from the PDF metadata.""" + path = file.path.path if hasattr(file.path, 'path') else file.path + + data = PDFMetadata() + + if PdfReader is None: + logger.error(f'Cannot get title of {path} PyPDF2 is not installed.') + data.title = super().get_title(file) + return data + + data.title = reader.metadata.title + data.author = reader.metadata.author + return data + + @classmethod + def get_words(cls, reader: PdfReader, path: pathlib.Path) -> Generator[str, None, None]: """ Reads all text facing up in a PDF. @@ -157,7 +187,6 @@ class PDFIndexer(Indexer, ABC): yield '' return - reader = PdfReader(path) for page in reader.pages: # Get all text facing up. text = page.extract_text(0) diff --git a/wrolpi/files/test/test_lib.py b/wrolpi/files/test/test_lib.py index 77be95aa..ae1ae2c2 100644 --- a/wrolpi/files/test/test_lib.py +++ b/wrolpi/files/test/test_lib.py @@ -488,10 +488,12 @@ def test_pdf_indexer(example_pdf): a_text, b_text, c_text, d_text = indexers.PDFIndexer.create_index(file) - # The file name. - assert a_text == ['example', 'pdf'] - assert b_text is None - assert c_text is None + # The title extracted from the PDF. + assert a_text == 'WROLPi Test PDF' + # The author. + assert b_text == 'roland' + # The parsed file name. + assert c_text == ['example', 'pdf'] # Both pages of the example PDF are extracted. assert d_text == 'Page one\nPage two' @@ -505,7 +507,7 @@ def test_pdf_indexer_max_size(example_pdf): a_text, b_text, c_text, d_text = indexers.PDFIndexer.create_index(file) # The file name is still indexed. - assert a_text == ['example', 'pdf'] + assert a_text == 'WROLPi Test PDF' assert d_text is None