Tekninen artikkeli

Tekninen artikkeli: PDFlibPas Compliance and Signing Workbench in Delphi suomeksi

Tämä lokalisoitu versio keskittyy aiheeseen Building a Compliance and Signing Workbench in Delphi with PDFlibPas ja käyttää päivitettyä englanninkielistä artikkelia teknisenä perustana Delphi-, PDF- ja dokumenttiohjelmistotiimeille

Sivu muuntaa päivitetyn pohja-artikkelin konkreettisiksi tarkistuspisteiksi suunnittelua, toteutusta ja validointia varten

Mitä englanninkielisestä artikkelista synkronoitiin

Englanninkielistä pohja-artikkelia on laajennettu käytännön kontekstilla, teknisillä päätöksillä ja konkreettisilla esimerkeillä, joten tätä sivua kannattaa lukea työohjeena eikä lyhyenä yhteenvetona

Päivitetyn pohja-artikkelin tärkeät osat:

  • Käytä ensin pieniä toistettavia syötetiedostoja
  • Pidä tuotenimet, API-nimet, tiedostonimet ja literal-arvot muuttumattomina
  • Tallenna validatorin tuloste ja versiotiedot luodun esimerkkitiedoston kanssa

Käytännön toteutusvalinnat

Aloita tiedostotyypistä, odotetusta tuloksesta ja virhetilasta, jonka käyttäjän pitää nähdä. Sido sen jälkeen jokainen API-kutsu tarkistettavaan tulokseen, jotta validointi, lokitus ja tuki voivat toistaa asiakkaan tilanteen

  • Käytä ensin pieniä toistettavia syötetiedostoja
  • Pidä tuotenimet, API-nimet, tiedostonimet ja literal-arvot muuttumattomina
  • Tallenna validatorin tuloste ja versiotiedot luodun esimerkkitiedoston kanssa

Koodi ja API-kohdat

Koodiesimerkit säilytetään muuttumattomina, jotta kehittäjä voi verrata niitä suoraan Delphi-, C++Builder- ja Lazarus/FPC-projekteihin

var
  PDF: TPDFlib;
  ListID, I: Integer;
begin
  PDF := TPDFlib.Create;
  try
    ListID := PDF.CheckFileCompliance('invoice-fixed.pdf', '', 1, 0);  // 1 = PDF/A
    if ListID = 0 then
    begin
      if PDF.LastErrorCode <> 0 then
        raise Exception.Create('Preflight could not read the file')
      else
        Writeln('No PDF/A findings');
    end
    else
    begin
      for I := 0 to PDF.GetStringListCount(ListID) - 1 do
        Writeln(PDF.GetStringListItem(ListID, I));
      PDF.ReleaseStringList(ListID);
    end;
  finally
    PDF.Free;
  end;
end;
ProcessID := PDF.NewSignProcessFromFile('invoice-fixed.pdf', '');
if ProcessID = 0 then
  raise Exception.Create('Cannot open source for signing');
PDF.SetSignProcessField(ProcessID, 'ApprovalSig');
PDF.SetSignProcessPFXFromFile(ProcessID, 'company.pfx', PfxPassword);
PDF.SetSignProcessInfo(ProcessID, 'Invoice approval', 'Berlin', 'billing@example.com');
PDF.SetSignProcessCustomSubFilter(ProcessID, 'ETSI.CAdES.detached');  // PAdES baseline
PDF.SetSignProcessDigestAlgorithm(ProcessID, 2);                      // SHA-256
PDF.SetSignProcessReserveContentsBytes(ProcessID, 8192);              // room for a later timestamp
PDF.EndSignProcessToFile(ProcessID, 'invoice-signed.pdf');
if PDF.GetSignProcessResult(ProcessID) <> 1 then
  Writeln('Sign failed, code ', PDF.GetSignProcessResult(ProcessID));
PDF.ReleaseSignProcess(ProcessID);
var
  Doc: TPDFlibSignDoc;
  Names: TStringList;
  FS: TFileStream;
  I: Integer;
  SourceSize, RangeStart, GapStart, TailStart, TailLen: Int64;
