HotPDF perkelia kelių pasirinkimų sąrašo dėžutės reikšmes pro FDF ir XFDF, išlaikydamas lauko reikšmę masyvu nuo pradžios iki galo. Nuo 2.755.0 versijos ExportLoadedFormToFDF, ExportLoadedInterchangeToFDF ir ExportLoadedFormToXFDF rašo kiekvieną pasirinktą parinktį kaip atskirą FDF eilutę arba XFDF <value> elementą, o atitinkami importo metodai kiekvieną reikšmę patikrina pagal lauko parinktis ir perstato /I pasirinkimų indeksus dar prieš ką nors keisdami. Niekas pakelyje nesuklijuojama į vieną eilutę
Gedimą, kurį tai taiso, nesunku atkurti. Paimkite užsakymo formą su kelių pasirinkimų sąrašo dėžute produkto parinkčių, leiskite vartotojui pasirinkti dvi iš jų, eksportuokite formos duomenis vidinei sistemai, tada importuokite redaguotą failą atgal į PDF. Iki šio pakeitimo sąrašo dėžutė sugrįždavo tuščia arba neteisinga. Priežastis ta, kad viena eksporto reikšmių turėjo eilutės lūžį, o senasis kelias buvo suplojęs pasirinkimus į vieną eilutėmis atskirtą eilutę. Iš tos eilutės sugrąžinti kelis pasirinkimus niekada nebuvo patikima, o su eksporto reikšme, kuri pati turi eilutės lūžį, tai apskritai negali veikti
Kodėl kelių pasirinkimų reikšmių suklijavimas eilutės lūžiais sugadina apsuką?
Suklijavus pasirinkimus į vieną eilutę, išmetamos ribos tarp reikšmių, o reikšmė gali turėti tą patį skirtuką, tad joks importuotojas negali eilutės teisingai vėl suskaidyti. ISO 32000-1 §12.7.4.4 leidžia choice lauko /V įrašui būti arba viena teksto eilute, arba teksto eilučių masyvu, o sąrašo dėžutė su MultiSelect vėliavėle (22 /Ff bitas) naudoja masyvo formą, tik paimta daugiau nei viena parinktis. Tas pats skyrius apibrėžia /I kaip 0-based parinkčių indeksų masyvą didėjančia tvarka, kurį peržiūros programos naudoja atskirti dviem parinktims, atsitiktinai dalijantiems eksporto reikšmę. HotPDF skaliarinis getteris GetFormFieldValue skaito tik eilutės formą, tad masyvas pro jį nuleisdavo eksportą iki tuščios eilutės, o senasis XFDF importas pakartotinius <value> elementus suklijuodavo su LF. Įsivaizduokite parinktį, eksportuotą kaip Deep, eilutės pabaiga, Blue: suklijavus Deep\nBlue\nRed gali būti du pasirinkimai arba trys, ir failas neduoda jokio būdo sužinoti, kuris. Pataisa buvo visiškai atsisakyti skaliaro apsukos viduryje
Ką turi eksportuoti FDF ir XFDF failai?
HotPDF kelių pasirinkimų reikšmę rašo kaip tipizuotą masyvą FDF ir kaip po vieną <value> elementą kiekvienam pasirinkimui XFDF, tad ribos diske lieka matomos. FDF kiekvienas elementas išlaiko rašybą, kurią turėjo šaltinio PDF: šešioliktainės eilutės išeina kaip hex, o literalinės eilutės pabėgamos vienu pagalbininku, kuris CR ir LF paverčia \r ir \n. XFDF šaknis neša xml:space="preserve", kaip reikalauja ISO 19444-1, o tai reiškia, kad bet kokie tarpo simboliai teksto elemento viduje skaičiuojasi kaip duomenys. HotPDF todėl rašo kiekvieno <value> pradžios žymę, pabėgtą tekstą ir pabaigos žymę vienu gabalu, įtrauką laiko už elemento ribų, o CR, LF ir TAB užkoduoja kaip simbolių nuorodas, kad XML analizatorius, taikantis eilutės pabaigos normalizaciją, negalėtų pakeisti originalių baitų
<!-- FDF: po vieną tipizuotą masyvą laukui -->
<< /T (options) /V [(Deep\nBlue) (Red)] >>
<< /T (region) /V [<45553132>] >>
<!-- XFDF: po vieną <value> pasirinkimui -->
<xfdf xmlns="http://ns.adobe.com/xfdf/" xml:space="preserve">
<fields>
<field name="options">
<value>Deep
Blue</value>
<value>Red</value>
</field>
</fields>
</xfdf>
Du eksporto kraštiniai atvejai verti žinoti, dar nerašant kviečiančiojo kodo. Pirma, ExportLoadedFormToFDF pilną FDF turinį sukonstruoja atmintyje dar prieš kurdamas taikinio failą (pataisyta 2.755.1), tad reikšmė, kurios negalima eksportuoti, pavyzdžiui, masyvas, laikantis ką kitą nei eilutes, kelia išimtį netrumpindama egzistuojančio failo. Antra, tuščias pasirinkimas sąrašo dėžutėje, kuri kartu siūlo ir tuščios eilutės eksporto reikšmę, XFDF yra dviprasmiškas, nes <value/> gali reikšti, kad nepasirinkta niekas, arba kad pasirinkta tuščioji parinktis. ExportLoadedFormToXFDF tą atvejį kelia išimtimi, vietoj to, kad spėliotų, ir kelia ją dar prieš atverdamas taikinio failą. FDF tokios dviprasmybės neturi, nes /V [] ir /V [()] skiriasi. Abu FDF eksportuotojai taip pat praleidžia tik-valdiklio terminalinius laukus be /T vardo, sutapdami su XFDF eksportuotoju, nes joks importuotojas tų įrašų negalėtų sugrąžinti į lauką
var
Pdf: THotPDF;
Written: Integer;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('order-form.pdf', '') > 0 then
begin
// Kelių pasirinkimų sąrašo dėžutės rašomos kaip /V [(...) (...)]
Written := Pdf.ExportLoadedFormToFDF('order-form.fdf');
try
Pdf.ExportLoadedFormToXFDF('order-form.xfdf');
except
on E: Exception do
// Tuščias pasirinkimas plius tuščia eksporto parinktis: XFDF jų
// atskirti negali, o egzistuojantis .xfdf failas lieka neliestas
ShowMessage('XFDF export refused: ' + E.Message);
end;
end;
finally
Pdf.Free;
end;
end;
Kaip HotPDF patikrina kelių pasirinkimų reikšmę importuodama?
HotPDF importuotą masyvą priima tik tada, kai taikinys yra choice laukas su įjungta MultiSelect vėliavėle ir kiekviena masyvo reikšmė sutampa su eksporto reikšme lauko /Opt masyve. Kiekviena parinkčių vieta panaudojama kartą, tad sąrašas su dviem parinktimis, dalijančiomis eksporto reikšmę b, priima [<62> <62>] kaip du atskirus pasirinkimus ir atmeta trečią b. Perstatyta /I seka /Opt tvarką, o ne gaunamų reikšmių tvarką, nes §12.7.4.4 reikalauja didėjančių indeksų. HotPDF naują /V ir /I stato kaip atsietus objektus ir priskiria juos tik po to, kai kiekviena reikšmė praėjo patikrą, tad atmesta reikšmė niekada nepalieka pusės masyvo ar pasenusių indeksų. Kopija rašoma į importuojamą lauką, o ne į bendrą protėvio masyvą, hex rašybos, atkeliaujančios iš FDF, lieka hex per įrašymą, o laukai, kurių skaičiavimai priklauso nuo sąrašo dėžutės, pažymimi perskaičiavimui. Jei reikia nustatyti tik vieną reikšmę, vienos formos lauko reikšmės nustatymas įkeltame PDF eina skaliariniu keliu, kuris pagal projektą neapsimato kelių pasirinkimų
Kai kurie kiti įrankiai paprastas ASCII eksporto reikšmes rašo kaip hex eilutes be baitų eiliškumo ženklo, pavyzdžiui <416272>, o tada eksportuoja XFDF, tuos hex skaitmenis išrašydami kaip tekstą. Griežtas literalinis gretinimas kelyje atgal žlunga, ir importas nutrūksta. 2.755.1 versija prideda vieną pakartojimą: kai reikšmė nesutampa su jokia parinktimi, HPDFHexSpellingText tekstą iškoduoja kaip hex turinį ir rezultatą sugretina dar kartą. Pakartojimas taikomas tik įvedimui, kuris kitaip būtų kėlęs išimtį, tad jis niekada nekeičia jau sutampusios reikšmės. Ta pati laida taip pat padarė, kad skaliarinis ir masyvinis keliai naudotų tą patį Unicode dekoderį, suprantantį PDFDocEncoding, UTF-16 su bet kuria baitų eiliškumo žyma ir UTF-8. Iki tol viena loginė reikšmė viename kelyje galėjo sutapti, o kitame žlugti dokumentuose, maišiusiuose koduotes
Kodėl teisėtas FDF failas analizės metu vis tiek gali prarasti laukus?
FDF skeneris, nesekantis šešioliktinėmis eilutėmis, gali perpjauti lauko žodyną pusiau, kai hex reikšmė baigiasi paties žodyno pabaigos ženklo kaimynystėje. << /T (region) /V <416273>>> pirmasis > užveria hex eilutę, bet naivus skeneris jį perskaito kartu su sekančiu > kaip žodyno pabaigą ir tyliai praranda lauką. Failo lygio FDF importuotojas jau sekė, ar yra hex eilutės viduje, o 2.755.1 tą patį daro masyvo ir žodynų skeneriai už ImportLoadedInterchangeFromFDF. Antras klausimas liečia netiesiogines nuorodas. FDF failas yra mažas PDF sintaksės dokumentas su savu objektų numeravimu (ISO 32000-1 §12.7.7), tad reikšmė, tokia kaip /V [11 0 R], nurodo FDF failo objektą 11, o ne jūsų pildomo PDF objektą 11. Supaprastintas HotPDF FDF analizatorius nuorodų faile neišsprendžia, tad tokį masyvą atmeta, vietoj to, kad skaitytų bet kokį objektą 11 taikinio dokumente
Failo, srauto ir XFDF importai klaidas praneša skirtingai
Trys importo keliai tikrina vienodai, bet žlugimus praneša skirtingai, ir verta vieną pasirinkti sąmoningai. ImportLoadedFormFromFDF praleidžia bet kurį lauką, nepraėjusį patikros, ir grąžina taikytų laukų skaičių, tad skaičius, mažesnis už tikėtą, yra vienintelis problemos požymis. ImportLoadedInterchangeFromFDF ir ImportLoadedFormFromXFDF kelia išimtį ties pirmuoju atmestu lauku. Kiekvienas laukas patvirtinamas savarankiškai, tad laukai, apdoroti iki išimties, išlaiko savo naujas reikšmes. Nė vieno iš šių elkitės ne kaip su transakcija per visą apsikeitimo failą: jei reikia visko-arba-nieko elgesio, įvykus išimčiai atminkite įkeltą dokumentą, vietoj to, kad jį įrašytumėte
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
// Tik laukai; reikšmė už /Opt arba ne-kelių-pasirinkimų taikinys kelia išimtį
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;
XFDF callback plėtimas nesulaužant egzistuojančių kviečiančiųjų
Masyvų palaikymas žemesnio lygio XFDF unite gyvena atskirame įraše THPDFXFDFArrayAccess ir naujuose HPDFXFDFExportFields bei HPDFXFDFImportFields overloaduose, o ne papildomuose laukuose, pridėtuose prie egzistuojančio THPDFXFDFAccess įrašo galo. Priežastis – dvejetainis suderinamumas. Kodas, pildantis THPDFXFDFAccess kaip vietinį kintamąjį, dažnai nustato tik žinomas vietas ir niekada neišvalo kitų, tad nauja funkcijos rodyklė, pridėta į tą įrašą, turėtų steko šiukšlių, o biblioteka imtų ją kaip tikrą callback. Su atskiru įrašu senieji kviečiantieji išlaiko seną išdėstymą ir senus overloadus, o tie overloadai viduje perduoda visur nulinį masyvų įrašą. Originalus skaliarinio importo overloadas suderinamumui vis dar suklijuoja pakartotines reikšmes su LF, ir tik masyvus atpažįstantis overloadas jas laiko atskirtas. Susiedami savą duomenų saugyklą, pradėkite nuo Default(THPDFXFDFArrayAccess). Grąžinkite True iš GetFormFieldValueArray bet kuriam sąrašinės reikšmės laukui, įskaitant tuščią, ir False, norėdami sugrįžti į skaliarinį callback
uses HPDFXFDF;
// Paprasta funkcijos rodyklė, ne „of object“: Context neša jūsų pačių saugyklą
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); // jūsų egzistuojantys skaliariniai susiejimai
ArrayAccess := Default(THPDFXFDFArrayAccess); // kiekviena nepanaudota vieta yra nil
ArrayAccess.GetFormFieldValueArray := StoreGetSelections;
HPDFXFDFExportFields(Access, ArrayAccess, Bytes);
end;
Kelių pasirinkimų apsikeitimas veikia ant sąrašo dėžučių, kurios jau egzistuoja ir turi uždėtą MultiSelect bitą /Ff. Kaip choice laukai ir jų vėliavėlių bitai iš viso kuriami, žiūrėkite ListBox ir kitų AcroForm laukų pridėjime į įkeltą PDF. Komentarų ženklinimui, einančiam pro XFDF <annots> medį, žiūrėkite XFDF anotacijų importą ir eksportą HotPDF. Pilna API atskaita ir bandomasis atsisiuntimas – HotPDF Delphi PDF komponento puslapyje