A HotPDF oda-vissza viszi a multi-select list box értékeket FDF-en és XFDF-en úgy, hogy a mezőértéket elejétől a végéig tömbként tartja. A 2.755.0-s verzió óta az ExportLoadedFormToFDF, az ExportLoadedInterchangeToFDF és az ExportLoadedFormToXFDF minden kiválasztott opciót saját FDF stringként vagy XFDF <value> elemként ír ki, a megfelelő import metódusok pedig minden értéket a mező opcióihoz mérnek, és újraépítik az /I kiválasztási indexeket, mielőtt bármit megváltoztatnának. Semmi nem ragasztódik egyetlen stringgé az út során
A hiba, amit ez javít, könnyen előidézhető. Vegyél egy rendelési űrlapot termékopciók multi-select list boxjával, hagyd, hogy egy felhasználó válasszon kettőt, exportáld az űrlapadatot egy back-office rendszernek, majd importáld vissza a szerkesztett fájlt a PDF-be. A változás előtt a list box üresen vagy rosszul jött vissza. Az ok, hogy az egyik exportérték sortörést tartalmazott, és a régi útvonal a kiválasztásokat egyetlen, sortörésekkel elválasztott stringgé lapította. Több kiválasztást kinyerni abból a stringből soha nem volt megbízható, és olyan exportértékkel, ami maga is tartalmaz sortörést, egyáltalán nem működhet
Miért töri meg az oda-vissza utat a multi-select értékek sortörésekkel való összefűzése?
A kiválasztások egyetlen stringgé fűzése eldobja az értékek közti határokat, és egy érték tartalmazhatja az elválasztót, így egyetlen importáló sem tudja a stringet helyesen visszabontani. Az ISO 32000-1 §12.7.4.4-e megengedi, hogy egy choice mező /V bejegyzése vagy egyetlen szöveges string vagy szöveges stringek tömbje legyen, és egy MultiSelect flaggel (a /Ff 22. bitje) bíró list box a tömb formát használja, amint egynél több opció lett kiválasztva. Ugyanaz a szakasz a /I-t nullától induló, növekvő sorrendű opcióindexek tömbjeként definiálja, amit a megjelenítők arra használnak, hogy megkülönböztessenek két opciót, amik véletlenül osztoznak egy exportértéken. A HotPDF-ben a skalár getter, a GetFormFieldValue, csak a string formát olvassa, így egy tömb rajta átfuttatva az exportot üres stringgé rontotta le, a régi XFDF import pedig ismételt <value> elemeket fűzött össze LF-fel. Képzeld el, hogy egy opció Deep, sortörés, Blue formában exportálódik: az összefűzés után a Deep\nBlue\nRed lehet két kiválasztás vagy három, és a fájl nem ad módot annak tudására, melyik. A javítás az volt, hogy teljesen elhagyták a skalár használatát az oda-vissza út közepén
Mit tartalmaznak az exportált FDF és XFDF fájlok?
A HotPDF egy multi-select értéket típusos tömbként ír FDF-ben, és XFDF-ben kiválasztásonként egy <value> elemként, így a határok láthatók maradnak a lemezen. FDF-ben minden elem megtartja a helyesírást, amivel a forrás PDF-ben szerepelt: a hexadecimális stringek hexként mennek ki, a literál stringeket pedig egyetlen helper escape-eli, ami CR-t és LF-t \r-ré és \n-né alakít. XFDF-ben a gyökér hordozza az xml:space="preserve"-ot, ahogy az ISO 19444-1 megköveteli, ami azt jelenti, hogy egy szövegelem belsejében minden whitespace adatnak számít. A HotPDF ezért minden <value> nyitó címkéjét, escape-elt szövegét és záró címkéjét egyben írja, a behúzást az elemen kívül tartja, a CR-t, LF-t és TAB-ot pedig karakterhivatkozásként kódolja, hogy egy soremelés-normalizációt alkalmazó XML parser ne változtathassa meg az eredeti bájtokat
<!-- FDF: mezőnként egy típusos tömb -->
<< /T (options) /V [(Deep\nBlue) (Red)] >>
<< /T (region) /V [<45553132>] >>
<!-- XFDF: kiválasztásonként egy <value> -->
<xfdf xmlns="http://ns.adobe.com/xfdf/" xml:space="preserve">
<fields>
<field name="options">
<value>Deep
Blue</value>
<value>Red</value>
</field>
</fields>
</xfdf>
Két export-határeset érdemel tudni, mielőtt megírnád a hívó kódot. Először, az ExportLoadedFormToFDF a teljes FDF testet memóriában építi, mielőtt létrehozná a célfájlt (a 2.755.1-ben javítva), így egy nem exportálható érték, például egy stringtől eltérőt tartalmazó tömb, kivételt dob anélkül, hogy egy létező fájlt csonkolna. Másodszor, egy üres kiválasztás egy olyan list boxon, ami üres string exportértéket is kínál, kétértelmű XFDF-ben, mert a <value/> jelentheti azt, hogy semmi nincs kiválasztva, vagy azt, hogy az üres opció van kiválasztva. Az ExportLoadedFormToXFDF ebben az esetben kivételt dob a találgatás helyett, és úgy dob, mielőtt a célfájl megnyílna. FDF-ben nincs ilyen kétértelműség, mert a /V [] és a /V [()] különbözik. Mindkét FDF exportáló átugorja azokat a csak-widget végpontokat, amiknek nincs /T nevük, egyezően az XFDF exportálóval, mert egyetlen importáló sem tudná azokat a bejegyzéseket mezőhöz visszakapcsolni
var
Pdf: THotPDF;
Written: Integer;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('order-form.pdf', '') > 0 then
begin
// A multi-select list boxok /V [(...) (...)] formában íródnak
Written := Pdf.ExportLoadedFormToFDF('order-form.fdf');
try
Pdf.ExportLoadedFormToXFDF('order-form.xfdf');
except
on E: Exception do
// Üres kiválasztás plusz üres exportopció: az XFDF nem tudja
// megkülönböztetni, és a létező .xfdf fájl érintetlen marad
ShowMessage('XFDF export refused: ' + E.Message);
end;
end;
finally
Pdf.Free;
end;
end;
Hogyan validálja a HotPDF a multi-select értéket importnál?
A HotPDF egy importált tömböt csak akkor fogad el, ha a cél egy MultiSelect flaggel bíró choice mező, és a tömb minden értéke egyezik egy exportértékkel a mező /Opt tömbjében. Minden opció slot egyszer használható, így egy olyan lista, aminek két opciója osztozik a b exportértéken, elfogadja a [<62> <62>]-t két különböző kiválasztásként, és elutasítja a harmadik b-t. Az újraépített /I a /Opt sorrendjét követi, nem a beérkező értékek sorrendjét, mivel a §12.7.4.4 növekvő indexeket ír elő. A HotPDF az új /V-t és /I-t leválasztott objektumként építi, és csak azután rendeli hozzá őket, hogy minden érték átment a validáción, így egy elutasított érték soha nem hagy maga után féltömböt vagy elavult indexeket. A másolat az importált mezőre íródik, nem egy megosztott ős tömbjébe, az FDF-ből érkező hex írásmódok hexként maradnak a mentésen át, és azok a mezők, amiknek a számításai a list boxon múlnak, újraszámolásra jelölődnek. Ha csak egyetlen értéket kell beállítanod, a űrlapmező-érték beállítása betöltött PDF-ben a skalár útvonalon megy, ami a tervezés szerint nem kezel több kiválasztást
Néhány más eszköz sima ASCII exportértékeket ír hex stringként byte order mark nélkül, például <416272>, majd XFDF-et exportál úgy, hogy azokat a hex számjegyeket szövegként írja ki. Egy szigorú literál összehasonlítás a visszaúton megbukik, és az import megszakad. A 2.755.1-es verzió egyetlen újrapróbálást ad hozzá: amikor egy érték egyetlen opcióhoz sem illeszkedik, a HPDFHexSpellingText a szöveget hex hasznos teherként dekódolja, és újra összehasonlítja az eredményt. Az újrapróbálás csak olyan bemenetre vonatkozik, ami egyébként kivételt dobott volna, így soha nem változtat meg egy már illeszkedő értéket. Ugyanez a release tette egységessé, hogy a skalár és tömb útvonalak ugyanazt a Unicode dekodert használják, ami érti a PDFDocEncoding-et, a UTF-16-ot mindkét byte order markkal és a UTF-8-at. Azelőtt egy logikai érték illeszkedhetett az egyik útvonalon és megbukhatott a másikon kevert kódolású dokumentumokban
Miért veszíthet mégis mezőket egy érvényes FDF fájl a parszolás alatt?
Egy FDF szkenner, ami nem követi a hexadecimális stringeket, félbevághat egy mezőszótárat, amikor egy hex érték pont a szótárlezáró mellett ér véget. A << /T (region) /V <416273>>> részben az első > zárja le a hex stringet, de egy naiv szkenner azt a következő >-vel együtt a szótár végének olvassa, és csendben eldobja a mezőt. A fájl szintű FDF importáló már követette, hogy hex stringen belül van-e, és a 2.755.1-ben az ImportLoadedInterchangeFromFDF mögötti tömb- és szótárszkennerek ugyanezt teszik. Egy második kérdés az indirekt hivatkozásokat érinti. Egy FDF fájl kis PDF-szintaxisú dokumentum saját objektumszámozással (ISO 32000-1 §12.7.7), így egy /V [11 0 R] érték az FDF fájl 11-es objektumára utal, nem a kitöltött PDF 11-es objektumára. A HotPDF egyszerűsített FDF parsere nem oldja fel a hivatkozásokat a fájlon belül, ezért elutasítja azt a tömböt ahelyett, hogy elolvasná, mi a 11-es objektum épp a céldokumentumban
A fájl, stream és XFDF importok másképp jelentenek hibát
A három import útvonal ugyanúgy validál, de másképp jelenti a hibákat, és érdemes tudatosan választani köztük. Az ImportLoadedFormFromFDF átugor minden validáción megbukó mezőt, és annak a mezőknek a számát adja vissza, amiket ténylegesen alkalmazott, így az elvárttól alacsonyabb szám az egyetlen jel a problémára. Az ImportLoadedInterchangeFromFDF és az ImportLoadedFormFromXFDF az első elutasított mezőnél dob. Minden mező a saját jogán rögzül, így a kivétel előtt feldolgozott mezők megtartják az új értékeiket. Egyiket se kezeld tranzakcióként a teljes cserefájl felett: ha all-or-nothing viselkedés kell, dobd el a betöltött dokumentumot, ha kivétel történik, a mentése helyett
var
Pdf: THotPDF;
Source: TMemoryStream;
Status: AnsiString;
Info: THPDFFDFInterchangeInfo;
begin
Pdf := THotPDF.Create(nil);
Source := TMemoryStream.Create;
try
Source.LoadFromFile('order-form-reviewed.fdf');
if Pdf.LoadFromFile('order-form.pdf', '') > 0 then
try
// Csak mezők; /Opt-on kívüli érték vagy nem-multi-select cél kivételt dob
if Pdf.ImportLoadedInterchangeFromFDF(Source, True, False, Status, Info) then
Pdf.SaveLoadedDocument('order-form-filled.pdf');
except
on E: Exception do
ShowMessage('Import rejected, nothing saved: ' + E.Message);
end;
finally
Source.Free;
Pdf.Free;
end;
end;
Az XFDF callbackek bővítése a meglévő hívók megtörése nélkül
A tömbtámogatás az alsó szintű XFDF unitban egy külön rekordban, a THPDFXFDFArrayAccess-ben él, és az HPDFXFDFExportFields és az HPDFXFDFImportFields új overloadjaiban, nem pedig a meglévő THPDFXFDFAccess rekord végére biggesztett extra mezőkben. Az ok a bináris kompatibilitás. A THPDFXFDFAccess-et lokális változóként feltöltő kód gyakran csak azokat a slotoit állítja be, amiket ismer, és soha nem törli a többit, így az ahhoz a rekordhoz biggesztett új függvénypointer stack szemetet tartalmazna, és a library valódi callbacknek venné. Külön rekorddal a régi hívók megtartják a régi elrendezést és a régi overloadokat, azok az overloadok pedig belül csupa-nil tömbrekordot adnak át. Az eredeti skalár import overload kompatibilitásból továbbra is LF-fel fűzi össze az ismételt értékeket, és csak a tömbtudatos overload tartja őket szét. Amikor a saját adattárod kötöd be, indulj a Default(THPDFXFDFArrayAccess)-ből. A GetFormFieldValueArray-ből adj vissza True-t bármelyik listás értékű mezőre, beleértve az üresen hagyottat is, és False-t, hogy a skalár callbackre essen vissza
uses HPDFXFDF;
// Sima függvénypointer, nem „of object": a Context a te saját tárodat hordozza
function StoreGetSelections(Context: Pointer; FieldIndex: Integer;
out Values: THPDFXFDFValueArray): Boolean;
begin
Result := TFormStore(Context).IsListField(FieldIndex);
if Result then
Values := TFormStore(Context).Selections(FieldIndex);
end;
procedure ExportStore(Store: TFormStore; out Bytes: TBytes);
var
Access: THPDFXFDFAccess;
ArrayAccess: THPDFXFDFArrayAccess;
begin
Access := MakeStoreAccess(Store); // a te meglévő skalár kötéseid
ArrayAccess := Default(THPDFXFDFArrayAccess); // minden nem használt slot nil
ArrayAccess.GetFormFieldValueArray := StoreGetSelections;
HPDFXFDFExportFields(Access, ArrayAccess, Bytes);
end;
A multi-select csere már létező, a /Ff-ben MultiSelect bittel bíró list boxokon működik. A choice mezők és flag bitjeik létrehozásáért lásd a ListBox és más AcroForm mezők hozzáadását betöltött PDF-hez. Az XFDF <annots> fáján átmenő megjegyzés-jelöléshez lásd az XFDF annotáció importot és exportot a HotPDF-ben. A teljes API referencia és a próbaverzió letöltése a HotPDF Delphi PDF komponens oldalon található