Quando consegni documenti annotati, non consegnare semplicemente un PDF che sembra pulito.
Questo articolo fornisce una soluzione di estrazione e verifica della sicurezza a più livelli basata su PyMuPDF per risolvere i problemi di confusione tra testo e commenti, perdita di dati sensibili e confusione dei sistemi di coordinate nell'estrazione delle annotazioni PDF. Il contenuto copre il principio del livello dati indipendente, esempi di programmazione difensiva, elaborazione delle condizioni al contorno e criteri di accettazione manuale e spiega come connettere correttamente OmniDoc nel flusso di lavoro di traduzione dei documenti per il confronto e l'accettazione bilingue.
Prima di consegnare le annotazioni PDF, molte persone utilizzeranno direttamente page.get_text() per esportare un testo dall'aspetto pulito per la traduzione, l'archiviazione o la consegna al revisore successivo. Ma questo testo di solito non contiene annotazioni. Alcuni metodi di estrazione inseriranno anche commenti al centro del testo, interrompendone la semantica; i commenti sulle modifiche e le osservazioni sensibili nella barra laterale potrebbero andare persi.
Questo articolo è scritto per sviluppatori e proprietari di documenti che devono lavorare con PDF con annotazioni. Dopo la lettura, capirai perché le annotazioni sono livelli di dati indipendenti, come estrarre testo e annotazioni nei livelli, quali casi limite devono essere gestiti in modo difensivo e quali punti di accettazione devono essere confermati manualmente.
Tutti gli esempi in questo articolo utilizzano campioni sintetici. Il codice è stato controllato a campione in un ambiente isolato, ma ciò non è una garanzia di tutti i file reali o delle prestazioni del prodotto. Prima di elaborare documenti importanti, conservare gli originali e rivederli rispettando i limiti e gli elementi di accettazione nel testo.
Come estrarre il PDF con annotazioni prima della consegna? Conclusione di una frase
Tratta il testo e le annotazioni come due flussi di dati indipendenti ed estraili separatamente ed esegui un controllo di sicurezza esplicito sul contenuto delle annotazioni.
| la tua situazione | Pratiche consigliate | motivo |
|---|---|---|
Utilizzare solo get_text() per esportare il testo | Non trattarlo come completo | L'output predefinito di solito non contiene annotazioni |
| I commenti di revisione dei revisori devono essere conservati | Itera sulle annotazioni individualmente con page.annots() | Le annotazioni sono oggetti indipendenti allegati alla pagina |
| I commenti possono contenere numeri ID e codici interni. | Abbina secondo regole sensibili, contrassegna redacted=True e sostituisci con segnaposto | Le note di revisione possono contenere dati sensibili |
| La pagina viene ruotata | Per prima cosa usa page.rotation_matrix o page.derotation_matrix per trasformare le coordinate | Rettangolo di annotazione basato sulla pagina non ruotata |
| Pronto per il processo di traduzione | Pulisci prima i tag rich text, quindi controlla manualmente dopo la traduzione | Evita traduzioni contenenti codice |
Perché le annotazioni sono indipendenti dal livello dati del testo
Nella specifica PDF, il flusso di contenuto della pagina (Content Stream) e il dizionario delle annotazioni (Annots Array) sono strutturalmente separati. Il corpo è costituito da istruzioni di visualizzazione testo; le annotazioni sono oggetti indipendenti allegati alla pagina, con le proprie coordinate, tipo e attributi di contenuto. Il metodo Page.annots() di PyMuPDF restituisce un generatore che attraversa questo elenco di annotazioni indipendenti anziché il flusso di testo della pagina.
Ciò significa che non puoi dare per scontato che tutto il testo che vedi possa essere estratto nello stesso modo.
La differenza tra le annotazioni della classe mark e della classe contenuto
- Le annotazioni dei tag (come l'evidenziazione e la sottolineatura) non contengono testo in sé, ma registrano solo l'area (Quad/Retti) corrispondente al testo originale. Il testo reale può essere ottenuto solo cercando il testo tramite le coordinate.
- Annotazioni di contenuto (come note, testo libero) hanno le proprie stringhe di contenuto e possono essere lette direttamente.
- Se leggi direttamente
annot.info["content"]senza distinguere il tipo, perderai il contesto originale a cui fa riferimento l'annotazione della classe tag o lancerai un'eccezione se il contenuto è vuoto.
Esempio di sintesi: estrazione a strati e intercettazione di informazioni sensibili
Per dimostrare la corretta logica di elaborazione, abbiamo costruito uno scenario sintetico contenente tre tipiche annotazioni nel codice. I seguenti input sono esempi creati all'interno del codice, non da file di clienti reali, e sono solo a scopo dimostrativo didattico.
L'output previsto contiene tre campi strutturati:
- Frammento di testo: contiene solo il testo originale della pagina, senza alcun contenuto di annotazioni.
- Elenco annotazioni: ogni record include tipo, posizione, autore, ora e contenuto (se presente).
- Marchio di sicurezza: se il contenuto dell'annotazione corrisponde alle regole sensibili preimpostate (come numero ID, codice interno), la voce viene contrassegnata come
redacted=Truee il contenuto viene sostituito con un segnaposto.
Tre tipi di confini che devono essere difesi
- Annotazione di contenuto vuoto: inchiostro (inchiostro) o annotazioni grafiche create da alcuni lettori non hanno attributi di testo. L'accesso diretto a
.info["content"]può risultare in una stringa vuota o None, il codice deve eseguire controlli difensivi e non può presumere che tutte le annotazioni abbiano testo leggibile. - Coordinate della pagina ruotata: dopo che la pagina è stata ruotata di 90 gradi, il rettangolo di annotazione è ancora basato sulla pagina non ruotata. Se utilizzi direttamente queste coordinate per ritagliare o posizionare il testo, la posizione sarà completamente incasinata e la trasformazione delle coordinate dovrà essere eseguita prima.
- Oggetto orfano: se il documento viene chiuso o la pagina viene ricaricata durante il processo di attraversamento, l'oggetto annot corrente diventerà immediatamente non valido. Tutte le operazioni di lettura devono essere completate entro la durata di una singola pagina.
Implementazione Python: attraversamento e verifica sicura delle annotazioni
L'esempio utilizza PyMuPDF per creare un PDF di prova in memoria, dimostrando l'estrazione gerarchica e la logica di mascheramento di base. La libreria dipendente è PyMuPDF (ovvero il pacchetto pymupdf). Si prega di eseguirlo in un ambiente di dipendenza bloccato. La versione deve essere >=1.23.0 per accedere completamente all'attributo annot. Questo codice non esegue operazioni di rete o scrittura di file, viene utilizzato solo per dimostrare la logica.
import fitz # PyMuPDF
import re
# 合成示例:仅在内存中构造,无需外部文件
def create_sample_pdf():
doc = fitz.open()
page = doc.new_page()
# 插入正文
page.insert_text((72, 72), "Confidential Project Alpha Report", fontsize=12)
# 添加便签批注(含敏感信息模拟)
page.add_text_annot((200, 72), "Reviewer: SSN 123-45-6789 approved.")
# 添加高亮批注(无自身文本,关联正文)
highlight = page.add_highlight_annot(fitz.Rect(72, 60, 250, 80))
# 必须调用update()使info变更生效
highlight.set_info(title="Editor", content="Check this term")
highlight.update()
return doc
def extract_with_safety(page, sensitive_pattern=r"\d{3}-\d{2}-\d{4}"):
body_text = page.get_text("text")
annotations = []
# 显式遍历批注生成器
for annot in page.annots():
info = annot.info
# 防御性获取内容,处理None或空字符串
content = info.get("content", "") or ""
# 边界处理:敏感词检测与脱敏
is_sensitive = bool(re.search(sensitive_pattern, content))
safe_content = "[REDACTED]" if is_sensitive else content
annotations.append({
"type": annot.type[1],
"author": info.get("title", "Unknown"),
"content": safe_content,
"is_redacted": is_sensitive,
# 注意:此为未旋转坐标系,若页面有旋转需配合rotation_matrix使用
"rect": list(annot.rect)
})
return body_text, annotations
# 执行校验
doc = create_sample_pdf()
page = doc[0]
text, annots = extract_with_safety(page)
assert "SSN 123-45-6789" not in str(annots), "敏感信息未被拦截"
assert any(a["type"] == "Highlight" for a in annots), "标记类批注丢失"
assert len(text.strip()) > 0, "正文提取失败"
print(f"Extracted {len(annots)} annotations safely.")
Fondamentalmente, il codice è una programmazione difensiva: la purezza del contenuto dell'annotazione non è attendibile, né si presuppone l'integrità del tipo di annotazione. L'istruzione assert viene utilizzata per verificare la logica prevista, ma questa è solo una dimostrazione e non rappresenta una garanzia di prestazioni end-to-end per tutte le varianti PDF.
Confronto delle categorie di annotazioni
| Tipo di annotazione | Se portare il proprio testo | Come ottenere contenuti | Punti da considerare al momento della consegna |
|---|---|---|---|
| Evidenziare, sottolineare | No, registra solo le aree (Quadroni/Retti) | Controlla il testo utilizzando le coordinate | Il contesto del testo originale corrispondente deve essere mantenuto |
| Note, testo libero | Sì, viene fornito con la propria stringa di contenuto | leggere direttamente | Controlla i contenuti sensibili |
| Inchiostro, grafica, sigilli | Spesso nessun attributo di testo | Fai una chiamata breve difensiva | Conferma manualmente se si tratta di una firma o di un sigillo di approvazione |
Criteri di accettazione e limiti della revisione manuale
L'estrazione automatizzata non può coprire tutti i casi limite. Nella tabella seguente sono elencati i punti di accettazione a cui prestare attenzione durante l'handover, distinguendo tra l'ambito che il codice può gestire e gli scenari che richiedono un intervento manuale.
| Osservare i fenomeni | Elaborazione a livello di codice | Revisione umana o processo decisionale |
|---|---|---|
| Il contenuto del commento è vuoto ma l'icona esiste | Il tipo di record è Inchiostro o Timbro, il contenuto viene lasciato vuoto | Conferma se si tratta di una firma o di un timbro di approvazione e decidi se conservare l'istantanea dell'immagine |
| La posizione del commento supera CropBox | Le coordinate sono ancora valide, ma il rendering non è visibile | Determina se si tratta di un'annotazione abbandonata rimasta dalla cronologia, che di solito può essere ignorata. |
| I commenti in formato RTF contengono tag HTML | Estrai la stringa originale, mantieni i tag | Pulisci i tag o convertili in testo semplice prima della traduzione per evitare traduzioni contenenti codice. |
| Il nome dell'autore dell'annotazione è confuso | Estrai così com'è, senza congetture sulla codifica | Controlla i metadati del file di origine per confermare se è il nome di una codifica specifica |
| Thread di risposta nidificato | PyMuPDF restituisce un elenco semplice | Ricostruisci l'albero di dialogo in base a ParentID, altrimenti il contesto va perso |
Non applicabile: questa soluzione non si applica ai PDF crittografati senza fornire una password, ai PDF strutturalmente danneggiati e ai file che utilizzano estensioni private non standard per definire i tipi di annotazioni. In queste situazioni, lo script potrebbe ignorare o generare silenziosamente un'eccezione C++ di basso livello, che deve essere confermata aprendo manualmente il file sorgente utilizzando strumenti professionali.
Utilizza le liste di controllo per la traduzione dei documenti
Per prima cosa conferma l'ordine delle pagine, la posizione della fonte e l'integrità del testo, quindi invia lo stesso campione desensibilizzato al processo di traduzione. Dopo la traduzione, devi ancora verificare se titolo, testo e illustrazioni corrispondono correttamente. Non puoi semplicemente verificare se il testo è fluido.
OmniDoc fornisce un portale di traduzione di documenti multiformato e il sito Web ufficiale introduce funzioni OCR, glossario e confronto bilingue. Se il team vuole iniziare la lavorazione direttamente dal file, questo percorso può essere valutato con campioni reali. Utilizza file non sensibilizzati che sei autorizzato a caricare ed esamina i risultati in base agli elementi di controllo presenti in questo articolo. Non si afferma qui che OmniDoc utilizzi le librerie open source o gli algoritmi interni di questo articolo, né l'esempio sostituisce il test delle prestazioni del prodotto.
Riferimenti e passi successivi
- Selezionare un file di progetto storico che contenga tipi di annotazioni misti.
- Esegui lo script di verifica riportato sopra e conta la percentuale di voci che richiedono una revisione manuale.
- Se il rapporto supera le aspettative, aggiungere una fase di pre-elaborazione al processo di consegna invece di fare affidamento sugli strumenti a valle per fornire la risposta.
🎯Quale metodo è adatto a te?
Se hai solo bisogno di tradurre il testo
Puoi estrarre direttamente il testo, ma devi confermare se ci sono annotazioni mancanti nel contenuto esportato.
Se desideri consegnare il documento a qualcun altro per l'archiviazione
Allo stesso tempo, mantieni il testo principale e l'elenco delle annotazioni e contrassegna i contenuti sensibili come desensibilizzati.
Se il tuo PDF ha le pagine ruotate
Per prima cosa esegui la trasformazione delle coordinate, quindi utilizza la posizione dell'annotazione per individuare il testo.
Se il file è crittografato o strutturalmente danneggiato
Questa soluzione non è applicabile. Utilizzare manualmente strumenti professionali per aprire il file sorgente per confermare.
❓ Domande frequenti (FAQ)
1. Perché get_text() non contiene annotazioni?
L'output predefinito di solito contiene solo il corpo della pagina. Le annotazioni sono oggetti indipendenti dal flusso di testo della pagina e devono essere attraversate separatamente tramite page.annots().
2. È possibile leggere direttamente il testo di tutti i commenti?
non può. Le annotazioni di tipo segno, come l'evidenziazione e la sottolineatura, non contengono testo in sé e registrano solo aree; le annotazioni in inchiostro e grafiche potrebbero anche non avere attributi di testo e richiedere un giudizio difensivo.
3. Perché la posizione della pagina risulta incasinata dopo la rotazione?
Il rettangolo di annotazione si basa su una pagina non ruotata. Se viene utilizzato direttamente per posizionare il testo, causerà errori. È necessario utilizzare prima page.rotation_matrix o page.derotation_matrix per la trasformazione delle coordinate.
4. Le risposte nidificate andranno perse?
PyMuPDF restituisce un elenco semplice e se l'albero di dialogo non viene ricostruito in base a ParentID, il contesto andrà perso.
5. È possibile elaborare PDF crittografati con questo script?
I PDF crittografati e che non forniscono una password non rientrano nell'ambito di questa soluzione e richiedono la conferma manuale.
📝 Riepilogo
Le annotazioni costituiscono un livello dati indipendente nel PDF. Prima della consegna, il testo e le annotazioni dovrebbero essere estratti separatamente e le annotazioni dovrebbero essere esaminate per individuare eventuali contenuti sensibili. Gli script automatizzati possono coprire i casi più comuni, ma i casi limite e l'accettazione finale devono ancora essere eseguiti manualmente.
Provalo subito: carica il documento per iniziare la traduzione, mantenendo il layout originale


