Artykuł techniczny

Adnotacje PDF w Delphi z HotPDF: Typy i prostokąty (Rects)

Adnotacja nie jest zawartością strony. Gdy wywołujesz TextOut lub rysujesz prostokąt, znaki te stają się częścią strumienia zawartości strony, wmieszane w bajty, które maluje program renderujący. Adnotacja to osobny słownik podwieszony do strony za pośrednictwem tablicy /Annots, posiadający własny prostokąt, własny wygląd i własny cykl życia. Czytelnik może ją otworzyć, przenieść, ukryć lub usunąć bez dotykania ani jednego glifu z podlegającej strony. Ta separacja jest całym powodem, dla którego adnotacje istnieją, a zarazem jest też źródłem dwóch rzeczy, które na początku najczęściej zaskakują ludzi: gdzie ląduje adnotacja i jak wygląda, gdy konkretna przeglądarka weźmie ją w swoje ręce

HotPDF udostępnia podtypy adnotacji ISO 32000 poprzez rodzinę wywołań AddXxxAnnotation w obiekcie strony. Wszystkie współdzielą ten sam kształt: prostokąt ustalający adnotację na stronie w przestrzeni użytkownika PDF, pewien ładunek (tekst, nazwa pieczątki, para punktów) i kolor. Zrób prostokąt poprawnie, a większość pracy będzie wykonana. Reszta to wiedza, które podtypy niosą swój własny wygląd, a które polegają na tym, że przeglądarka je narysuje

Strona PDF wyprodukowana przez HotPDF przedstawiająca ikony notatek tekstowych, pola z wolnym tekstem, znaczniki kwadratowe i liniowe oraz pieczątki zatwierdzenia rozmieszczone na całej stronie
Jedna strona niosąca naraz kilka podtypów adnotacji: notatki tekstowe, wolny tekst, oznaczenia geometryczne i pieczątki

Prostokąt to adnotacja, nie tekst

Każde wywołanie adnotacji przyjmuje TRect, a ten prostokąt oznacza coś innego niż współrzędne, które przekazujesz do TextOut. W przypadku notatki tekstowej jest to klikalny hotspot, mały obszar, w którym znajduje się ikona notatki i gdzie kliknięcie otwiera komentarz. Dla kwadratu lub pola wolnego tekstu jest to widoczny zasięg oznaczenia. W przypadku pieczątki jest to pole, do którego skalowana jest grafika pieczątki. Liczby to punkty w przestrzeni użytkownika PDF, mierzone od lewego dolnego rogu strony z wartością Y rosnącą w górę, ta sama konwencja, której używa reszta HotPDF

Notatka tekstowa to najlżejszy podtyp. Nadajesz jej tekst główny, prostokąt dla ikony, flagę oznaczającą, czy jest domyślnie otwarta, nazwę ikony i kolor

Pdf.CurrentPage.AddTextAnnotation(
  'Reviewer: confirm the totals on this line before sign-off.',
  Rect(120, 700, 140, 720),   // icon hotspot, ~20pt square
  False,                      // closed until the reader clicks it
  taComment,                  // bubble icon
  clBlue);

Prostokąt tutaj celowo jest mały, około dwudziestu punktów z boku, ponieważ notatka tekstowa jest tylko ikoną, dopóki ktoś w nią nie kliknie. Spraw, aby prostokąt był duży, a nie otrzymasz dużej notatki; otrzymasz powiększony cel do klikania z ikoną przypiętą do jednego rogu. Flaga Open kontroluje, czy okno wyskakujące jest pokazywane po załadowaniu dokumentu. Ustaw garść notatek na True, a będą one układać się jedna na drugiej i na wierzchu zawartości, dlatego zachowaj to tylko dla jednej notatki, którą naprawdę chcesz, aby czytelnik od razu zobaczył

Nazwa ikony pochodzi z THPDFTextAnnotationType, co mapuje do standardowych ikon notatek: taComment, taKey, taNote, taHelp, taParagraph, taNewParagraph i taInsert. Ikona to jedyna rzecz, którą zmienia dany typ. Nie modyfikuje zachowania i warto wiedzieć, że nie każda przeglądarka rysuje całą siódemkę; te bezpieczne w starych i nowych czytnikach to taComment, taNote i taHelp

Wolny tekst pisze na stronie, ale pozostaje adnotacją

Adnotacja wolnego tekstu wygląda jak zawartość, ponieważ tekst jest widoczny bez klikania, siedząc w swoim prostokącie jak podpis. Jednak to nadal adnotacja, ze wszystkimi możliwościami separacji, z czym się to wiąże, czyli jest to dokładnie to, czego chcesz w przypadku pieczątki rewizyjnej lub etykiety wersji roboczej, którą ktoś powinien móc później usunąć. Sygnatura zamienia ikonę i flagę otwarcia na wartość justowania

Pdf.CurrentPage.AddFreeTextAnnotation(
  'DRAFT - not for distribution',
  Rect(200, 210, 400, 235),   // the box the text is laid into
  ftCenter,                   // ftLeftJust / ftCenter / ftRightJust
  clRed);

