Műszaki cikk

Szöveges megjegyzések PDFium QuadPoints segítségével Delphi-ben

A PDFium Component szöveges megjegyzéseket hoz létre — vagyis kiemelést, aláhúzást, áthúzást és hullámos aláhúzást — a TPdf.CreateAnnotation metóduson keresztül: a TPdfAnnotation rekordon a HasAttachmentPoints := True értéket állítja be, és kitölti az AttachmentPoints négyszöget, a komponens pedig beírja az ISO 32000-1 §12.5.6.10 szakaszában meghatározott QuadPoints bejegyzést; ez a teljes API felület; a cikk létezésének oka az, ami ezalatt történik, mivel a nyers PDFium hívási láncnak van egy olyan hibaállapota, amely az eszköztár legkevésbé hasznos tünetét produkálja: az FPDFAnnot_SetAttachmentPoints minden alkalommal false értéket ad vissza egy újonnan létrehozott megjegyzésnél, hibakód és utalás nélkül; ez a létrehozási oldali kiegészítője a meglévő megjegyzések olvasásáról és ellenőrzéséről szóló cikkünknek, amely a másik irányból mutatja be ugyanezeket a struktúrákat

A hibakeresés folyamata mindig ugyanaz; létrehoz egy kiemelő megjegyzést, meghívja a kapcsolódási pontok beállítóját a 0 indexszel, a függvény false értéket ad vissza, Ön pedig elkezdi kételkedni a koordinátáiban; felcseréli a pontokat, megfordítja az Y tengelyt, felcseréli az oldalteret az eszköz térrel; semmi sem segít, mert a koordinátákkal soha nem volt probléma; a probléma a C API index-szemantikája, és amint ezt meglátja, a javítás mindössze két sor

Mit jelentenek a QuadPoints-ok az ISO 32000-1 szabványban?

A QuadPoints egy 8×n számból álló tömb, amely n négyszöget ír le, az ISO 32000-1 §12.5.6.10 pedig megköveteli azt minden szöveges megjegyzésnél: minden négyszög egy szót vagy szomszédos szavak csoportját jelöli meg, amelyre a kiemelés, aláhúzás vagy áthúzás vonatkozik; a megjegyzés Rect bejegyzése továbbra is létezik, pero a jelölési altípusoknál ez csak a régiót határolja le; a négyszögek azok, amelyeket a renderelő valóban kirajzol; négyszög és nem téglalap, mert a szöveg elforgatható vagy dönthető, így a négy sarok négy független pontként tárolódik: x1 y1 x2 y2 x3 y3 x4 y4

A négy pont sorrendje az a pont, ahol a specifikáció és az elterjedt gyakorlat elválik egymástól; a specifikáció szövege úgy írja le a pontokat, mint amelyek az óramutató járásával ellentétes irányban követik a négyszöget, de az Adobe saját renderelője ezt mindig Z alakzatban értelmezte: először a felső él balról jobbra, majd az alsó él balról jobbra; mivel minden szerző az Acrobat-tal szemben tesztelt, gyakorlatilag minden renderelő, beleértve a PDFium-ot is, a Z alakzatot követi, és az olyan fájlok, amelyek követik a specifikáció szó szerinti megfogalmazását, egyes megjelenítőkben összeomlott vagy megcsavarodott kiemelésekként jelennek meg; a PDFium FS_QUADPOINTSF struktúrája pontosan ezt a konvenciót kódolja: az (x1,y1) a bal felső sarok, (x2,y2) a jobb felső, (x3,y3) a bal alsó, (x4,y4) a jobb alsó sarok, oldalkoordinátákban, ahol az Y felfelé növekszik; kövesse ezt a sorrendet és kész; a renderelők sok mindennel szemben elnézőek, de a hibás négyszög nem tartozik ezek közé

Miért ad vissza false értéket az FPDFAnnot_SetAttachmentPoints?

Az FPDFAnnot_SetAttachmentPoints azért bukik el egy új megjegyzésnél, mert a feladata az, hogy kicseréljen egy négyszöget egy adott indexen, és a frissen létrehozott megjegyzésnek nulla négyszöge van, amit ki lehetne cserélni; a szignatúra megjegyzéskezelőt, egy quad_index-et és a pontokat várja; a 0-s index nem azt jelenti, hogy „az első hely, szükség esetén létrehozva azt”, hanem azt, hogy „a meglévő 0. számú négyszög”, és amikor a FPDFAnnot_CountAttachmentPoints értéke 0, nincs ilyen négyszög, és a hívás false értéket ad vissza; a helyet létrehozó függvény az FPDFAnnot_AppendAttachmentPoints; minden, a FPDFPage_CreateAnnot-on keresztül létrehozott megjegyzés nulla számlálóval indul, így a létrehozási folyamatnak először az Append-et kell meghívnia, és csak a későbbi frissítések hívhatják meg a Set-et

