PDF-sisäänoton tarkastelutyöpöytä (intake review workbench) on (is a) pieni (small) ohjelma, jolla (with) on yksi (one) tehtävä (job): katso (look at) jokaista (every) tiedostoa ennen (before) kuin (anything) mikään sitä myöhempi prosessi (downstream) saa koskea (allowed to touch it) siihen (it). Tehdäkseen (To do) tuon (that) työn (job) sen on koottava kourallinen ominaisuuksia (capabilities) yhteen vaiheeseen (pass). Se avaa tiedoston (luottamatta siihen), lukee mitä (what) tiedosto (file) väittää (claims about itself) itsestään, etsii sisältöä, joka (will) voi johtaa harhaan (mislead) naiivia (naive extractor) purkajaa (extractor) tai (or) kantaa (carry) hyökkäystä (attack), päättää, onko (there is any) siinä lainkaan puhdasta (extractable) tekstiä (text at all), ja sitten reitittää (routes) asiakirjan jonoon sen perusteella, mitä se (it) löysi (found). Ohita tarkastus, ja (and the) epäonnistumiset (failures) ovat äänettömiä (quiet ones): omistajan salasanalla suojattu (owner-password-encrypted) PDF, joka (that) käärii (wraps) XFA-lomakkeen, purjehtii (sails through) tekstinpoimijan (text extractor as) läpi tyhjinä (empty) merkkijonoina (strings), indeksoidaan (gets indexed as) tyhjänä (blank) asiakirjana, eikä (and) kukaan huomaa, ennen kuin joku (someone) myöhemmässä vaiheessa (downstream) alkaa etsiä (goes looking for) sisältöä, jota (that) ei koskaan luettu. PDFium Component on (is a) lähdekoodilla toimitettava (source-code) VCL/LCL-katseluohjelma (viewer) ja tarkastuskirjasto (inspection library) Delphille, C++Builderille ja Lazarukselle, ja (and it) se (it) paljastaa (exposes) ne introspektiokutsut (introspection calls), joita tämä työpöytä tarvitsee (needs). Alla olevat osiot (sections below) käyvät läpi (walk through), mikä kutsu vastaa mihin (which) kysymykseen, ja kaksi paikkaa (two places), joissa (where the) ilmeinen (obvious) kutsu antaa (gives you) itsevarmasti väärän vastauksen
Viisi (Five) kysymystä (questions), joihin on (to) vastattava (answer) ennen (before a) tiedoston (file is) reititystä (routed)
Riisu (Strip away) ruudukko (grid) ja pikkukuvakaista (thumbnail strip), ja (and) sisäänoton (intake) lajittelu (triage) kutistuu viiteen kysymykseen:
- Voiko tiedostoa (file) ylipäätään avata (open at all), ja millä salasanalla?
- Mikä se (it) väittää olevansa: otsikko (title), tekijä (author), luontipäivä (creation date)?
- Kantaako (Does it carry) se (it) aktiivista tai (or) riskialtista (risky) sisältöä (content) kuten (such as) JavaScriptiä, XFA-lomaketta (form) tai (or) upotettuja (embedded) tiedostoja?
- Onko siinä poimittavissa olevaa (extractable) tekstiä (text), vai onko se skannaus (scan), joka on menossa OCR-käsittelyyn (OCR)?
- Ottaen (Given) kaikki tuo huomioon (all of that), mihin (which) jonoon se (queue gets it) joutuu: suora (straight-through) käsittely (processing), manuaalinen (manual) tarkistus (review) vai (or) karanteeni?
Jokainen kysymys (Each question) kartoittuu (maps onto) yhteen tai kahteen PDFium Component -kutsuun (calls). Kahdella noista (Two of those) kartoituksista (mappings) on teräviä kulmia, jotka (that) selittävät (account for) suurimman osan väärin reititetyistä tiedostoista, joita olen joutunut (had to) virheenjäljittämään (debug) tuotannossa (in production). Asiakirjan (Document) metadata asuu (lives) kahdessa eri paikassa, jotka voivat olla (can) eri mieltä (disagree), eikä (and) salaus (encryption) välttämättä (not necessarily) estä (stop a) asiakirjaa avautumasta (opening)
Avaa halvalla (cheaply): lomakkeen täyttö (form fill off) pois, nolla sivua (pages) renderöitynä (rendered)
Lajittelun (Triage should) tulisi (be the) olla halvin (cheapest) mahdollinen avaus. FormFill := False asettaminen (Setting) ennen (before) Active := True kertoo komponentille, että (to) sen (skip the) tulee (skip the) ohittaa lomakkeentäyttöympäristö (form-fill environment) kokonaan (entirely). Tuo lyhentää latausaikaa (load time), ja (ja mikä on yhtä tärkeää tuntemattoman alkuperän omaaville tiedostoille) estää (prevents) minkään asiakirjatason (document-level) JavaScriptin alustumisen (initializing). Yksikään (None of the) alla (below) käytetyistä (used) tarkastusominaisuuksista ei vaadi (require) sivun renderöintiä, joten lajitteluvaiheen (triage pass) ei koskaan tarvitse tuottaa (produce) ainoatakaan (single) bittikarttaa (bitmap)
procedure InspectIncoming(const IncomingPath: string; var Rec: TIntakeRecord);
var
Pdf: TPdf;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := IncomingPath;
Pdf.FormFill := False; // no form environment, no JavaScript init
Pdf.Active := True; // failure is silent: Active simply stays False
if not Pdf.Active then
begin
Rec.OpenFailed := True; // damaged file or user-password lock
Exit; // the finally block still runs
end;
Rec.PageCount := Pdf.PageCount;
CollectIdentity(Pdf, IncomingPath, Rec);
CollectRiskSignals(Pdf, Rec);
finally
Pdf.Active := False;
Pdf.Free; // never leak the instance on a malformed file
end;
end;
Tarkistus sijoituksen (assignment) jälkeen ei ole (is not) valinnainen, ja se on tarkistus (check) eikä (rather than) poikkeuskäsittelijä (exception handler) syystä (for a reason). Kun moottori (engine) ei pysty lataamaan (load) tiedostoa, komponentti (component) nielaisee (swallows) sisäisen (internal) EPdfError-poikkeuksen ja jättää (leaves) ominaisuuden Active arvoon False sen sijaan (instead of), että se levittäisi sen eteenpäin (propagating it). Koodi (Code), joka odottaa (waits for) poikkeusta (exception), lukee onnellisena PageCount-arvon asiakirjasta, joka ei koskaan auennut. Jos hylkäystyönkulku (rejection workflow) tarvitsee moottorin todellisen virhetekstin (error text), lue tiedosto tavutaulukkoon (byte array) ja (and) kutsu (call the) LoadDocument-ylilatausta, joka ottaa TBytes-taulukon; tuo (that) polku (path) todella nostaa (does raise) EPdfError-poikkeuksen viestin (message) kera, mukaan lukien (including) salasanatapaus (password case). try..finally-lohko (block) ansaitsee yhä (still earns) paikkansa (its place). Sisäänottopalvelut (Intake services) pyörivät valvomattomina viikkoja, eikä (and) mikään myöhempi (no later) poikkeus saa vuotaa (leak the) TPdf-instanssia tai (or) pitää (hold a) lukkoa (lock), johon uudelleenyritys (retry pass) kompastuu (will trip over)
Läpimenosta (Throughput) tulee harvoin pullonkaula (bottleneck). Kun lomakkeen täyttö on pois käytöstä ja renderöintiä ei tehdä, lajitteluavausta hallitsee (is dominated by) I/O, ja (and a) yksittäinen (single) työläinen tarkastaa helposti (comfortably inspects) useita (several) tiedostoja (files) sekunnissa paikalliselta levyltä. Jos sisäänoton tilavuus (intake volume) joskus (ever does) kasvaa yhden työläisen yli (outgrow one worker), osioi työ tiedostoittain, älä (rather than by) tarkistuksittain (check). Nämä viisi kysymystä jakavat (share) yhden avauksen, ja (and) niiden (them) jakaminen (splitting them) prosesseille moninkertaistaisi kalleimman (most expensive) vaiheen (step) sen kuolettamisen (amortizing it) sijaan
Metadata asuu kahdessa paikassa, ja (and) ne ovat eri mieltä (disagree)
ISO 32000-1 määrittelee asiakirjan (document) metadatallle kaksi (two) kotia (homes): asiakirjan (document) Info-sanakirja (information dictionary) (kohta 14.3.3) ja (and an) luetteloon (catalog) liitetty (attached) XMP-paketti (XMP packet) (kohta 14.3.2). Title, Author, Subject ja CreationDate -ominaisuudet (properties) lukevat (read the) Info-sanakirjaa, ja MetaText[] mitä (for any) tahansa (other) muuta (other) avainta (key) ja (and) DecodeDate jäsentää (to parse the) D:YYYYMMDD... -päivämäärämerkkijonon. Juju on siinä (The catch is), että modernit tuottajat (producers) kirjoittavat enenevässä määrin (increasingly write) vain (only) XMP:tä, suuntaus (direction), jonka (ISO 32000-2) tekee viralliseksi hylkäämällä (deprecating) useimmat (most) Info-sanakirjan avaimet PDF 2.0:ssa. Oire (symptom) sisäänottotyökalussa (intake tool) on (is) konkreettinen. Työpöytäsi näyttää tyhjän (empty) otsikon, kun taas Adobe Acrobat näyttää sellaisen (displays one), koska (because) Acrobat turvautui (fell back to) dc:title-elementtiin XMP-paketin sisällä (inside the XMP packet), mihin Info-sanakirjan (Info-dictionary) ominaisuudet (properties) eivät koskaan (never) koske (touch)
procedure CollectIdentity(Pdf: TPdf; const FilePath: string;
var Rec: TIntakeRecord);
begin
Rec.Title := Pdf.Title; // Info dictionary value
Rec.Author := Pdf.Author;
Rec.CreatedAt := Pdf.CreationDate; // raw PDF date string ("D:2026...")
// An empty Info title does not mean the document is untitled. The
// component does not expose the XMP packet, so probe the raw file
// bytes for the dc:title element before trusting the blank.
if (Rec.Title = '') and FileContainsText(FilePath, 'dc:title') then
Include(Rec.Flags, ifTitleInXmpOnly);
end;
Jopa (Even the) yllä oleva karkea (crude) alimerkkijonon tutkinta ansaitsee paikkansa (earns its keep): "metadataa on läsnä (metadata present), mutta (but) ei siellä, mistä (where) vanhat (legacy) työkalut etsivät", on reitityksen kannalta olennainen (routing-relevant) fakta mille (for any) tahansa arkistoputkelle (archive pipeline), joka indeksoi (indexes on) otsikon tai tekijän perusteella. Jos myöhempi (downstream) indeksisi lukee vain (reads only the) Info-sanakirjaa, tällä tavoin (this way) merkityistä (flagged) tiedostoista tulee hiljaa (silently) hakemattomia (unsearchable)
Salatut (Encrypted) tiedostot, jotka (that) avautuvat joka tapauksessa (anyway)
Salatun asiakirjan avautuminen (An encrypted document does not necessarily) ei (not) välttämättä epäonnistu. Standardi turvallisuuskäsittelijä (security handler) (ISO 32000-1 kohta 7.6.3) erottaa (distinguishes a) käyttäjän (user) salasanan (password), jota tarvitaan (required) asiakirjan (document) avaamiseen, omistajan salasanasta, joka (that) vain rajaa (merely gates) oikeuksia (permissions), kuten tulostamista ja kopiointia (copying). Suuri osa (A large share of) "suojatuista" ("protected") liikeasiakirjoista on (are) salattu omistajan salasanalla ja (and an) tyhjällä käyttäjän salasanalla. Ne (They) avautuvat ilman (without) kehoteikkunaa (prompting), purkautuvat täysin (decrypt fully), ja (and) luottavat siihen, että katseluohjelmat (viewers) suostuvat vapaaehtoisesti (volunteering) kunnioittamaan oikeuslippuja (permission flags). Se (That) on käytäntö (policy), ei (not) suojaus (protection), ja sisäänoton tilojesi (intake states) tulisi heijastaa (reflect) tuota eroa
Salauksen (encryption) havaitseminen onnistuneen (successful) avauksen (open) jälkeen vaatii yhden moottorikutsun (engine call) sekä vararatkaisun (fallback). FPDF_GetSecurityHandlerRevision(Pdf.Document) palauttaa arvon -1 suojaamattomille (unprotected) tiedostoille ja käsittelijän revisionumeron (handler revision) muutoin (otherwise), ja Pdf.Permissions, joka (returning) palauttaa (anything) mitä tahansa muuta kuin kaikki-bitit-asetettu $FFFFFFFF-maskin, on vahvistava (corroborating) signaali. Aidosti käyttäjän-salasanalla-lukituille tiedostoille (genuinely user-password-locked files) sijoita Password ennen (before) Active := True -arvon asettamista (setting); jos (if the) avaus (open) epäonnistuu silti, reititä tiedosto (route the file to a) estettyyn (blocked) tilaan (state), joka (that) pyytää (requests) kirjautumistietoja lähettäjältä turvallista (secure) kanavaa (channel) pitkin sokkona (blindly) tapahtuvan uudelleenyrityksen (retrying) sijaan. Ja (And) vastusta (resist the) kiusausta (temptation to) käsitellä "salattua" ("encrypted" as an) automaattisena (automatic) karanteenina. Useimmilla asiakirjaintensiivisillä (document-heavy) aloilla salatut, mutta (encrypted-but-openable) avattavissa olevat (files are the) tiedostot ovat (are the) normaali (normal) tapaus (case), ei epäilyttävä (suspicious one)
Aktiivinen sisältö: JavaScript, XFA ja upotetut (embedded) tiedostot
Kolmen (Three) havainnon (findings) pitäisi (should) aina (always) saavuttaa (reach the) reitityspäätös (routing decision). Ensinnäkin JavaScript: tapahtuma OnUnsupportedFeature raportoi (reports) rakenteelliset (structural) ominaisuudet (features), kuten XFA- tai 3D-sisällön sitä mukaa kun moottori kohtaa (encounters) ne, mutta se (it) ei (does not) havaitse (detect) JavaScriptiä. Tarkista (Check) sen sijaan (instead) JavaScriptActionCount ja (and) käsittele nollasta (non-zero) poikkeavaa tulosta aktiivisena (active) sisältönä (content). Toiseksi XFA: kun FormType palauttaa ftXfaFull, näkyvät sivut (visible pages) ovat (are) usein vähän muuta kuin XFA-mallin renderöinti (rendering), ja tavanomainen tekstinpoiminta (conventional text extraction) näkee vakiotekstiä (boilerplate) täytettyjen (filled) arvojen sijaan. Kolmanneksi (Third) liitteet (attachments): PDF on (is a) säiliömuoto (container format), ja AttachmentCount kertoo sinulle, kantaako tämä yksilö (one is carrying) matkustajia
procedure CollectRiskSignals(Pdf: TPdf; var Rec: TIntakeRecord);
var
i, PageNo: Integer;
Ext: string;
begin
Rec.IsEncrypted := Assigned(FPDF_GetSecurityHandlerRevision) and
(FPDF_GetSecurityHandlerRevision(Pdf.Document) <> -1);
Rec.HasForms := Pdf.FormType <> ftNone;
Rec.IsXfa := Pdf.FormType = ftXfaFull;
Rec.HasJavaScript := Pdf.JavaScriptActionCount > 0;
// AnnotationCount is a per-page property; walk the pages to total
// it. Loading a page object renders nothing, so this stays cheap.
Rec.Annotations := 0;
for PageNo := 1 to Pdf.PageCount do
begin
Pdf.PageNumber := PageNo;
Inc(Rec.Annotations, Pdf.AnnotationCount);
end;
Rec.Attachments := Pdf.AttachmentCount;
for i := 0 to Rec.Attachments - 1 do
begin
Ext := LowerCase(ExtractFileExt(string(Pdf.AttachmentName[i])));
if (Ext = '.exe') or (Ext = '.js') or (Ext = '.vbs') or (Ext = '.dll') then
Include(Rec.Flags, ifDangerousAttachment);
end;
end;
Kaksi yksityiskohtaa tuossa (that) silmukassa (loop) ansaitsee (deserve) huomiota (attention). Liitteen nimi tulee asiakirjan sisältä, joten (so) älä koskaan käytä sitä uudelleen (reuse it) tulostepolkuna (output path) puhdistamatta (without sanitizing) sitä ensin (first); upotettu nimi kuten (like) ..\..\start.exe on (is a) polun läpikulkuhyökkäys (path traversal) odottamassa (waiting for a) huolimatonta (careless) tallennuskutsua (save call). Ja (And an) laajennusten (extension) estolista (blocklist) on ansalanka (tripwire), ei takuu (not a guarantee). Sen tehtävä (Its job is) on pakottaa (to force a) ihmisen (human) päätös (decision), ei sertifioida tiedostoa (file) puhtaaksi
Signaalien kääntäminen reititystiloiksi
Toimiva tilamalli (workable state model) tarvitsee (needs) vähemmän (fewer) tiloja (states) kuin (than) useimmat (most) tiimit (teams) odottavat (expect): valmis (ready) (ei (no) estäjiä (blockers), teksti läsnä (present)), tarkistus (review) (avaus (open) onnistui, mutta jokin vaatii (needs) silmiä, kuten XFA-lomake (form), JavaScript, tyhjä (empty) tekstikerros (text layer), tai otsikko (title) vain XMP:ssä), estetty (blocked) (käyttäjän (user) salasana vaaditaan), ja vaurioitunut (damaged) (avaus epäonnistui). Tallenna (Record the) todisteet (evidence) tilan rinnalle (alongside the state). Tiedoston hajautusarvo (file hash), sivumäärä (page count), tarkat (the exact) liput, ja moottorin virheviesti vaurioituneille tiedostoille ovat kaikki tärkeitä, koska henkilö, joka kyseenalaistaa reitityspäätöksen, tekee sen viikkoja myöhemmin tiedostolle, joka on saattanut sen jälkeen korvautua tai muuttua (modified)
Kun operaattorin (operator does) täytyy (need to) katsoa (look at a) karanteeniin (quarantined) asetettua tiedostoa (file), älä anna sitä (hand it to the) oletusarvoiselle kuoren katseluohjelmalle (default shell viewer). Renderöi se paksunnetun paneelin sisällä (inside a hardened pane), josta skriptaus (scripting) ja linkkien käsittely on poistettu käytöstä, kuten kuvattiin artikkelissa pdfium-component-secure-pdf-preview-delphi.html (turvallisen PDF-esikatselupinnan rakentaminen Delphissä, building a secure PDF preview surface in Delphi). Ja jos sisäänotto syöttää (feeds an) arkistoa (archive), jolla on (with) vaatimustenmukaisuusvaatimuksia (conformance requirements), lajitteluvaihe on (is the) luonnollinen paikka aikatauluttaa (schedule a) syvempi tarkistus; artikkeli pdfium-component-preflight-report-cli-batch.html (eräajona tehtävä ennakkotarkastus PDF/A- ja PDF/UA-profiileja vastaan, batch preflight validation against PDF/A and PDF/UA profiles) jatkaa tasan (exactly) siitä, mihin (where) tämä (this) tarkastus (inspection) päättyy (stops)
Komponentin (component's) tuotesivu (product page) kattaa (covers) lisensoinnin (licensing), täyden (full) tarkastus-API:n (inspection API) ja (and the) mukana tulevat (bundled) demot (demos), mukaan lukien sisääntulotyylisen asiakirjan tarkastajan: PDFium Component