HotPDF kuljettaa monivalintaluetteloruutujen arvot FDF:n ja XFDF:n läpi pitämällä kenttäarvon taulukkona päästä päähän. Versiosta 2.755.0 alkaen ExportLoadedFormToFDF, ExportLoadedInterchangeToFDF ja ExportLoadedFormToXFDF kirjoittavat jokaisen valitun vaihtoehdon omaksi FDF-merkkijonokseen tai XFDF-<value>-elementiksi, ja vastaavat tuontimetodit tarkistavat jokaisen arvon kentän vaihtoehtoja vasten ja rakentavat /I-valintaindeksit ennen kuin muuttavat mitään. Mitään ei liimata yhdeksi merkkijonoksi matkan varrella
Vika, jonka tämä korjaa, on helppo toistaa. Ota tilauslomake, jossa on monivalintaluetteloruutu tuotevaihtoehtoja, anna käyttäjän valita kaksi niistä, vie lomakedata taustajärjestelmälle ja tuo sitten muokattu tiedosto takaisin PDF:ään. Ennen tätä muutosta luetteloruutu palasi tyhjänä tai vääränä. Syy on siinä, että yksi vientiarvoista sisälsi rivinvaihdon, ja vanha polku oli litistänyt valinnat yhdeksi rivillä erotetuksi merkkijonoksi. Useiden valintojen saaminen takaisin kyseisestä merkkijonosta ei ollut koskaan luotettavaa, ja vientiarvolla, joka itse sisältää rivinvaihdon, se ei voi onnistua lainkaan
Miksi monivalinta-arvojen liittäminen rivinvaihdolla rikkoo edestakaisen kuljetuksen?
Valintojen liittäminen yhdeksi merkkijonoksi heittää pois arvojen väliset rajat, ja arvo voi sisältää erottimen, joten mikään tuoja ei voi jakaa merkkijonoa takaisin oikein. ISO 32000-1 §12.7.4.4 sallii valintakentän /V-merkinnän olla joko yksi tekstimerkkijono tai tekstimerkkijonojen taulukko, ja luetteloruutu MultiSelect-lipulla (bitti 22 kohdassa /Ff) käyttää taulukkomuotoa heti, kun yhtä useampi vaihtoehto on valittu. Sama kohta määrittelee /I-merkinnän nollapohjaisten vaihtoehtoindeksien taulukoksi nousevassa järjestyksessä, mitä katselimet käyttävät erottamaan kaksi vaihtoehtoa, jotka sattuvat jakamaan vientiarvon. HotPDF:ssä skalaarigetteri GetFormFieldValue lukee vain merkkijonomuodon, joten taulukon ajaminen sen läpi rappeutti viennin tyhjäksi merkkijonoksi, ja vanha XFDF-tuonti liitti toistuvat <value>-elementit LF:llä. Kuvittele vaihtoehto, joka viedään muodossa Deep, LF, Blue: liittämisen jälkeen Deep\nBlue\nRed voi olla kaksi valintaa tai kolme, eikä tiedosto anna mitään keinoa tietää kumpaa. Korjaus oli lopettaa skalaarin käyttäminen edestakaisen kuljetuksen keskellä kokonaan
Mitä vietetyt FDF- ja XFDF-tiedostot sisältävät?
HotPDF kirjoittaa monivalinta-arvon tyypitettynä taulukkona FDF:ssä ja yhtenä <value>-elementtinä per valinta XFDF:ssä, joten rajat pysyvät näkyvinä levylle tallennettuina. FDF:ssä jokainen alkio pitää kirjoitusasunsa, jonka sillä oli lähde-PDF:ssä: heksadesimaalimerkkijonot menevät ulos heksana, ja literaalimerkkijonot escapataan yhdellä apurilla, joka muuttaa CR:n ja LF:n muotoon \r ja \n. XFDF:ssä juuri kantaa xml:space="preserve"in, niin kuin ISO 19444-1 vaatii, mikä tarkoittaa, että mikä tahansa tyhjämerkki tekstielementin sisällä lasketaan dataksi. HotPDF kirjoittaa siksi jokaisen <value>-elementin aloitustagin, escapatun tekstin ja lopputagin yhtenä kappaleena, pitää sisennyksen elementin ulkopuolella ja koodaa CR:n, LF:n ja TABin merkkiviittauksiksi, jotta rivinpäiden normalisointia soveltava XML-jäsennin ei voi muuttaa alkuperäisiä tavuja
<!-- FDF: yksi tyypitetty taulukko per kenttä -->
<< /T (options) /V [(Deep\nBlue) (Red)] >>
<< /T (region) /V [<45553132>] >>
<!-- XFDF: yksi <value> per valinta -->
<xfdf xmlns="http://ns.adobe.com/xfdf/" xml:space="preserve">
<fields>
<field name="options">
<value>Deep
Blue</value>
<value>Red</value>
</field>
</fields>
</xfdf>
Kaksi vientin reunatapausta kannattaa tuntea ennen kuin kirjoitat kutsukoodin. Ensinnäkin ExportLoadedFormToFDF rakentaa kokonaisen FDF-rungon muistiin ennen kuin se luo kohdetiedoston (korjattu versiossa 2.755.1), joten vientikelvoton arvo, kuten merkkijonojen muuta kuin sisältävä taulukko, nostaa poikkeuksen katkaisematta olemassa olevaa tiedostoa. Toiseksi tyhjä valinta luetteloruudussa, joka tarjoaa myös tyhjän merkkijonon vientiarvon, on monitulkintainen XFDF:ssä, koska <value/> voi tarkoittaa joko sitä, ettei mitään ole valittu, tai sitä, että tyhjä vaihtoehto on valittu. ExportLoadedFormToXFDF nostaa tuossa tapauksessa poikkeuksen arvaamisen sijaan, ja se nostaa sen ennen kuin kohdetiedosto avataan. FDF:ssä ei ole tuota monitulkintaisuutta, sillä /V [] ja /V [()] ovat eri asiat. Molemmat FDF-viejät ohittavat myös widget-päätekentät, joilla ei ole /T-nimeä, XFDF-viejän tapaan, koska mikään tuoja ei voisi koskaan yhdistää kyseisiä merkintöjä takaisin kenttään
var
Pdf: THotPDF;
Written: Integer;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('order-form.pdf', '') > 0 then
begin
// Monivalintaluetteloruudut kirjoitetaan muotoon /V [(...) (...)]
Written := Pdf.ExportLoadedFormToFDF('order-form.fdf');
try
Pdf.ExportLoadedFormToXFDF('order-form.xfdf');
except
on E: Exception do
// Tyhjä valinta plus tyhjä vientivaihtoehto: XFDF ei erota niitä,
// ja olemassa oleva .xfdf-tiedosto jää koskemattomaksi
ShowMessage('XFDF export refused: ' + E.Message);
end;
end;
finally
Pdf.Free;
end;
end;
Miten HotPDF valido monivalinta-arvon tuonnissa?
HotPDF hyväksyy tuodun taulukon vain, kun kohde on valintakenttä, jonka MultiSelect-lippu on asetettu, ja kun jokainen taulukon arvo täsmää vientiarvoon kentän /Opt-taulukossa. Jokainen vaihtoehtopaikka voidaan käyttää kerran, joten luettelo, jossa on kaksi samaa vientiarvoa b jakavaa vaihtoehtoa, hyväksyy [<62> <62>]in kahtena erillisenä valintana ja hylkää kolmannen b:n. Rakennettu /I noudattaa /Opt-järjestystä eikä saapuvien arvojen järjestystä, sillä §12.7.4.4 vaatii nousevia indeksejä. HotPDF rakentaa uudet /V- ja /I-merkinnät irrotettuina objekteina ja sijoittaa ne vasta, kun jokainen arvo on läpäissyt validoinnin, joten hylätty arvo ei koskaan jätä puoltakaan taulukkoa tai vanhentuneita indeksejä taakseen. Kopio kirjoitetaan tuotavaan kenttään eikä jaettuun esivanhemman taulukkoon, FDF:stä saapuvat heksakirjoitusasut pysyvät heksana tallennuksen läpi, ja kentät, joiden laskennat riippuvat luetteloruudusta, merkitään uudelleenlaskettaviksi. Jos sinun tarvitsee vain asettaa yksi arvo, yhden lomakekentän arvon asettaminen ladattuun PDF:ään kulkee skalaaripolkua, joka ei suunnittelusta johtuen käsittele monivalintoja
Jotkin muut työkalut kirjoittavat tavalliset ASCII-vientiarvot heksamerkkijonoina ilman tavujärjestysmerkkiä, esimerkiksi <416272>, ja vievät sitten XFDF:n kirjoittamalla kyseiset heksanumerot tekstiksi. Tiukka literaalivertailu paluumatkalla epäonnistuu, ja tuonti keskeytyy. Versio 2.755.1 lisää yhden uusinnan: kun arvo ei täsmää mihinkään vaihtoehtoon, HPDFHexSpellingText dekoodaa tekstin heksahyötykuormana ja vertaa tulosta uudelleen. Uusinta koskee vain syötettä, joka muuten olisi nostanut poikkeuksen, joten se ei koskaan muuta arvoa, joka jo täsmäsi. Sama julkaisu teki myös skalaari- ja taulukkopoluista saman Unicode-dekooderin käyttäjiä, joka ymmärtää PDFDocEncodingin, UTF-16:n kummalla tahansa tavujärjestysmerkillä ja UTF-8:n. Ennen sitä yksi looginen arvo saattoi täsmätä toisella polulla ja epäonnistua toisella asiakirjoissa, jotka sekoittivat koodauksia
Miksi kelvollinen FDF-tiedosto voi silti menettää kenttiä jäsentämisen aikana?
FDF-skanneri, joka ei seuraa heksadesimaalimerkkijonoja, voi leikata kenttäsanakirjan kahtia, kun heksa-arvo päättyy aivan sanakirjan päätemerkin viereen. Muodossa << /T (region) /V <416273>> ensimmäinen > sulkee heksamerkkijonon, mutta naiivi skanneri lukee sen yhdessä seuraavan >in kanssa sanakirjan lopuksi ja pudottaa kentän hiljaa. Tiedostotason FDF-tuodilla oli jo kirjattuna se, oltiinko heksamerkkijonon sisällä, ja versiossa 2.755.1 myös ImportLoadedInterchangeFromFDFin takana olevat taulukko- ja sanakirjaskannerit tekevät samoin. Toinen asia koskee epäsuoria viittauksia. FDF-tiedosto on pieni PDF-syntaksin asiakirja omalla objektinumeroinnillaan (ISO 32000-1 §12.7.7), joten arvo kuten /V [11 0 R] viittaa FDF-tiedoston objektiin 11, ei täyttämäsi PDF:n objektiin 11. HotPDF:n yksinkertaistettu FDF-jäsennin ei ratkaise viittauksia tiedoston sisällä, joten se hylkää tällaisen taulukon sen sijaan että lukisi sen, mitä objekti 11 sattuu olemaan kohdeasiakirjassa
Tiedosto-, stream- ja XFDF-tuonnit raportoivat virheet eri tavoin
Kolme tuontireittiä valido samalla tavalla mutta raportoivat epäonnistumiset eri tavoin, ja yhden valitseminen tahallaan kannattaa. ImportLoadedFormFromFDF ohittaa jokaisen validoinnin epäonnistavan kentän ja palauttaa soveltamiensa kenttien määrän, joten odotettua pienempi määrä on ainoa merkki ongelmasta. ImportLoadedInterchangeFromFDF ja ImportLoadedFormFromXFDF nostavat poikkeuksen ensimmäisessä hylätyssä kentässä. Jokainen kenttä sitoutuu omanaikaisesti, joten poikkeusta edeltäneet kentät säilyttävät uudet arvonsa. Älä kohtele näistä mitään transaktiona koko vaihtotiedoston yli: jos tarvitset kaikki-tai-ei mitään -käyttäytymisen, hylkää ladattu asiakirja, kun poikkeus tapahtuu, tallentamisen sijaan
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
// Vain kentät; arvo /Opt:n ulkopuolella tai ei-monivalinta-kohde nostaa poikkeuksen
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-takaisinkutsujen laajentaminen rikkomatta olemassa olevia kutsujia
Taulukkotuki alemman tason XFDF-yksikössä asuu erillisessä tietueessa, THPDFXFDFArrayAccessissa, sekä uusissa HPDFXFDFExportFieldsin ja HPDFXFDFImportFieldsin ylikuormituksissa, ei ylimääräisinä kenttinä olemassa olevan THPDFXFDFAccess-tietueen loppuun lisättynä. Syy on binääriyhteensopivuus. Koodi, joka täyttää THPDFXFDFAccessin paikallisena muuttujana, asettaa usein vain ne paikat, jotka se tuntee, eikä koskaan tyhjennä loput, joten kyseiseen tietueeseen lisätty uusi funktioosoitin sisältäisi pinoromua, ja kirjasto pitäisi sitä oikeana takaisinkutsuna. Erillisellä tietueella vanhat kutsujat säilyttävät vanhan asettelun ja vanhat ylikuormitukset, ja kyseiset ylikuormitukset välittävät sisäisesti kaikki-nil-tietueen. Alkuperäinen skalaarituonnin ylikuormitus liittää edelleen toistuvat arvot LF:llä yhteensopivuuden vuoksi, ja vain taulukkotietoinen ylikuormitus pitää ne erillään. Kun kytket omaa datavarastoasi, aloita funktiosta Default(THPDFXFDFArrayAccess). Palauta True funktiolta GetFormFieldValueArray mille tahansa luettelopuoliselle kentälle, mukaanlukien tyhjäksi jäänyt, ja False palataksesi skalaaritakaisinkutsuun
uses HPDFXFDF;
// Tavallinen funktioosoitin, ei 'of object': Context kantaa oman varastosi
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); // olemassa olevat skalaarikytkentäsi
ArrayAccess := Default(THPDFXFDFArrayAccess); // jokainen käyttämätön paikka on nil
ArrayAccess.GetFormFieldValueArray := StoreGetSelections;
HPDFXFDFExportFields(Access, ArrayAccess, Bytes);
end;
Monivalintavaihto toimii luetteloruuduilla, jotka ovat jo olemassa ja joilla on MultiSelect-bitti asetettuna kohdassa /Ff. Siitä, miten valintakentät ja niiden lippubitit luodaan alun perin, kerrotaan artikkelissa ListBoxin ja muiden AcroForm-kenttien lisäämisestä ladattuun PDF:ään. XFDF:n <annots>-puun kautta kulkevasta kommenttimerkinnästä kerrotaan artikkelissa XFDF-kommenttien tuonti ja vienti HotPDF:ssä. Täydellinen API-referenssi ja kokeilulataus ovat HotPDF Delphi PDF -komponentin sivulla