Un contrassegno di oscuramento disegnato senza un colore /IC può uscire dalla fase di applicazione come un varco trasparente o un contorno vuoto, mentre il commento del revisore su di esso resta nel file. PDFlibPas applica le annotazioni Redact con ApplyRedactions: il contenuto coperto va prima, l'overlay arriva da /RO o da /IC con il nero come ripiego, e il dizionario del contrassegno viene svuotato
Contrassegnare è reversibile, applicare no
ISO 32000-1 §12.5.6.23 divide l'oscuramento in identificazione del contenuto e rimozione del contenuto. Durante l'identificazione una annotazione Redact è solo una proposta che gli utenti possono ancora vedere, spostare e ridefinire, ed è ciò che rende possibile un giro di revisione. La rimozione è lasciata all'applicazione, con requisiti fermi: tutto ciò che l'annotazione identifica va via, l'annotazione va via, e la parte coperta di ogni immagine ha i suoi dati distrutti, perché un percorso di ritaglio o una image mask sopra i pixel non valgono. Le chiavi sono elencate nella Table 192 di ISO 32000-1 (Table 195 in ISO 32000-2), e la maggior parte descrive come dovrebbe apparire la pagina dopo la rimozione invece di che cosa rimuovere
| Chiave | Ruolo nella specifica | Che cosa ci fa ApplyRedactions |
|---|---|---|
QuadPoints | Regione da rimuovere come n quadrilateri; sostituisce Rect come regione quando presente | Elimina il bounding box di ogni quadrilatero separatamente; un array la cui lunghezza non è multiplo di 8 ripiega su Rect |
Rect | Regione da rimuovere quando QuadPoints è assente | Usato con gli angoli invertiti normalizzati |
IC | Riempimento DeviceRGB dopo la rimozione; assente significa trasparente; ignorato quando esiste RO | Conservato quando presente, aggiunto come nero quando assente |
RO | Overlay di Form XObject; ha precedenza su IC, OverlayText, DA e Q | Installato come appearance normale e appiattito sulla pagina |
OverlayText, Repeat | Etichetta disegnata sopra la regione, opzionalmente ripetuta | Disegnata nella overlay generata, ripetuta quando Repeat è true |
DA, Q | Font, dimensione e colore dell'etichetta; giustificazione | Dimensione e colore letti da DA, allineamento da Q |
Creare contrassegni con AddRedactAnnotation
TPDFlib.AddRedactAnnotation(Left, Top, Width, Height, FillR, FillG, FillB, OverlayText, RepeatOverlay) aggiunge un contrassegno alla pagina selezionata e restituisce il nuovo numero di annotazioni della pagina, che è anche l'indice 1-based del contrassegno, oppure 0 quando nulla è stato aggiunto. Le coordinate seguono l'origine e le unità correnti, così l'esempio passa all'origine in alto a sinistra, dove Top è il bordo superiore. La chiamata scrive /Rect, una /IC dalle componenti del riempimento e un /DA fisso di /F0 10 Tf 1 1 1 rg, testo bianco a 10 punti; un OverlayText non vuoto aggiunge /OverlayText, /Repeat e /Q 1 per un'etichetta centrata. Le annotazioni Redact sono una funzionalità di PDF 1.7, così un'intestazione più vecchia viene portata a 1.7. Nessun appearance stream viene ancora scritto; il contrassegno in sospeso appare come lo disegna l'applicazione del revisore. Con l'etichetta fissata al bianco, abbinate OverlayText a un riempimento scuro, dato che sul giallo pallido l'etichetta sparisce. I contrassegni possono essere creati in ogni edizione di PDFlibPas
uses
System.SysUtils, PDFlibrary;
procedure MarkForReview(const SourceFile, ReviewFile: string);
var
Pdf: TPDFlib;
Index: Integer;
begin
Pdf := TPDFlib.Create;
try
if Pdf.LoadFromFile(SourceFile, '') <> 1 then
raise Exception.CreateFmt('Cannot open %s', [SourceFile]);
Pdf.SetOrigin(1); // origine in alto a sinistra: Top è il bordo superiore
Pdf.SetMeasurementUnits(0); // punti
if Pdf.SelectPage(1) <> 1 then
raise Exception.Create('Page 1 not found');
// Nome del teste: riempimento nero, etichetta bianca centrata
Index := Pdf.AddRedactAnnotation(72, 180, 220, 14, 0, 0, 0, 'REDACTED');
if Index = 0 then
raise Exception.Create('Mark was not added');
// Blocco conto: ripete il codice di esenzione su un box più alto
Pdf.AddRedactAnnotation(72, 410, 300, 40, 0, 0, 0, '(b)(6)', 1);
// Solo contrassegni; ogni byte del contenuto originale è ancora presente
Pdf.SaveToFile(ReviewFile);
finally
Pdf.Free;
end;
end;
I QuadPoints decidono la zona di rimozione; le risposte conservano il loro testo
Quando un contrassegno porta /QuadPoints, sono quei quadrilateri a decidere cosa ApplyRedactions rimuove, e Rect li delimita solo per la visualizzazione. I contrassegni fatti con strumenti di selezione testo hanno di solito un quadrilatero per riga selezionata, così ridimensionare un tale contrassegno con SetAnnotRect sposta la sua cornice sullo schermo e lascia la zona di rimozione dov'era; SetAnnotQuadPoints è la chiamata che sposta la regione, e GetAnnotQuadCount restituisce 0 per un contrassegno che si affida al solo Rect. Il testo allegato a un contrassegno segue una regola diversa. Gli strumenti di revisione tengono la nota in /Contents e l'autore in /T, e una nota su un contrassegno di oscuramento tende a descrivere proprio la cosa che viene rimossa ("SSN del cliente, conferma con l'ufficio legale"). ApplyRedactions svuota il dizionario del contrassegno stesso, ma le risposte in thread sotto di esso tramite /IRT sono annotazioni separate, cancellate solo quando il loro rettangolo sovrappone una regione di oscuramento, così una risposta parcheggiata nel margine conserva il suo testo. Il listato qui sotto stampa ogni contrassegno con il conteggio dei suoi quadrilateri e le risposte in thread sotto di esso
procedure ListPendingMarks(Pdf: TPDFlib);
var
P, I, Parent: Integer;
begin
for P := 1 to Pdf.PageCount do
begin
if Pdf.SelectPage(P) <> 1 then
Continue;
for I := 1 to Pdf.AnnotationCount do
if Pdf.GetAnnotType(I) = 'Redact' then
Writeln(Format('p%d #%d by %s, quads=%d: %s',
[P, I, Pdf.GetAnnotTitle(I), Pdf.GetAnnotQuadCount(I),
Pdf.GetAnnotContentsEx(I)]))
else
begin
Parent := Pdf.GetAnnotReplyTo(I); // 0 quando /IRT è assente
if (Parent > 0) and (Pdf.GetAnnotType(Parent) = 'Redact') then
Writeln(Format(' reply #%d to #%d: %s',
[I, Parent, Pdf.GetAnnotContentsEx(I)]));
end;
end;
end;
Quale overlay appare dopo ApplyRedactions?
Una voce /RO vince ogni volta che è presente, come richiede la regola di precedenza della Table 192. ApplyRedactions installa quel Form XObject come appearance normale dell'annotazione (/AP /N), elimina qualsiasi stato di appearance e lo appiattisce, così /IC, /OverlayText, /DA e /Q non hanno effetto su quel contrassegno. Il piazzamento è la parte più sottile. La specifica dice che l'origine della overlay si allinea con l'angolo inferiore sinistro di Rect, mentre l'appiattimento mappa la BBox della Form attraverso la sua Matrix sul Rect, lo stesso piazzamento BBox e Matrix che l'appiattimento applica ai widget dei campi modulo. Una overlay la cui BBox è [0 0 w h], dimensionata sul rettangolo, con una Matrix identità appare uguale sotto entrambe le letture, così richiedete quella forma quando i vostri strumenti generano contrassegni /RO
Senza /RO, ApplyRedactions normalizza il dizionario e costruisce l'appearance da sé. Elimina /C e /BS e imposta /Border a [0 0 0]: quelle voci descrivono il contrassegno in sospeso, di solito un contorno colorato, e inciderlo nella pagina finale farebbe sembrare incompiuto un oscuramento finito. Con il bordo via, il riempimento copre l'intero rettangolo. L'etichetta viene disegnata dentro la Form generata, con dimensione e colore da /DA, giustificazione da /Q, ripetizione da /Repeat, e il nome di font F0 legato nelle risorse proprie di quella Form alla Helvetica standard. Quel legame conta quando i file viaggiano tra strumenti: ISO 32000-1 §12.7.3.3 definisce un nome di font DA come una voce nelle risorse predefinite del modulo interattivo (/DR), un documento senza campi modulo non ne ha, e un'altra applicazione che applichi lo stesso contrassegno può sostituire un font di ripiego. PDFlibPas salta quella ricerca, e con Helvetica l'etichetta si addice a testo in scrittura latina come REDACTED o un codice di esenzione
Una /IC assente diventa un riquadro nero
Il default della specifica per una /IC assente è un interno trasparente, e PDFlibPas se ne discosta di proposito. Dopo la rimozione, un interno trasparente mostra la carta, un varco bianco dove prima c'era un nome, e quel varco appare uguale a un impaginato che non ha mai avuto nulla lì. Un lettore non distingue oscurato da bianco, e un revisore non può confermare che l'oscuramento sia avvenuto senza confrontare il testo estratto. Una cornice vuota, che è ciò che produce una fase di applicazione quando appiattisce l'appearance in sospeso di un contrassegno solo contorno, fallisce diversamente: si legge come una proposta su cui nessuno ha agito. Abbiamo pesato di seguire il default alla lettera e abbiamo scelto il riquadro nero, perché il segno visibile che qualcosa è stato trattenuto fa parte di ciò che un documento oscurato dice al suo lettore, e la scelta tocca solo i contrassegni il cui autore non ha mai dichiarato un riempimento. Una /IC già nel dizionario viene usata qualunque sia il suo valore, così un produttore che ha chiesto il bianco ottiene il bianco
Che cosa non c'è più prima che la overlay venga disegnata
ApplyRedactions lavora pagina per pagina e completa tutta la rimozione prima di appiattire una qualsiasi overlay, così una overlay non può mai essere colta dalla propria regione. Per ogni pagina raccoglie un rettangolo per quadrilatero, o il Rect dei contrassegni senza quadrilateri, e ripulisce ogni layer di contenuto contro ciascuno di loro:
- Il testo viene tagliato glifo per glifo, misurato dai font di pagina e dallo stato di testo completo, e il resto di una stringa parzialmente coperta resta alla sua posizione originale
- Percorsi vettoriali, immagini inline e shading che dipingono dentro la regione vengono eliminati dal content stream
- Si entra nei Form XObject in modo ricorsivo; una Form che altre pagine o piazzamenti disegnano pure viene oscurata attraverso una copia privata, così quelle pagine conservano l'originale
- Gli image XObject perdono solo i pixel coperti, sovrascritti con nero (inchiostro K pieno per CMYK); le immagini JPEG, JPEG 2000, JBIG2 e CCITT vengono decodificate e rimemorizzate come stream Flate senza perdita
- Nel PDF taggato,
/Alte/ActualTextallegati al marked content rimosso vengono cancellati nel contenuto di pagina e nell'albero delle strutture - Le altre annotazioni il cui rettangolo tocca la regione vengono eliminate, e i campi modulo i cui widget stanno lì perdono i loro valori
Il passo dei pixel è la clausola immagini della specifica in pratica. Un rettangolo nero dipinto sopra un passaporto scansionato soddisfa l'occhio e boccia comunque §12.5.6.23, perché i campioni originali si decodificano appena qualcuno estrae l'immagine. Lo stesso motore sta dietro a RedactRegion, che rimuove contenuto da un rettangolo che passate direttamente e fa coppia con SanitizeDocument, senza leggere alcuna annotazione. Una volta che il contenuto è andato, ogni contrassegno viene appiattito e il suo dizionario pulito fino a /Type e /Subtype. L'appiattimento rimuove l'annotazione dall'array /Annots della pagina, ma il dizionario resta comunque un oggetto nel documento, e un salvataggio che lo scrivesse consegnerebbe /Contents, /RC, /T e il resto della traccia di revisione al destinatario
Salvare senza la revisione non oscurata
Un salvataggio incrementale disfa tutto quanto sopra. Ai sensi di §7.5.6 (lo stesso numero in ISO 32000-1 e ISO 32000-2), un aggiornamento incrementale accoda gli oggetti cambiati in coda al file e lascia i byte originali intatti, così la revisione originale, con il suo contenuto di pagina, immagini e contrassegni annotati, resta leggibile da chiunque tagli il file al %%EOF precedente; ExtractDocumentRevision nell'edizione Small Business di PDFlibPas lo fa in una chiamata. Dopo ApplyRedactions, scrivete un nuovo file con SaveToFile, mai SaveChanges, che accoda. Prima di salvare, GarbageCollectObjects passa in rassegna la tabella degli oggetti e butta gli oggetti che più nulla referenzia, come gli appearance stream che i contrassegni portavano prima della fase di applicazione; un salvataggio completo salta quella passata a meno che SetGarbageCollect la abiliti, oppure il campo GarbageCollect del record TPDFlibSaveOptions che passate a SaveToFileOptions sia impostato. I metadati sono il buco che resta. SanitizeDocument(SANITIZE_METADATA or SANITIZE_XMP_PACKET) cancella Author, Subject, Keywords, Creator e Producer e rimuove lo stream XMP, lasciando il Title da controllare a voi. Evitate SANITIZE_ALL qui: include SANITIZE_ANNOTATIONS ed elimina ogni annotazione rimasta nel documento, tra cui le note che intendevate conservare
ApplyRedactions, come RedactRegion, è disponibile nell'edizione Enterprise. Su un TPDFlib semplice restituisce 0 e imposta LastErrorCode a PDFLIB_ERROR_ENTERPRISE_REQUIRED (900), così il passo di rilascio usa TPDFlibEnterprise dall'unità PDFlibEnterprise
uses
System.SysUtils, PDFlibrary, PDFlibEnterprise;
procedure ApplyAndRelease(const ReviewFile, ReleaseFile: string);
var
Pdf: TPDFlibEnterprise;
Applied, Orphans: Integer;
begin
Pdf := TPDFlibEnterprise.Create;
try
if Pdf.LoadFromFile(ReviewFile, '') <> 1 then
raise Exception.CreateFmt('Cannot open %s', [ReviewFile]);
Applied := Pdf.ApplyRedactions; // ogni contrassegno Redact su ogni pagina
if Applied = 0 then
raise Exception.CreateFmt('No marks applied (error %d)',
[Pdf.LastErrorCode]);
// Info e XMP possono ancora nominare ciò che le pagine non mostrano più
Pdf.SanitizeDocument(SANITIZE_METADATA or SANITIZE_XMP_PACKET);
Orphans := Pdf.GarbageCollectObjects;
// Riscrittura completa su un nuovo file; mai SaveChanges dopo la rimozione
if Pdf.SaveToFile(ReleaseFile) <> 1 then
raise Exception.Create('Save failed');
Writeln(Format('%d marks applied, %d unreferenced objects dropped',
[Applied, Orphans]));
finally
Pdf.Free;
end;
end;
Atteso: ApplyRedactions restituisce il numero di contrassegni Redact appiattiti su tutte le pagine, 2 per la copia di revisione scritta da MarkForReview, e GarbageCollectObjects restituisce quanti oggetti non referenziati ha buttato la passata
Qualunque strumento abbia disegnato un contrassegno, le sue voci /RO e /IC stabiliscono cosa vede il destinatario sulla pagina, mentre la fase di applicazione in PDFlibPas Enterprise for Delphi stabilisce cosa è andato via sotto