Tutaj prostokąt ma większe znaczenie niż w notatce tekstowej, ponieważ tekst zawija się i wyrównuje w jego wnętrzu. Jeśli ustawisz zbyt krótki rozmiar pudełka, tekst zostanie przycięty na dolnej krawędzi; zbyt wąski, a będzie się zawijał w miejscach, w których tego nie zamierzałeś. Wyrównanie pochodzi z THPDFFreeTextAnnotationJust i ma tylko trzy wartości. Ponieważ wolny tekst jest adnotacją typu markup, czytelnik otwierający plik w edytorze może zaznaczyć go, przenieść lub usunąć jako jednostkę, co stanowi różnicę determinującą, czy sięgasz po wolny tekst czy po prostu rysujesz słowa za pomocą TextOut. Jeśli etykieta ma być stała, narysuj ją. Jeżeli jest redaktorska i docelowo do usunięcia, uczyń ją adnotacją

Oznaczenia geometryczne i liniowe służące do wskazywania na rzeczy

Kwadraty, okręgi i linie to oznaczenia (markup) służące do wskazywania regionu, a nie opisywania go słowami. AddCircleSquareAnnotation obejmuje dwa kształty pól poprzez THPDFCSAnnotationType z opcją csCircle lub csSquare, przy czym prostokąt wyznacza granice kształtu

// A box drawn around a figure that needs attention
Pdf.CurrentPage.AddCircleSquareAnnotation(
  'Check this region against the source data',
  Rect(50, 300, 120, 360),
  csSquare,
  clGreen);

// A line, given two points rather than a rectangle
var
  StartPt, EndPt: THPDFCurrPoint;
begin
  StartPt.X := 130; StartPt.Y := 360;
  EndPt.X   := 250; EndPt.Y   := 320;
  Pdf.CurrentPage.AddLineAnnotation(
    'Points from the note to the figure',
    StartPt, EndPt,
    clBlue);
end;

Zauważ, że adnotacja linii łamie wzorzec prostokąta: przyjmuje dwa rekordy THPDFCurrPoint, początkowy i końcowy, ponieważ linia jest definiowana przez jej punkty końcowe, a nie prostokąt ograniczający (bounding box). Kolor ustawia pociągnięcie. Jeśli potrzebujesz grotów strzałek, HotPDF posiada przeciążenia AddLineAnnotation przyjmujące style kończenia linii, ale prosta trójargumentowa forma rysuje zwykłą linię, czego przeważnie oczekuje odnośnik (callout)

Podtypy oznaczenia tekstu pracują na już zaaranżowanym regionie. AddHighlightAnnotation bierze prostokąt, opcjonalną treść oraz kolor (domyślnie żółty) i barwi obszar na wzór zakreślacza. W zamierzeniu ma znajdować się nad rzeczywistym tekstem, więc prostokąt powinien pasować do granic wyrysowanych przez ciebie słów, co oznacza z reguły, że będziesz go obliczał na bazie współrzędnych uprzednio posłanych do TextOut zamiast zgadywać

Pieczątki opierają się na przeglądarce odnośnie renderowania

Adnotacja pieczątki ma największe szanse wyglądać różnie zależnie od czytnika, i powód tego jest warty zrozumienia. AddStampAnnotation określa standardową pieczątkę poprzez THPDFStampAnnotationType, pod wartościami jak satApproved, satConfidential, satFinal, satDraft i satForComment

Pdf.CurrentPage.AddStampAnnotation(
  'Approved for release on review',
  Rect(50, 400, 200, 440),
  satApproved,
  clGreen);

Nazwa pieczątki jest prośbą. Zestaw PDF definiuje standardowe nazwy pieczątek, lecz nie samą grafikę ukrytą za nimi, toteż każda przeglądarka dostarcza swoje własne wyrenderowane słowa "APPROVED" albo "CONFIDENTIAL," podczas gdy parę nie renderuje wcale nierozpoznanych przez nie nazw. Prostokąt kontroluje pole, do którego skaluje się grafika, a kolor jest wskazówką, którą przeglądarka może uhonorować albo i nie. Jeśli pieczątka wszędzie musi wyglądać identycznie, niezawodną metodą wcale nie jest wstawianie standardowej: narysuj znak we własnym zakresie operując na wywołaniach TextOut oraz rysunku, albo umieść go jako adnotację z wolnym tekstem, nad której widokiem sprawujesz pieczę. Sięgaj po standardową pieczątkę jeśli zależy Ci na znanym przez widza wyglądzie i potrafisz znieść odmienności

Załączniki do plików idą tym samym torem co kształt prostokąta-i-ładunku. AddFileAttachmentAnnotation zabiera w sobie opis, ścieżkę pliku celem ucieleśnienia go, prostokąt pod ikonę spinacza oraz kolor. Plik zasuwa we wnętrzu pliku PDF, a ikona robi za klamkę, której czytelnik używa do wydobycia go na zewnątrz