Ez magát a PDFium Component-et is érintette; a v1.79.0 verzióig a CreateAnnotation és a SetAnnotation által megosztott belső rutin mereven kódolta a FPDFAnnot_SetAttachmentPoints(Annotation, 0, ...) hívást, ami helyes volt egy meglévő megjegyzés frissítéséhez, de garantáltan meghiúsult egy új megjegyzésnél, és EPdfException hibát váltott ki a 'Cannot set attachment points' üzenettel; a v1.79.1-ben megjelent javítás a számláló alapján ágazik el

// A komponens megjegyzés-íróján belül (v1.79.1+):
// az új megjegyzésnek még nincsenek négyszöghelyei, így az Append létrehozza
// az elsőt; a Set csak a már meglévő helyet cseréli ki
if FPDFAnnot_CountAttachmentPoints(Annotation) = 0 then
  Check(FPDFAnnot_AppendAttachmentPoints(Annotation, QuadPoints) <> 0,
    'Cannot set attachment points')
else
  Check(FPDFAnnot_SetAttachmentPoints(Annotation, 0, QuadPoints) <> 0,
    'Cannot set attachment points');

Ugyanez a minta érvényes, ha a közzétett C függvényeket közvetlenül hívja meg, amit a komponens lehetővé tesz, mivel az összes FPDFAnnot_* belépési pont elérhető a PDFium.pas fájlban; amikor egy FPDF_ANNOTATION kezelőt tart a kezében, és négyszögeket szeretne írni, először kérdezze meg a FPDFAnnot_CountAttachmentPoints függvényt, és annak megfelelően irányítson; ha az „FPDFAnnot_SetAttachmentPoints returns false” kifejezésre keres, ez a számlálás-majd-hozzáfűzés ág szinte biztosan a megoldás

Kiemelés létrehozása a TPdf.CreateAnnotation segítségével

Mivel a komponens elvégzi Ön helyett az Append-vagy-Set irányítást, a kiemelés létrehozása egy rekord kitöltésére redukálódik; az alábbi példa egy A4-es oldalt hoz létre, és egy félig átlátszó sárga kiemelést helyez el egy 200×20 pontos terület felett; vegye figyelembe, hogy a négyszög követi a fent leírt Z sorrendet, és a Rectangle úgy van beállítva, hogy körbezárja a négyszöget, ami észszerű működést biztosít a Rect-en alapuló kattintás-tesztet használó megjelenítőkben

var
  Pdf: TPdf;
  A: TPdfAnnotation;
begin
  Pdf := TPdf.Create(nil);
  try
    Pdf.CreateDocument;
    Pdf.AddPage(0, 595, 842);

    FillChar(A, SizeOf(A), 0);
    A.Subtype := anHighlight;
    A.HasColor := True;
    A.Color := clYellow;
    A.ColorAlpha := $80;                     // 50% átlátszóság
    A.HasAttachmentPoints := True;
    A.AttachmentPoints[1].X := 50;  A.AttachmentPoints[1].Y := 700; // bal felső
    A.AttachmentPoints[2].X := 250; A.AttachmentPoints[2].Y := 700; // jobb felső
    A.AttachmentPoints[3].X := 50;  A.AttachmentPoints[3].Y := 680; // bal alsó
    A.AttachmentPoints[4].X := 250; A.AttachmentPoints[4].Y := 680; // jobb alsó
    A.Rectangle.Left := 50;  A.Rectangle.Top := 700;
    A.Rectangle.Right := 250; A.Rectangle.Bottom := 680;
    A.ContentsText := 'Kiemelt terület';
    Pdf.CreateAnnotation(A);

    Pdf.SaveAs('highlighted.pdf');
  finally
    Pdf.Free;
  end;
end;

Az altípusok közötti váltás egyetlen sorba kerül; az anUnderline, anStrikeout és anSquiggly azonos rekordformát, négyszögeket és minden mást vesz fel, mert az ISO 32000-1 mind a négyet ugyanabba a megjegyzéscsaládba sorolja, amelyeket csak az különböztet meg, hogy a négyszög-tartomány hogyan van díszítve; a nem szöveges jelölési altípusok, mint az anSquare, anCircle és anText, kizárólag a Rectangle alapján pozicionálják magukat; ezeknél hagyja a HasAttachmentPoints értékét False-on, és a négyszög-mechanizmus soha nem fog lefutni

