Forma, kurios tarpinės sumos persiskaičiuoja programoje „Acrobat“, bet lieka nejudrios jūsų pačių Delphi peržiūros lange, beveik niekada nėra atvaizdavimo klaida. „PDFium“ išjungia visą dokumento „JavaScript“ kodą, nebent pagrindinė programa prijungia JS platformą, o „PDFium Component“ automatiškai sujungia šią platformą nuo v2.13.2 versijos, todėl „AcroForm“ „JavaScript“, dokumento lygio scenarijai bei laukų skaičiavimai vykdomi visada, kai įkeliama V8 palaikanti DLL. Pagrindinė programa lieka atsakinga per įvykių rinkinį, kuris gali rodyti, nukreipti arba blokuoti viską, ką bando daryti scenarijus
Šis vienas sakinys išsprendžia kelias palaikymo užklausas, kurias matėme: „mano užsakymo forma skaičiuoja sumas Acrobat programoje, bet ne mano programoje“, „app.alert niekada nepasileidžia“, „datos laukas pats nesusiformuoja“. Visų jų priežastis yra ta pati sutartis (contract), ir jos supratimui verta skirti dešimt minučių, nes ši sutartis taip pat yra jūsų saugumo riba prieš kenksmingus dokumentus
Kodėl PDF formų skaičiavimai neveikia „PDFium“?
„PDFium“ dokumento „JavaScript“ vykdymą laiko griežtai pasirenkamu dalyku (opt-in): jei formos užpildymo aplinkos narys „m_pJsPlatform“ yra lygus „NULL“, visi dokumento scenarijai tyliai praleidžiami. Nėra jokios klaidos, jokio įrašo žurnale ar dalinio vykdymo. Skaičiuojami laukai išlaiko paskutinę išsaugotą reikšmę, „app.alert“ išnyksta, o formatavimo scenarijai niekada nepasileidžia. „Acrobat“, kuri visada turi savo „JavaScript“ variklį, sėkmingai vykdo šį failą, todėl šis skirtumas atrodo kaip jūsų kodo klaida, nors iš tikrųjų tai yra apgalvotas numatytasis nustatymas pačioje bibliotekoje
„PDFium Component“ turėjo antrą, istorinį šios problemos sluoksnį. Iki v2.13.1 versijos sąsaja prijungdavo „m_pJsPlatform“ tik XFA inicijavimo šakoje, zodžiu, paprasti „AcroForm“ dokumentai (kurie sudaro absoliučią daugumą PDF dokumentų su scenarijais) niekada negaudavo „JavaScript“ platformos, net jei buvo V8 palaikanti DLL. Versija 2.13.2 iškėlė platformos prijungimą iš XFA sprendimo: „InitializeFormFill“ dabar sukuria „IPDF_JsPlatform“ lentelę visada, kai „V8FeaturesAvailable“ grąžina „True“, nepriklausomai nuo to, ar tai XFA dokumentas. Praktinis rezultatas yra tas, kad dokumento lygio scenarijai, laukų skaičiavimo grandinės bei „app.alert“ dabar sėkmingai vykdomi ir įprastuose „AcroForm“ dokumentuose
Kurios V8 DLL reikia „AcroForm“ JavaScript vykdymui Delphi aplinkoje?
„AcroForm“ „JavaScript“ vykdymui reikalingas „PDFium“ binarinis failas, sukompiliuotas su V8 varikliu, kurį „PDFium Component“ įkelia kaip „pdfium.v8.dll“, kai globalus požymis „EnableV8Engine“ yra nustatytas į „True“ prieš pirmąjį bibliotekos įkėlimą. Čia yra vienas spąstas, kurį verta įvardyti atvirai: komponentas automatiškai aptinka XFA dokumentus, ieškodamas „/XFA“ žymių, ir nustato „EnableV8Engine“ už jus, tačiau paprastas „AcroForm“ dokumentas su „JavaScript“ neturi XFA žymės, todėl automatinis aptikimas nevyksta. Jei jūsų peržiūros programa turi vykdyti formų scenarijus, patys nustatykite šį požymį vieną kartą prieš įkeliant pirmąjį dokumentą; kai įkeliamas paprastas „pdfium.dll“, procesas nebegali pakeisti variklio
uses PDFium;
procedure TViewerForm.FormCreate(Sender: TObject);
begin
// Must run before the singleton PDFium library first loads;
// once plain pdfium.dll is in the process it cannot be swapped
EnableV8Engine := True;
end;
procedure TViewerForm.OpenDocument(const FileName: string);
begin
FPdf.LoadDocument(FileName);
if not V8FeaturesAvailable then
StatusBar.SimpleText :=
'JavaScript disabled: pdfium.v8.dll not deployed';
end;
„V8FeaturesAvailable“ yra tikrasis vykdymo laiko patikrinimas: jis praneša, ar įkeltas failas iš tikrųjų turi nuo V8 priklausančius eksportuojamus metodus, todėl patikra veikia nepriklausomai nuo to, kurią DLL įdiegė diegimo programa. Tas pats variklis ir tas pats požymis valdo ir dinaminį XFA atvaizdavimą; platesnė informacija apie tai, kaip veikia XFA aptikimas ir automatinis V8 parinkimas, aprašyta XFA formų aptikime ir XFA paketų išgavime su PDFium
Pagrindinės programos įvykiai pranešimams, spausdinimui, paštui bei formų pateikimui
Kiekvienas matomas scenarijaus veiksmas grįžta į jūsų kodą per „TPdf“ įvykius, ir kiekvienas iš jų pagal nutylėjimą yra saugiai ignoruojamas, jei nėra priskirtas. „OnJavaScriptAlert“ gauna pranešimą, antraštę, mygtuko tipą bei piktogramą iš „app.alert“ ir leidžia grąžinti paspausto mygtuko rezultatą; „OnJavaScriptResponse“ apdoroja „app.response“ įvesties užklausas, įskaitant slaptažodžio požymį; „OnJavaScriptBeep“, „OnJavaScriptPrint“, „OnJavaScriptMail“ ir „OnJavaScriptSubmitForm“ pateikia atitinkamas užklausas su pilnais jų parametrais. Peržiūros programa, kuri nepriskiria nė vieno iš shių įvykių, vis tiek teisingai atvaizduoja vaizdą ir skaičiuoja reikšmes; tiesiog dokumentas negali atidaryti dialogų, spausdinti ar ką nors siųsti
procedure TViewerForm.PdfJavaScriptAlert(Sender: TObject;
const Msg, Title: WString; ButtonType, Icon: Integer;
var Result_: Integer; var Handled: Boolean);
begin
// Route app.alert through the VCL so it is owned by our window
Result_ := MessageBox(Handle, PWideChar(Msg), PWideChar(Title),
MB_OK or MB_ICONINFORMATION);
Handled := True;
end;
procedure TViewerForm.PdfJavaScriptSubmitForm(Sender: TObject;
const Url: WString; const Data: TBytes);
begin
// Nothing is transmitted unless this handler chooses to send it
LogAudit(Format('Document requested form submit to %s (%d bytes)',
[Url, Length(Data)]));
end;
Šis numatytosios elgsenos išskyrimas yra svarbus saugumo modeliavimui. „OnJavaScriptMail“ ir „OnJavaScriptSubmitForm“ yra tik pranešamojo stiliaus: „PDFium Component“ pats neatlieka jokių tinklo ar MAPI veiksmų, todėl kenksmingas dokumentas, kuris kviečia „this.submitForm“ ar „this.mailDoc“ prieš nepasiruošusią programą, neatlieka visiškai nieko. Programa turi pasirinkti šį vykdymą (opt-in), priskirdama apdorojimo funkciją ir pati realizuodama duomenų perdavimą. Tai reiškia, kad sprendimas išsiųsti duomenis iš kompiuterio visada lieka jūsų rankose, priimamas jūsų kode su žinomu URL bei duomenimis
Kaip sustabdyti kenksmingą PDF failą nuo URL nuorodų atidarymo?
URI veiksmai yra ta vieta, kur anksčiau numatytoji elgsena buvo aktyvi, o ne pasyvi, todėl ji gavo atskirą blokavimo galimybę (veto). Iki v2.13.1 versijos „FFI_DoURIActionWithKeyboardModifier“ iškvietimas besąlygiškai vykdė bet kokį URI, kurį pateikė XFA laukas, kas leido kenksmingam dokumentui atidaryti bet kokį URL paspaudimo metu. Įvykis „OnXfaUriAction“ tai uždaro: komponentas kreipiasi į įvykį prieš vykdymą, ir funkcija, nustatanti „Handled“ į „True“, visiškai atšaukia numatytąjį „ShellExecuteW“ iškvietimą. Palikus įvykį nepriskirtą, išlaikomas senasis suderinamumas, todėl saugumu besirūpinanti peržiūros programa visada turėtų jį priskirti
procedure TViewerForm.PdfXfaUriAction(Sender: TObject;
const Uri: WString; Modifiers: Integer; var Handled: Boolean);
begin
// Veto anything that is not plain https; the default would
// otherwise ShellExecuteW the URI as-is
if not SameText(Copy(Uri, 1, 8), 'https://') then
begin
LogAudit('Blocked URI action: ' + Uri);
Handled := True;
end;
end;
Leistinų schemų sąrašas yra minimali apsauga; griežtesnė programa papildomai prašo naudotojo patvirtinimo arba nukreipia viską per savo izoliuotą naršyklės komponentą. Ta pati blokavimo filosofija taikoma ir kitiems v2.13.1 įvykiams: „OnXfaEmail“, „OnXfaHttpRequest“ ir „OnXfaOpenFile“ atveria tekstinius prašomo veiksmo parametrus, kol pats komponentas neatlieka jokių tinklo ar failų operacijų. Kaip šios apsaugos dera platesnėje nepatikimų dokumentų strategijoje, įskaitant atvaizdavimo izoliavimą, aprašyta straipsnyje apie saugios PDF peržiūros kūrimą Delphi aplinkoje
Rašymas į aktyvų lauką naudojant SetFocusedFormFieldText
„TPdf.SetFocusedFormFieldText“, įdiegtas v2.13.2 versijoje, yra rašymo analogas savybei „FocusedFormFieldText“: jis pažymi visą aktyvaus lauko turinį per „FORM_SelectAllText“ ir perrašo jį per „FORM_ReplaceSelection“ tiksliai taip, lyg pats naudotojas būtų įvedęs tekstą. Kadangi tekstas patenka per interaktyvaus redagavimo kelią, visi klavišų paspaudimo, formatavimo bei skaičiavimo scenarijai suveikia taip pat, kaip ir rankinio įvedimo atveju, todėl tai yra tinkamas būdas programiškai pildyti laukus programoje su įjungtu „JavaScript“. Laukų perėjimas ir fokusavimo valdymas aprašyti formos laukų navigacijoje su PDFium komponentu
if FPdf.FocusedFormFieldIndex >= 0 then
begin
if not FPdf.SetFocusedFormFieldText('42.50') then
ShowMessage('No focused field accepts text on this page');
// AcroForm: value commits to /V when focus leaves the field.
// XFA: the write stays in the in-memory edit buffer only and
// will not survive a save
end;
Duomenų išlaikymo asimetrija šiame komentare yra tikra riba, o ne šiaip pastaba. „AcroForm“ tekstiniams bei pasirinkimo laukams redaguota reikšmė įrašoma į lauko „/V“ įrašą praradus fokusavimą ir išlieka išsaugojus failą. XFA tekstiniams laukams rašymas patenka tik į darbinę atmintį (in-memory edit buffer), nes „PDFium“ neturi viešo API, leidžiančio grąžinti reikšmes atgal į XFA duomenų rinkinio paketą; išsaugojus dokumentą šis pakeitimas nebus perkeltas. Jei reikalingas ilgalaikis XFA duomenų redagavimas, redaguokite patį duomenų rinkinio XML failą, o ne valdymo elementą
Ribos, kurias verta žinoti prieš išleidžiant programą
Išlieka dvi aiškios ribos. Pirma, „PDFium“ realizuota „JavaScript“ API yra tik veikiantis „Acrobat“ objektų modelio poaibis, orientuotas į formų scenarijų branduolį: laukų pasiekimą, skaičiavimo bei formatavimo įvykius, „app.alert“ bei „app.response“, spausdinimo, pašto ir formų pateikimo užklausas. Dokumentai, kurie naudoja egzotiškus, tik „Acrobat“ aplinkoje egzistuojančius objektus, neveiks (dažniausiai tyliai), todėl išbandykite realias jūsų naudotojų formas, užuot tikėjęsi visiško atitikimo. Antra, įjungus V8 variklį, reikia kartu pateikti failą „pdfium.v8.dll“, kuris yra žymiai didesnis už paprastą bibliotekos variantą. Peržiūros programa, kuriai nereikia scenarijų vykdymo ar XFA formų, gali naudoti mažesnę DLL ir neprijungti „m_pJsPlatform“ — tai pati savaime yra gera saugumo pozicija
Tolesnis šablonas yra paprastas: nustatykite „EnableV8Engine“ programos paleidimo metu, priskirkite pranešimų bei atsakymų įvykius, kad dialogai atrodytų natūraliai, priskirkite „OnXfaUriAction“ ir registruokite pašto bei pateikimo įvykius, o visa kita palikite numatytajai elgsenai, kol prireiks ko nors kito. Pilnas įvykių aprašymas ir formų pildymo API dokumentacija pateikiama „PDFium Component“ produkto puslapyje