W czym adnotacje odbiegają od pól AcroForm

Częstym pomyleniem, które zabiera gro czasu, jest rozpatrywanie adnotacji jako ewentualnego pola formularza. Zarówno jedne jak i drugie przywiązują się do strony za pośrednictwem /Annots, i poletko formularza w gruncie rzeczy jest specyficznym podtypem adnotacyjnym (widżetem), stąd też ich wrażenie współzależności. Jednakże, nie są one zamienne. Pole formularza przechowuje wartość, nosi nazwę, angażuje się w kolejność podczas używania klawisza tab (tab order), do tego można je zatwierdzić, wyzerować czy napisać mu skrypt; takowe można stworzyć korzystając z wywołań AddTextField, AddCheckBox oraz AddPushButton, a wcale nie stosując wywołań ze strony opowiadającej o adnotacjach. Znacznikowa adnotacja wstrzymuje w sobie komentarz bądź formę, na próżno w niej by doszukiwać się wartości podlegających przesłaniu, ponadto służy ona za złe narzędzie z chwilą, gdy natrafisz na moment, że zażądasz zbiórki inputu

Praktyczny sprawdzian jest zwyczajny. Skoro użytkownik ma podjąć akcję przepisywania, dobierania, lub prztyknięcia, tak ażeby plik to zapisał, jest ci pożądane podwórko formularzowe AcroForm. Kiedy bywasz w trakcie pozostawiania uwagi, tagowania sektora, lub pieczętowania statusu załączonego do zbioru, chociaż bynajmniej dane z owym faktem nie wędrują, adnotacja wychodzi naprzeciw twym zamiarom. Mieszanie ze sobą zaowocuje powstawaniem zbiorów dokumentacyjnych niby trzymających fason a wewnątrz wykoślawiających swą behawioralność: objawi się "pole", w które pospolitowani pisarczycy nie rzucą okiem by go wypełnić, ewentualnie zapodzieje się komentarz, od kiedy posypie się zerowanie wyśledzonego formularza. Aspekty interaktywne nafaszerowane kategoriami do wklepania, sprawdzaniem zgodności oraz poczynaniami wykonawczymi to wyłączna dziedzina nakreślona poprzez AcroForm fields and actions walkthrough (Pola oraz wytyczne działania AcroForm)

Zlepek wszystkiego na kartę papieru

Segmenty przeplatają się podobnież, jak reszta HotPDF czyni to pod drodze. Poukładaj właściwości tyczące się dokumentacji, zawołaj BeginDoc, porysuj jakąbądź merytoryczną treść stronnicy ci jest użyteczna z tekstem tudzież inwokacjami graficznymi, ponaklejaj z wierzchu zapiskami z adnotacji i doprowadź do epilogu na modłę EndDoc. Adnotacje chwytają się za CurrentPage, odtąd już pod pręgierzem AddPage wylatują gładko na zreaktywowaną stronę, i napomknięta na pierwszej notatka ukrycie ukaże twe zamyślenie tyczące się strony pierwszej tu na tej następnej, skoro pokwapisz się dodać takową uchybiając uwadze na owy odłam

Pdf := THotPDF.Create(nil);
try
  Pdf.FileName := 'annotated.pdf';
  Pdf.Compression := cmFlateDecode;
  Pdf.FontEmbedding := True;
  Pdf.BeginDoc;

  Pdf.CurrentPage.SetFont('Arial', [], 11);
  Pdf.CurrentPage.TextOut(50, 740, 0, 'Quarterly figures, draft for review');

  Pdf.CurrentPage.AddTextAnnotation(
    'Confirm the totals before sign-off.',
    Rect(50, 720, 70, 740), False, taComment, clBlue);
  Pdf.CurrentPage.AddFreeTextAnnotation(
    'DRAFT', Rect(450, 720, 540, 745), ftCenter, clRed);
  Pdf.CurrentPage.AddStampAnnotation(
    'For comment', Rect(50, 660, 180, 695), satForComment, clGreen);

  Pdf.EndDoc;
finally
  Pdf.Free;
end;

Ostatni z instynktownych odruchów warty wykształcenia na okoliczność mętnego urobku: otwórz plik opierając się o niejedną przeglądarkę zanim posuniesz się do stwierdzeń tyczących się zardzewiałej zębatki w zapisach kodu. Pieczątki powiązane z połączonym rarytasem z pogranicza piktogramów potrafią najzwyczajniej przewiniać, acz adnotacja rzuca blask na petycję nakierowaną wprost do wpatrzonego mola książkowego by z rzeczą nie postępować na równi pociągniętym lakierem pikselom. Zaistniały kontrast pomiędzy programem o posturze Acrobat a wagowo bezbolesną odnogą o miano przeglądarki wytycza szlak o prawidłowym standardzie, kładąc do snu marzenia o jakimkolwiek rzekomym bublu za kulisami twego domniemania

Odsłonięte rąbkiem tu wołania o adnotacjach zalicza się do składników HotPDF Component na potrzebe Delphi i C++Builder