Techninis straipsnis

Kodėl XFA laukų pakeitimai neišlieka išsaugant PDFium Delphi kalboje

TPdf.SetFocusedFormFieldText pakeičia sufokusuoto XFA lauko gyvą buferį, tačiau jo neįrašo į išsaugomą datasets paketą, todėl reikšmė po atidarymo dingstaTPdf.SetFocusedFormFieldTextdatasets/V

AcroForm ir XFA PDFium įrašymo kelyje yra skirtingi formos modeliai, todėl sėkmingas vienos eilutės API iškvietimas negarantuoja XFA išsaugojimoSetFocusedFormFieldTextAcroForm JavaScript straipsnis

Kaip SetFocusedFormFieldText įrašo lauko reikšmę

SetFocusedFormFieldText imituoja FORM_SelectAllText ir FORM_ReplaceSelection klaviatūros redagavimą, o FocusedFormFieldText skaito FORM_GetFocusedText rezultatąTPdf.SetFocusedFormFieldTextFORM_SelectAllTextFORM_ReplaceSelectionFocusedFormFieldTextFORM_GetFocusedTextSetFocusedFormFieldText

if Pdf.FocusedFormFieldIndex >= 0 then
begin
  if Pdf.SetFocusedFormFieldText('1284.50') then
    Log('Buffer now reads: ' + Pdf.FocusedFormFieldText)
  else
    Log('No field is focused, or it does not accept text');
end
else
  Log('Focus a field first - FocusFormField or a real click');

Kodėl AcroForm išlaiko reikšmę, o XFA ją praranda

AcroForm reikšmė išlieka, nes praradus fokusą ji įrašoma į /V, o TPdf.ClearFormFieldFocus priverčia šį įrašymą prieš SaveAs/VTPdf.ClearFormFieldFocusFORM_ForceToKillFocusTPdf.SaveAs/V

Pdf.FocusFormField(FieldIndex);
Pdf.SetFocusedFormFieldText('1284.50');
Pdf.ClearFormFieldFocus;              // forces the /V commit now
Pdf.SaveAs('invoice-acroform.pdf');

// Reopen and confirm - this is an AcroForm document, so it holds
Pdf.Active := False;
Pdf.FileName := 'invoice-acroform.pdf';
Pdf.Active := True;
Pdf.FocusFormField(FieldIndex);
Assert(Pdf.FocusedFormFieldValue = '1284.50');   // passes

Kur iš tikrųjų gyvena XFA lauko pakeitimas

XFA reikšmė lieka CPWL_Edit buferyje ir neperkeliama į PDF datasets paketą, todėl TPdf.GetXfaDatasets prieš ir po redagavimo grąžina tuos pačius baitusCPWL_EditdatasetsTPdf.GetXfaDatasets

var
  Before, After: TBytes;
begin
  Before := Pdf.GetXfaDatasets;
  Pdf.FocusFormField(FieldIndex);
  Pdf.SetFocusedFormFieldText('1284.50');
  After := Pdf.GetXfaDatasets;
  // Before and After are byte-for-byte identical on an XFA document -
  // the edit never touched the packet GetXfaDatasets reads from
end;

Ar tai PDFium Component klaida, ar PDFium ribojimas

Viešoji PDFium API neturi FPDF_SetXFAPacket ar FPDF_SaveAsXFA, o FPDF_SaveAsCopy neturi kablio gyvai XFA būsenai išplautiFPDF_SetXFAPacketFPDF_SaveAsXFAdatasetsFPDF_SaveAsCopyTPdf.SaveAs

v2.13.2 auditas parodė šį apribojimą kartu su SetFocusedFormFieldText, nors atskira AcroForm JavaScript spraga buvo pataisytaSetFocusedFormFieldTextFORM_ReplaceSelectionapp.alertAcroForm JavaScript vykdymą su PDFium Component

Ką daryti Delphi programoje

AcroForm atveju prieš SaveAs iškvieskite ClearFormFieldFocus, o mišriam dokumentui patikrinkite FormType arba XFA būsenąClearFormFieldFocusSaveAsFormTypeXFAXFA formų aptikimą ir XFA paketų išskleidimą/V

Dinaminei XFA formai GetXfaDatasets naudokite kaip pradinį XML, atskirai saugokite naudotojo pakeitimus ir patys pataisykite XMLGetXfaDatasetsCPWL_Edit

function ExportEditedXfaValue(Pdf: TPdf; const FieldPath,
  NewValue: string): TBytes;
var
  DatasetsXml: string;
begin
  // GetXfaDatasets ships with PDFium Component; PatchXmlNode below is
  // your own helper over your own XML library, nothing PDFium provides
  DatasetsXml := TEncoding.UTF8.GetString(Pdf.GetXfaDatasets);
  DatasetsXml := PatchXmlNode(DatasetsXml, FieldPath, NewValue);
  Result := TEncoding.UTF8.GetBytes(DatasetsXml);
end;

Kaip pastebėti spragą anksčiau nei klientas

TPdf.SaveAs gali grąžinti True, nors XFA reikšmė neišliko, todėl regresijos testas turi iš naujo atverti failą ir palyginti reikšmęTPdf.SaveAsTrueGetXfaDatasets

Tai PDFium riba, kurią reikia įvertinti projektuojant, o ne įprasta PDFium Component Delphi susiejimo klaidaSetFocusedFormFieldTextPDFium Component