Miért fordul le az AttachmentPoints[0] Delphi-ben, de miért hibázik el FPC-ben?

A TQuadrilateralPoint típus array [1..4] of TPdfPoint-ként van deklarálva, ami egy 1-es alapú tömb, és ez megzavar mindenkit, akinek az ujjai alapértelmezés szerint a nulla alapú indexeléshez szoktak; ha azt írja, hogy A.AttachmentPoints[0], a Delphi dcc32 fordítója panasz nélkül lefordítja, mert a tartományellenőrzés alapértelmezés szerint ki van kapcsolva; futásidőben a kifejezés csendben beolvassa vagy írja a tömb előtti memóriát, ami a TPdfAnnotation rekordon egy szomszédos mező; a kiemelése kap egy hibás sarkot, vagy egy szomszédos mező megsérül, és semmi sem jelez hibát; a Free Pascal a Lazarus portolás során pontosan ezt a hibát csípte el a saját bemutató forrásainkban: az fpc fordítási időben végez tartományellenőrzést a konstans indexeken, és kerek perec elutasította az AttachmentPoints[0..3]-at, így került felszínre a hibás indexelés és a Set-vagy-Append könyvtári hiba egyszerre

Ebből két szokás következik; indexelje a négyszöget 1-től 4-ig, a fenti kódban szereplő sarokrendnek megfelelően, és legalább egyszer építse fel a megjegyzés-kódot engedélyezett tartományellenőrzéssel — vagy a {$R+} kapcsolóval Delphi-ben, vagy bármely fpc-buildben —, mielőtt megbízna benne; az alapértelmezett dcc32 build sikere nem bizonyíték arra, hogy az indexek megfelelőek; csupán bizonyíték arra, hogy semmi sem omlott össze azon a memórián, amely éppen ott volt

Négyszög-koordináták kinyerése valós szövegből

A mereven kódolt téglalapok megfelelnek bemutatóhoz, de az éles kiemelések valós karaktereket követnek nyomon, és a koordinátáknak a PDFium szövegoldal-geometriájából kell származniuk, nem pedig találgatásokból; a szöveg PDFium Component segítségével történő kinyeréséről szóló útmutatónkban tárgyalt rutinok karakterenkénti határolókereteket adnak ugyanabban az oldalkoordináta-térben, amelyet a négyszögek használnak, így a keresési találat közvetlenül sarokpontokká alakítható: az első karakter bal oldala, az utolsó karakter jobb oldala, valamint a sor kiterjedésének teteje és alja; ha Ön maga generálja a szöveget, és tudnia kell, hova esnek a sorok még azok létezése előtt, a szöveg méréséről és a sortörésről szóló cikk bemutatja ezeknek a kiterjedéseknek az előzetes kiszámítását

Egy őszinte korlát: a TPdfAnnotation rekord egyetlen TQuadrilateralPoint pontot tartalmaz, így egy CreateAnnotation hívás egyetlen négyszöget ír le; a három soron átnyúló kijelöléshez három négyszög szükséges, soronként egy a §12.5.6.10 szerint, és ehhez kétféleképpen juthat el; az egyszerű mód az egy megjegyzés soronként, ami mindenhol helyesen renderelődik, és megőrzi a komponens-szintű API-t; a kompakt mód — egy megjegyzés, amely három négyszöget hordoz — azt jelenti, hogy a megjegyzést a komponensen keresztül hozza létre, majd a második és harmadik négyszöghöz Ön hívja meg a közzétett FPDFAnnot_AppendAttachmentPoints függvényt, ami pontosan azért működik, mert az Append helyeket hoz létre ahelyett, hogy kicserélne azokat; ne próbálja meg a többszörös négyszögeket ismételt SetAttachmentPoints hívásokkal elérni; a jelenlegi számlálón túli minden index egyszerűen false-t ad vissza, ugyanabból az okból, amiért a 0 index tette a friss megjegyzésnél

Az írás után ellenőrizze a fájlt egy valódi megjelenítőben ahelyett, hogy bízna a visszatérési kódokban: nyissa meg a fájlt Acrobat-ban vagy bármely PDFium-alapú megjelenítőben, és győződjön meg arról, hogy a jelölés a szövegre kerül, a kívánt átlátszósággal olvasható, és túléli a mentés-majd-újratöltés oda-vissza utat; a megjegyzéstípusok, a négyszögkezelés és az itt bemutatott számláló-tudatos író mind a Delphi, C++Builder és Lazarus rendszerekhez készült szabványos PDFium Component részei; a termékoldal tartalmazza a teljes megjegyzés API referenciát a könyvtár többi részével együtt