Intrigato da questo post del buon @rai, da qualche giorno dedico i ritagli di tempo agli studi preliminari per riprodurre i Suoi risultati in una applicazione ad interfaccia grafica (tkinter), con posizionamento visuale di eventuali inserti di testo o immagini.
Tra le varie "utility" che mi è venuto in mente di porre in essere ho preso a studiarmi la possibilità di estrarre da un file pdf gli eventuali files di font inclusi nel pdf per provare a riutilizzarli nelle fasi di inserimento di testo aggiuntivo, per cercare di conservare i caratteri in uso. Tale estrazione è stato facile farla con le funzioni ad alto livello di pdfreader ma dato che già utilizzo pdfminer per estrarre diverse altre informazioni dalle pagine (marginatura generale del documento per tipo di pagina, rilegature, etc.) ho deciso di replicare tali operazioni con pdfminer, in maniera da ridurre i tempi di analisi del documento, che possono essere notevoli.
Pur se con qualche difficoltà (nessuna docs o discussione trovata per l'argomento specifico) mi è riuscito di risolvere la faccenda relativa alla estrazione e scrittura su disco di detti fonts inclusi in un pdf valutando i sorgenti di pdfminer e preparando il codice (dall'output molto verboso) di test ed estrazione che segue:
Codice: Seleziona tutto
# -*- coding: utf-8 -*-
import sys
from io import StringIO
from pdfminer.pdfparser import PDFParser
from pdfminer.pdfdocument import PDFDocument
from pdfminer.pdfpage import PDFPage
from pdfminer.pdfinterp import PDFResourceManager
from pdfminer.pdfinterp import PDFPageInterpreter
from pdfminer.layout import LAParams
from pdfminer.converter import PDFPageAggregator
import pdfminer
from pdfminer.pdftypes import resolve1
def createPDFDoc(f_name: str, pwd: str='') -> PDFDocument:
fp = open(f_name, 'rb')
parser = PDFParser(fp)
document = PDFDocument(parser, password=pwd)
# check if the document allow text extraction. If not abort
if not document.is_extractable:
raise "Not extractable"
else:
return document
def createDeviceInterpreter() -> tuple:
rsrcmgr = PDFResourceManager()
laparams = LAParams()
device = PDFPageAggregator(rsrcmgr, laparams=laparams)
interpreter = PDFPageInterpreter(rsrcmgr, device)
return device, interpreter
def extract_font_reference(page: PDFPage, font_dict: dict) -> None:
if not isinstance(page, PDFPage):
print('Passato oggetto', repr(type(page)), 'invece di PDFPage')
return
if not 'Resources' in page.attrs.keys(): return
if not 'Font' in page.attrs['Resources']: return
fonts = page.attrs['Resources']['Font']
if not fonts: return
for key in fonts:
print(f'Pagina {page.pageid:>3}: rif. {key:<5} = {repr(fonts[key])}')
if not repr(fonts[key]) in font_dict.keys():
font_dict[repr(fonts[key])] = fonts[key]
def font_reference_exame(ref: dict) -> None:
if not isinstance(ref, dict):
print('Referenza non valida')
return
for key in ref.keys():
o = ref[key]
if isinstance(o, pdfminer.pdftypes.PDFObjRef):
print(f'{key:<20} : oggetto {type(ref[key])}')
else:
print(f'{key:<20} : {resolve1(o)}')
if key == 'FontDescriptor':
font_descriptor_exame(resolve1(o), ref['Subtype'])
def font_descriptor_exame(des: dict, typ: object) -> None:
if not isinstance(des, dict):
print('Descrittore non valido')
return
space = ' ' * 21
for key in des.keys():
o = des[key]
if key == 'CharSet':
data = resolve1(o)
tmp = repr(data)[1:].split('/')
print(f'{space}- {key:<20}: {len(tmp)} codifiche')
elif 'FontFile' in key:
ob = resolve1(o) # provo a memorizzarlo
data = ob.get_data()
f_name = str(resolve1(des['FontName']))[2:-1].split('+')[-1].split(',')[0]
f_name += '.' + str(resolve1(typ))[2:-1]
with open(f_name, 'wb') as f_d:
f_d.write(data)
print(f'{space}- {key:<20}: uno stream => {f_name}')
else:
if isinstance(o, pdfminer.pdftypes.PDFObjRef):
print(f'{space}- {key:<20}: {repr(o)}')
else:
print(f'{space}- {key:<20}: {resolve1(o)}')
if __name__ == '__main__':
arg = sys.argv
if len(arg) < 2:
text = 'Utilizzo : python pdfm_get_fonts.py file_name'
print(text)
exit()
f_name = arg[1]
document = createPDFDoc(f_name)
device, interpreter = createDeviceInterpreter()
pages = PDFPage.create_pages(document)
fnt_dict = {}
for page in pages:
extract_font_reference(page, fnt_dict)
print(f'Rilevate {len(fnt_dict)} definizioni di fonts')
for f in fnt_dict.keys():
print(repr(fnt_dict[f]))
print('\nEsame delle risorse trovate')
for k in sorted(fnt_dict.keys()):
print()
value = resolve1(fnt_dict[k])
font_reference_exame(value)
Naturalmente, i caratteri estratti a volte sono dei ridotti sub-set ed in alcuni casi, non ho idea perché, vengono visualizzati in maniera un po' "pasticciata"

ma in genere sono abbastanza completi per un uso ordinario e chiari, tipo questo

il cui relativo output è il seguente stralcio:
Codice: Seleziona tutto
BaseFont : /'IBBBXZ+HelveticaNeue-MediumCond'
Encoding : oggetto <class 'pdfminer.pdftypes.PDFObjRef'>
FirstChar : 32
FontDescriptor : oggetto <class 'pdfminer.pdftypes.PDFObjRef'>
- Ascent : 951
- CapHeight : 714
- CharSet : 72 codifiche
- Descent : -216
- Flags : 32
- FontBBox : [-164, -216, 1031, 951]
- FontFamily : b'HelveticaNeue MediumCond'
- FontFile3 : uno stream => HelveticaNeue-MediumCond.Type1
- FontName : /'IBBBXZ+HelveticaNeue-MediumCond'
- FontStretch : /'Condensed'
- FontWeight : 500
- ItalicAngle : 0
- StemV : 108
- Type : /'FontDescriptor'
- XHeight : 538
LastChar : 121
Subtype : /'Type1'
ToUnicode : oggetto <class 'pdfminer.pdftypes.PDFObjRef'>
Type : /'Font'
Widths : [240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 352, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 519, 537, 519, 556, 481, 463, 240, 556, 222, 240, 519, 444, 240, 574, 556, 500, 556, 240, 519, 463, 537, 240, 741, 240, 240, 240, 240, 240, 240, 240, 240, 240, 463, 481, 444, 481, 463, 278, 481, 481, 222, 240, 240, 222, 740, 481, 463, 481, 240, 315, 426, 278, 481, 426, 685, 426, 426]
Ho cercato di ricorrere alle specifiche pdf 1.7 ma mi ci son perso (l'inglese mi mette ko) ... qualcuno ha conoscenze dell'argomento (fonts-pdf) sufficienti per darmi indicazioni su cosa consultare o, magari, consigliarmi su cosa utilizzare e cosa ignorare? [Edit] (Naturalmente per una presentazione allo user dei font e successivo uso per la definizione di nuovo testo)[/Edit]
Grazie dell'attenzione