begin
  // Capture the size before Open: the audit object holds a share lock on the file
  FS := TFileStream.Create('invoice-signed.pdf', fmOpenRead or fmShareDenyNone);
  SourceSize := FS.Size;
  FS.Free;
  Doc := TPDFlibSignDoc.Create;
  Names := TStringList.Create;
  try
    if not Doc.Open('invoice-signed.pdf', '', False) then Exit;
    Doc.GetSignatureFieldNames(Names);
    for I := 0 to Names.Count - 1 do
      if Doc.GetSignatureValueObjNum(Names[I]) > 0 then  // > 0 means the field is signed
      begin
        RangeStart := StrToInt64(string(Doc.GetSignatureValueByName(Names[I], 11)));
        GapStart   := StrToInt64(string(Doc.GetSignatureValueByName(Names[I], 12)));
        TailStart  := StrToInt64(string(Doc.GetSignatureValueByName(Names[I], 13)));
        TailLen    := StrToInt64(string(Doc.GetSignatureValueByName(Names[I], 14)));
        if (RangeStart = 0) and (TailStart + TailLen = SourceSize) then
          Writeln(Names[I], ': signature covers the file to EOF')
        else
          Writeln(Names[I], ': earlier revision, or unusual ByteRange layout');
      end;
    Doc.Close;
  finally
    Names.Free;
    Doc.Free;
  end;
end;

Tarkistus ennen julkaisua

Tarkista tulostiedosto samoilla työkaluilla, joita asiakas tai arkisto käyttää. Kirjaa komponenttiversio, testidata, validatorin versio ja havaittu tulos, jotta myöhempi regressio voidaan jäljittää täsmällisesti

Täydentävä tekninen tarkastelu

Tämä laajennettu osio liittyy artikkeliin Tekninen artikkeli: PDFlibPas Compliance and Signing Workbench in Delphi suomeksi ja avaa saman työketjun siltä kannalta, että tiimin pitää voida jäljittää myöhemmin sekä generoinnin, validoinnin että lokituksen päätökset. Linkitetyn sivun englanninkielinen perusartikkeli hreflangin kautta näyttää, miksi pelkkä otsikoiden kääntäminen ei riitä; olennaista on selittää, miksi asiakirja on valmis vasta silloin, kun säännöt, tulos ja tarkastusjäljet on oikeasti sovitettu yhteen

Toteutusta käsittelevissä artikkeleissa on hyödyllistä erottaa suunnittelu ja tarkistus toisistaan. Ensin määritetään tiedostotyyppi, odotettu tulos ja käyttäjän näkemä virhetila, ja sen jälkeen jokainen API-kutsu sidotaan tulokseen, jonka voi toistaa samassa skenaariossa. Tämä pätee sekä PDF- että taulukkolaskentatyöhön: koodiesimerkit säilyvät muuttumattomina, mutta ympäröivän tekstin pitää selittää, miksi komponenttiversio, mallin tunnus, syötedata ja validointitila kannattaa kirjata yhteen

Yhtä tärkeää on säilyttää tuotenimet, API-nimet, tiedostonimet ja literal-arvot täsmälleen kuten englanninkielisessä lähteessä. Se pitää kehityksen, tuen ja laadunvarmistuksen yhteisen viitekehyksen kasassa ja vähentää riskiä siitä, että paikallisesta versiosta tulee vain vapaa parafraasi ilman täsmällistä teknistä sisältöä. Jos artikkelissa on koodia, kommenttien ja tokenien tulee pysyä koskemattomina, koska juuri ne yhdistävät tekstin todelliseen projektiin

Kun sivua luetaan julkaisun jälkeen, siitä kannattaa ajatella jäljitettävää ketjua. Hyvä validointimerkintä kuvaa, mitä testattiin, millä työkalulla tulos arvioitiin, mitkä versiot olivat mukana ja minne todiste onnistumisesta tai epäonnistumisesta on tallennettu. Kun myöhemmin ilmenee regressio, arkistoitu raportti ja siihen liittyvä syötetiedosto ovat paljon arvokkaampia kuin pelkkä muisto siitä, että "se meni silloin läpi"

Tälle lokalisoidulle haaralle pätee siis yksinkertainen sääntö: pidä keskeiset päätökset, tarkistuspisteet ja koodin konteksti yhdessä, jotta artikkeli on hyödyllinen paitsi ensimmäisellä lukukerralla myös myöhemmässä virheenjäljityksessä, auditoinnissa ja versioiden vertailussa. Se on ero lyhyen yhteenvedon ja työasiakirjan välillä, jolla on arvoa vielä useiden julkaisutusten jälkeen.

  • Käytä ensin pieniä toistettavia syötetiedostoja
  • Pidä tuotenimet, API-nimet, tiedostonimet ja literal-arvot muuttumattomina
  • Tallenna komponenttiversio, validatorin tulos ja syötetiedot yhdessä
  • Säilytä koodilohkot ja kommentit täsmälleen kuten lähteessä