Znacznik redakcji narysowany bez koloru /IC może wyjść z kroku aplikowania jako przezroczysta dziura albo pusty obrys, podczas gdy komentarz recenzenta przy nim zostaje w pliku. PDFlibPas aplikuje adnotacje Redact przez ApplyRedactions: przykryta treść idzie najpierw, nakładka pochodzi z /RO albo z /IC z czernią jako fallback, a słownik znacznika jest opróżniany
Znakowanie jest odwracalne, aplikowanie już nie
ISO 32000-1 §12.5.6.23 dzieli redakcję na identyfikację treści i usuwanie treści. W czasie identyfikacji adnotacja Redact jest tylko propozycją, którą użytkownicy wciąż mogą widzieć, przenosić i przedefiniować, i to umożliwia rundę recenzji. Usuwanie zostawiono aplikacji, z twardymi wymogami: wszystko, co adnotacja identyfikuje, znika, adnotacja znika, a przykryta część każdego obrazu ma zniszczone dane, bo clipping path albo maska obrazu na pikselach się nie liczą. Klucze są wyliczone w tabeli 192 ISO 32000-1 (tabela 195 w ISO 32000-2), i większość z nich opisuje, jak strona ma wyglądać po usunięciu, a nie co usunąć
| Klucz | Rola w specyfikacji | Co ApplyRedactions z tym robi |
|---|---|---|
QuadPoints | Obszar do usunięcia jako n czworokątów; zastępuje Rect jako obszar, gdy jest obecny | Czyści otoczkę każdego czworokąta osobno; tablica o długości niebędącej wielokrotnością 8 wraca do Rect |
Rect | Obszar do usunięcia, gdy QuadPoints nieobecne | Używany po znormalizowaniu odwróconych rogów |
IC | Wypełnienie DeviceRGB po usunięciu; brak znaczy przezroczystość; ignorowany, gdy RO istnieje | Zachowany, gdy jest; dodawany jako czarny, gdy go nie ma |
RO | Nakładka Form XObject; ma pierwszeństwo przed IC, OverlayText, DA i Q | Instalowany jako wygląd normalny i spłaszczany na stronę |
OverlayText, Repeat | Podpis rysowany nad obszarem, opcjonalnie kafelkowany | Rysowany do wygenerowanej nakładki, kafelkowany, gdy Repeat jest true |
DA, Q | Font, rozmiar i kolor podpisu; justowanie | Rozmiar i kolor czytane z DA, wyrównanie z Q |
Tworzenie znaczników przez AddRedactAnnotation
TPDFlib.AddRedactAnnotation(Left, Top, Width, Height, FillR, FillG, FillB, OverlayText, RepeatOverlay) dodaje znacznik do wybranej strony i zwraca nową liczbę adnotacji strony, która jest zarazem indeksem znacznika liczonym od 1, albo 0, gdy nic nie dodano. Współrzędne idą za bieżącym początkiem i jednostkami, więc przykład przełącza się na początek w lewym górnym rogu, gdzie Top to górna krawędź. Wywołanie zapisuje /Rect, /IC ze składników wypełnienia i sztywną /DA /F0 10 Tf 1 1 1 rg, biały tekst 10 punktów; niepusty OverlayText dokłada /OverlayText, /Repeat i /Q 1 dla wyśrodkowanego podpisu. Adnotacje Redact to funkcja PDF 1.7, więc starszy nagłówek jest podciągany do 1.7. Strumień wyglądu nie jest jeszcze zapisywany; oczekujący znacznik wygląda tak, jak rysuje go aplikacja recenzenta. Z podpisem sztywno białym paruj OverlayText z ciemnym wypełnieniem, bo na bladożółtym podpis znika. Znaczniki można tworzyć w każdej edycji 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); // początek w lewym górnym rogu: Top to górna krawędź
Pdf.SetMeasurementUnits(0); // punkty
if Pdf.SelectPage(1) <> 1 then
raise Exception.Create('Page 1 not found');
// Nazwa świadka: czarne wypełnienie, wyśrodkowany biały podpis
Index := Pdf.AddRedactAnnotation(72, 180, 220, 14, 0, 0, 0, 'REDACTED');
if Index = 0 then
raise Exception.Create('Mark was not added');
// Blok konta: wykafelkuj kod zwolnienia na wyższym polu
Pdf.AddRedactAnnotation(72, 410, 300, 40, 0, 0, 0, '(b)(6)', 1);
// Tylko znaczniki; każdy bajt pierwotnej treści wciąż jest obecny
Pdf.SaveToFile(ReviewFile);
finally
Pdf.Free;
end;
end;
QuadPoints rozstrzygają obszar usuwania; odpowiedzi zachowują tekst
Gdy znacznik niesie /QuadPoints, te czworokąty rozstrzygają, co ApplyRedactions usuwa, a Rect tylko otacza je na potrzeby wyświetlania. Znaczniki robione narzędziami zaznaczania tekstu mają zwykle jeden czworokąt na zaznaczoną linię, więc zmiana rozmiaru takiego znacznika przez SetAnnotRect przesuwa jego ramkę na ekranie i zostawia obszar usuwania tam, gdzie był; SetAnnotQuadPoints to wywołanie, które przesuwa region, a GetAnnotQuadCount zwraca 0 dla znacznika polegającego na samym Rect. Tekst przypięty do znacznika podlega innej regule. Narzędzia recenzji trzymają notatkę w /Contents, a autora w /T, i notatka przy znaczniku redakcji bywa opisem właśnie tego, co jest usuwane („PESEL klienta, potwierdź z prawnikiem”). ApplyRedactions opróżnia własny słownik znacznika, ale odpowiedzi nawązane pod nim przez /IRT to osobne adnotacje, usuwane tylko wtedy, gdy ich prostokąt nachodzi na region redakcji, więc odpowiedź zaparkowana na marginesie zachowuje tekst. Poniższe zestawienie drukuje każdy znacznik z liczbą czworokątów i odpowiedziami nawązanymi pod nim
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, gdy /IRT nieobecne
if (Parent > 0) and (Pdf.GetAnnotType(Parent) = 'Redact') then
Writeln(Format(' reply #%d to #%d: %s',
[I, Parent, Pdf.GetAnnotContentsEx(I)]));
end;
end;
end;
Jaka nakładka pojawia się po ApplyRedactions?
Wpis /RO wygrywa zawsze, gdy jest obecny, jak wymaga reguła pierwszeństwa z tabeli 192. ApplyRedactions instaluje ten Form XObject jako wygląd normalny adnotacji (/AP /N), zrzuca stan wyglądu i spłaszcza go, więc /IC, /OverlayText, /DA i /Q nie mają na ten znacznik wpływu. Rozmieszczenie to jedyny subtelny element. Specyfikacja mówi, że początek nakładki wyrównuje się z lewym dolnym rogiem Rect, podczas gdy spłaszczanie mapuje BBox formy przez jej Matrix na Rect, to samo rozmieszczenie BBox i Matrix, które spłaszczanie stosuje do widgetów pól formularza. Nakładka o BBox [0 0 w h], wymiarowanym do prostokąta, z identycznościowym Matrix wygląda tak samo pod obiema lekturami, więc żądaj tego kształtu, gdy własne narzędzia generują znaczniki /RO
Bez /RO ApplyRedactions normalizuje słownik i buduje wygląd sam. Usuwa /C i /BS i ustawia /Border na [0 0 0]: te wpisy opisują oczekujący znacznik, zwykle kolorowy obrys, i wypalenie go do końcowej strony sprawiłoby, że skończona redakcja wygląda na niezakończoną. Z obrysem poza grą wypełnienie pokrywa cały prostokąt. Podpis jest rysowany wewnątrz wygenerowanej formy, z rozmiarem i kolorem z /DA, justowaniem z /Q, kafelkowaniem z /Repeat, a nazwa fontu F0 związana w zasobach samej formy ze standardowym Helvetica. To związanie ma znaczenie, gdy pliki podróżują między narzędziami: ISO 32000-1 §12.7.3.3 definiuje nazwę fontu w DA jako wpis w domyślnych zasobach formularza interaktywnego (/DR), dokument bez pól formularza nie ma żadnych, i inna aplikacja aplikująca ten sam znacznik może podstawić font zapasowy. PDFlibPas pomija tę wędrówkę, a z Helvetica podpis pasuje tekstowi pisma łacińskiego, jak REDACTED albo kod zwolnienia
Brakujące /IC staje się czarnym prostokątem
Domyślną specyfikacji dla nieobecnego /IC jest przezroczyste wnętrze, i PDFlibPas świadomie od niej odbiega. Po usunięciu przezroczyste wnętrze pokazuje papier, białą dziurę tam, gdzie kiedyś była nazwa, i ta dziura wygląda tak samo jak układ, który nigdy tam nic nie miał. Czytnik nie odróżni zredagowanego od pustego, a recenzent nie potwierdzi, że redakcja nastąpiła, bez porównania wyciągniętego tekstu. Pusta rama — to, co krok aplikowania produkuje, gdy spłaszcza oczekujący wygląd znacznika będącego samym obrysem — zawodzi inaczej: czyta się jako propozycja, na którą nikt nie zareagował. Rozważyliśmy dosłowne pójście za domyślną i wybraliśmy czarny prostokąt, bo widoczny znak, że coś zostało wstrzymane, jest częścią tego, co zredagowany dokument mówi swojemu czytelnikowi, a wybór dotyczy tylko znaczników, których autor nigdy nie podał wypełnienia. /IC już obecny w słowniku jest używany niezależnie od wartości, więc producent, który zażądał bieli, dostaje biel
Co znika, zanim nakładka zostanie narysowana
ApplyRedactions działa strona po stronie i dokańcza całe usuwanie, zanim spłaszczy jakąkolwiek nakładkę, więc nakładka nigdy nie może zostać złapana przez własny region. Dla każdej strony zbiera po jednym prostokącie na czworokąt, albo Rect znaczników bez czworokątów, i szoruje każdą warstwę treści przeciw każdemu z nich:
- Tekst jest wycinany glif po glifie, mierzony z fontów strony i pełnego stanu tekstu, a reszta częściowo przykrytego łańcucha zostaje na pierwotnej pozycji
- Wektorowe ścieżki, obrazy inline i cieniowania malujące wewnątrz regionu są usuwane ze strumienia treści
- Form XObjecty są odwiedzane rekurencyjnie; forma, którą rysują też inne strony albo rozmieszczenia, jest redagowana przez prywatną kopię, więc tamte strony zachowują oryginał
- Image XObjecty tracą tylko przykryte piksele, nadpisywane czernią (pełny atrament K dla CMYK); obrazy JPEG, JPEG 2000, JBIG2 i CCITT są dekodowane i odkładane z powrotem jako bezstratne strumienie Flate
- W tagged PDF
/Alti/ActualTextprzypięte do usuwanej oznaczonej treści są czyszczone w treści strony i w drzewie struktury - Inne adnotacje, których prostokąt styka się z regionem, są usuwane, a pola formularzy, których widgety tam siedzą, tracą wartości
Krok pikseli to punkt obrazowy specyfikacji w praktyce. Czarny prostokąt namalowany nad zeskanowanym paszportem zadowala oko i wciąż zawala §12.5.6.23, bo pierwotne próbki dekodują, skoro tylko ktoś wyciągnie obraz. Ten sam silnik siedzi za RedactRegion, który usuwa treść z prostokąta podanego wprost i chodzi w parze z SanitizeDocument, bez czytania jakichkolwiek adnotacji. Gdy treść już zniknie, każdy znacznik jest spłaszczany, a jego słownik czyszczony do /Type i /Subtype. Spłaszczanie usuwa adnotację z tablicy /Annots strony, ale słownik wciąż jest obiektem w dokumencie, i zapis, który go zapisze, wręczy /Contents, /RC, /T i resztę śladu recenzji odbiorcy
Zapis bez niezredagowanej rewizji
Zapis przyrostowy unieważnia wszystko powyżej. Na mocy §7.5.6 (ten sam numer w ISO 32000-1 i ISO 32000-2) aktualizacja przyrostowa dokleja zmienione obiekty na koniec pliku i zostawia pierwotne bajty nietknięte, więc pierwotna rewizja, z treścią stron, obrazami i oznaczonymi znacznikami, pozostaje czytelna dla każdego, kto utnie plik przy wcześniejszym %%EOF; ExtractDocumentRevision w edycji Small Business PDFlibPas robi to jednym wywołaniem. Po ApplyRedactions zapisz nowy plik przez SaveToFile, nigdy SaveChanges, który dokleja. Przed zapisem GarbageCollectObjects przesiewa tabelę obiektów i wyrzuca obiekty, których nic już nie referencjonuje, jak strumienie wyglądu, które znaczniki nosiły przed krokiem aplikowania; pełny zapis pomija ten przesiew, chyba że SetGarbageCollect go włączy, albo ustawione jest pole GarbageCollect rekordu TPDFlibSaveOptions przekazywanego do SaveToFileOptions. Metadane to pozostała dziura. SanitizeDocument(SANITIZE_METADATA or SANITIZE_XMP_PACKET) zamazuje Author, Subject, Keywords, Creator i Producer i usuwa strumień XMP, zostawiając Title do twojego sprawdzenia. Unikaj tu SANITIZE_ALL: zawiera SANITIZE_ANNOTATIONS i usuwa każdą adnotację pozostałą w dokumencie, w tym notatki, które miałeś zachować
ApplyRedactions, jak RedactRegion, jest dostępny w edycji Enterprise. Na gołym TPDFlib zwraca 0 i ustawia LastErrorCode na PDFLIB_ERROR_ENTERPRISE_REQUIRED (900), więc krok release używa TPDFlibEnterprise z jednostki 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; // każdy znacznik Redact na każdej stronie
if Applied = 0 then
raise Exception.CreateFmt('No marks applied (error %d)',
[Pdf.LastErrorCode]);
// Info i XMP wciąż mogą nazywać to, czego strony już nie pokazują
Pdf.SanitizeDocument(SANITIZE_METADATA or SANITIZE_XMP_PACKET);
Orphans := Pdf.GarbageCollectObjects;
// Pełne przepisanie do nowego pliku; nigdy SaveChanges po redakcji
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;
Oczekiwane: ApplyRedactions zwraca liczbę znaczników Redact spłaszczonych przez wszystkie strony, 2 dla kopii recenzenckiej zapisanej przez MarkForReview, a GarbageCollectObjects zwraca, ile niereferencjonowanych obiektów przesiew wyrzucił
Jakiekolwiek narzędzie narysowało znacznik, jego wpisy /RO i /IC rozstrzygają, co odbiorca widzi na stronie, podczas gdy krok aplikowania w PDFlibPas Enterprise for Delphi rozstrzyga, co zniknęło pod spodem