Ένα PDF που φτάνει σε ένα όριο παραγωγής — μια ουρά εκτύπωσης, ένα αρχείο, μια πύλη μεταφόρτωσης (upload) πελατών — θα πρέπει να ελέγχεται (audited) πριν οτιδήποτε το αποδώσει (renders). Το αρχείο μπορεί να φέρει μια ενέργεια Εκκίνησης (Launch) ρυθμισμένη να ξεκινήσει ένα εξωτερικό πρόγραμμα, εικόνες πολύ τραχιές (coarse) για να επιβιώσουν κατά την εκτύπωση, ένα λεξικό κρυπτογράφησης που απαγορεύει ακριβώς την ίδια την εργασία εκτύπωσης για την οποία υποβλήθηκε, ή μια ετικέτα PDF/A στην οποία δεν ανταποκρίνεται. Η επιθεώρηση ενός εγγράφου έναντι κανόνων όπως αυτοί προτού εισέλθει σε μια ροή εργασίας (workflow) ονομάζεται προέλεγχος (preflighting), και το C API του PDFium δίνει στο Delphi ό,τι χρειάζεται για την απευθείας υλοποίηση αυτών των ελέγχων, χωρίς την απόδοση (rendering) ούτε μίας σελίδας
Αυτό το άρθρο κατασκευάζει τους ίδιους τους ελέγχους: τέσσερις κλάσεις ελέγχου (audit classes), καθεμία εκ των οποίων αποτελεί μια μικρή ρουτίνα που προσαρτά ευρήματα σε μια κοινή λίστα αποτελεσμάτων. Διαδραστικά στοιχεία, μετρικές πόρων, κατάσταση ασφαλείας, και δείκτες προτύπων, όλα αποκτούν λειτουργικό κώδικα, συμπεριλαμβανομένης της αριθμητικής (arithmetic). Αν αυτό που χρειάζεστε είναι ο μηχανισμός γύρω από τους ελέγχους — βρόχοι (loops) φακέλων για μαζική (batch) επεξεργασία, αρχεία αναφορών σε JSON και HTML, απομόνωση ανά αρχείο — το PDFium Component παρέχει μια έτοιμη μηχανή προελέγχου, και το άρθρο για το batch preflight CLI καλύπτει αυτή την υποδομή. Τα δύο μέρη μοιράζονται εσκεμμένα ένα κοινό λεξιλόγιο κωδικών εξόδου (exit-code), έτσι ώστε ένας ελεγκτής (auditor) γραμμένος εδώ να ταιριάζει κατευθείαν κάτω από αυτόν τον οδηγό παρτίδων (batch driver)
Η εγγραφή εύρεσης (finding record) και το συμβόλαιο κωδικού εξόδου (exit-code)
Κάθε έλεγχος γράφει σε έναν επίπεδο τύπο εγγραφής (flat record type), επειδή η εναλλακτική λύση, δηλαδή κάθε έλεγχος να εκτυπώνει το δικό του κείμενο, δεν μπορεί να μετρηθεί, να φιλτραριστεί ή να οριστεί με κατώφλι (thresholded) εκ των υστέρων. Τέσσερα πεδία είναι αρκετά
uses
System.SysUtils, System.Math, System.IOUtils,
System.Generics.Collections, pdfium_lib;
type
TFindingSeverity = (fsInfo, fsWarning, fsError);
TPreflightFinding = record
Severity: TFindingSeverity;
Code: string; // stable machine key, e.g. 'ACT-LAUNCH'
Page: Integer; // 1-based; 0 means document level
Message: string; // for humans; free to reword between releases
end;
TFindings = TList<TPreflightFinding>;
procedure Add(Findings: TFindings; Severity: TFindingSeverity;
const Code: string; Page: Integer; const Msg: string);
var
F: TPreflightFinding;
begin
F.Severity := Severity;
F.Code := Code;
F.Page := Page;
F.Message := Msg;
Findings.Add(F);
end;
Τα εργαλεία στα επόμενα στάδια (downstream tooling) βασίζονται στο Code, ποτέ στο κείμενο Message, το οποίο είναι ελεύθερο να αλλάξει. Ο κωδικός εξόδου της διεργασίας ακολουθεί το ίδιο συμβόλαιο τριών τιμών με το άρθρο των batch: 0 σημαίνει ότι το αρχείο δεν παρήγαγε κανένα εύρημα, 1 σημαίνει ότι υπάρχουν ευρήματα, και 2 σημαίνει ότι ο ίδιος ο έλεγχος (audit) δεν μπόρεσε να εκτελεστεί επειδή το αρχείο απέτυχε να αναλυθεί (failed to parse) ή απαιτεί κωδικό πρόσβασης. Η διατήρηση του κωδικού 2 ως ξεχωριστού έχει σημασία. Ένας φάκελος με κατεστραμμένες σαρώσεις οφείλεται σε σπασμένο σαρωτή νωρίτερα στη διαδικασία (upstream), όχι σε ξαφνική κατάρρευση συμμόρφωσης (compliance collapse), και η συνένωση των δύο οδηγεί κάποιον να κυνηγάει το λάθος πρόβλημα
Διαδραστικά στοιχεία: scripts, στόχοι εκκίνησης (launch targets), εξωτερικοί σύνδεσμοι
Το PDFium ταξινομεί κάθε ενέργεια που βρίσκει με έναν ακέραιο τύπο, και οι σταθερές (constants) από το fpdf_doc.h αξίζει να καθοριστούν με ακρίβεια, επειδή αντιγραμμένες λάθος τιμές καθιστούν έναν σαρωτή (scanner) αθόρυβα τυφλό. Η πραγματική απαρίθμηση (enumeration) είναι PDFACTION_UNSUPPORTED = 0, PDFACTION_GOTO = 1, PDFACTION_REMOTEGOTO = 2, PDFACTION_URI = 3, PDFACTION_LAUNCH = 4, και PDFACTION_EMBEDDEDGOTO = 5. Προσέξτε τι απουσιάζει: δεν υπάρχει μέλος για τη JavaScript. Τα σενάρια επιπέδου εγγράφου (Document-level scripts) δεν είναι ενέργειες συνδέσμων και δεν εμφανίζονται ποτέ μέσω της FPDFAction_GetType· απαριθμούνται από μια ξεχωριστή οικογένεια κλήσεων. Ένας ελεγκτής που δοκιμάζει (tests) τύπους ενεργειών έναντι μιας φανταστικής σταθεράς JavaScript, μεταγλωττίζεται (compiles), τρέχει και δεν βρίσκει τίποτα, ποτέ
const
PDFACTION_GOTO = 1; // in-document jump: harmless
PDFACTION_REMOTEGOTO = 2; // jump into another local file
PDFACTION_URI = 3; // opens an external URL
PDFACTION_LAUNCH = 4; // starts an external program
PDFACTION_EMBEDDEDGOTO = 5; // jump into an embedded file
function ActionTarget(Doc: FPDF_DOCUMENT; Action: FPDF_ACTION;
AType: ULONG): string;
var
Buf: array[0..2047] of AnsiChar;
begin
FillChar(Buf, SizeOf(Buf), 0);
if AType = PDFACTION_URI then
FPDFAction_GetURIPath(Doc, Action, @Buf, SizeOf(Buf))
else
FPDFAction_GetFilePath(Action, @Buf, SizeOf(Buf));
Result := string(UTF8String(PAnsiChar(@Buf)));
end;
procedure AuditPageActions(Doc: FPDF_DOCUMENT; Page: FPDF_PAGE;
PageNo: Integer; Findings: TFindings);
var
StartPos: Integer;
Link: FPDF_LINK;
Action: FPDF_ACTION;
AType: ULONG;
begin
StartPos := 0;
while FPDFLink_Enumerate(Page, @StartPos, @Link) <> 0 do
begin
Action := FPDFLink_GetAction(Link);
if Action = nil then
Continue; // destination-only link, nothing to flag
AType := FPDFAction_GetType(Action);
case AType of
PDFACTION_LAUNCH:
Add(Findings, fsError, 'ACT-LAUNCH', PageNo,
'Launch action targets "' + ActionTarget(Doc, Action, AType) + '"');
PDFACTION_URI:
Add(Findings, fsWarning, 'ACT-URI', PageNo,
'link opens ' + ActionTarget(Doc, Action, AType));
PDFACTION_REMOTEGOTO, PDFACTION_EMBEDDEDGOTO:
Add(Findings, fsWarning, 'ACT-XFILE', PageNo,
'cross-file destination "' + ActionTarget(Doc, Action, AType) + '"');
end; // PDFACTION_GOTO stays silent by design
end;
end;
procedure AuditDocumentBehaviors(Doc: FPDF_DOCUMENT; Findings: TFindings);
var
N: Integer;
begin
N := FPDFDoc_GetJavaScriptActionCount(Doc);
if N > 0 then
Add(Findings, fsError, 'JS-DOC', 0,
Format('%d document-level JavaScript action(s) run on open', [N]));
N := FPDFDoc_GetAttachmentCount(Doc);
if N > 0 then
Add(Findings, fsWarning, 'ATT-EMB', 0,
Format('%d embedded file attachment(s)', [N]));
end;
Ο διαχωρισμός της σοβαρότητας (severity) κωδικοποιεί πολιτική (policy). Μια ενέργεια Launch (Εκκίνηση) είναι ένα σφάλμα (error) επειδή η έναρξη ενός αυθαίρετου προγράμματος είναι το πιο επικίνδυνο πράγμα που μπορεί να κάνει ένα κλικ σε ένα PDF, και κανένα τιμολόγιο δεν το χρειάζεται. Τα εξωτερικά URI είναι προειδοποιήσεις (warnings): συνηθισμένα σε νόμιμα έγγραφα, αλλά ένας ελεγκτής (reviewer) θα πρέπει να βλέπει τον στόχο χωρίς να κάνει κλικ, αφού το ορατό κείμενο του συνδέσμου και ο πραγματικός προορισμός δεν χρειάζεται να συμφωνούν. Τα άλματα (jumps) GoTo εντός του εγγράφου είναι δομή (structure), όχι συμπεριφορά, και μένουν εντελώς εκτός αναφοράς — ένας προέλεγχος (preflight) που φωνάζει "λύκος" σε κάθε καταχώρηση του πίνακα περιεχομένων εκπαιδεύει τους ανθρώπους να τον αγνοούν. Για την ανάγνωση του περιεχομένου των σεναρίων (script bodies) πίσω από τον αριθμό JavaScript, και για επίπεδα υπογραφών MDP και ανίχνευση XFA, το άρθρο για τον έλεγχο κινδύνων ασφαλείας διατρέχει την ίδια επιφάνεια (surface) μέσω του object wrapper του στοιχείου
Μετρικές πόρων: αποτελεσματικό DPI εικόνας
Μια εικόνα μέσα σε ένα PDF δεν έχει δικό της DPI. Έχει pixel, και η σελίδα τοποθετεί αυτά τα pixel σε ένα ορθογώνιο μετρημένο σε στιγμές (points), όπου 72 στιγμές αποτελούν μια ίντσα. Η ανάλυση (resolution) υπάρχει μόνο ως ο λόγος των δύο, γι' αυτό και η ίδια φωτογραφία 600 επί 400 είναι εξαιρετικά ευκρινής ως μικρογραφία (thumbnail) και ένα θολό χάος (blurry mess) ως κεντρική εικόνα (hero) σε όλη τη σελίδα. Επομένως, ο έλεγχος (audit) χρειάζεται και τους δύο αριθμούς για κάθε εικόνα: τις διαστάσεις των pixel-πηγή από τα μεταδεδομένα της εικόνας, και το τοποθετημένο ορθογώνιο από τα όρια (bounds) του αντικειμένου
procedure AuditPageImages(Page: FPDF_PAGE; PageNo: Integer;
Findings: TFindings);
var
I, ObjCount: Integer;
Obj: FPDF_PAGEOBJECT;
Meta: FPDF_IMAGEOBJ_METADATA;
L, B, R, T: Single;
WidthPt, HeightPt, DpiX, DpiY, EffDpi: Double;
begin
ObjCount := FPDFPage_CountObjects(Page);
for I := 0 to ObjCount - 1 do
begin
Obj := FPDFPage_GetObject(Page, I);
if FPDFPageObj_GetType(Obj) <> FPDF_PAGEOBJ_IMAGE then
Continue;
if FPDFImageObj_GetImageMetadata(Obj, Page, @Meta) = 0 then
Continue;
if FPDFPageObj_GetBounds(Obj, @L, @B, @R, @T) = 0 then
Continue;
WidthPt := R - L; // placed size on the page, in points
HeightPt := T - B;
if (WidthPt <= 0) or (HeightPt <= 0) or
(Meta.Width = 0) or (Meta.Height = 0) then
Continue;
// 72 points = 1 inch, so placed inches = points / 72, and
// effective DPI = source pixels / placed inches.
DpiX := Meta.Width / (WidthPt / 72.0);
DpiY := Meta.Height / (HeightPt / 72.0);
EffDpi := Min(DpiX, DpiY); // the worse axis decides print quality
if EffDpi < 150.0 then
Add(Findings, fsWarning, 'IMG-LOWRES', PageNo,
Format('image %dx%d px placed at %.1fx%.1f pt = %.0f DPI effective',
[Meta.Width, Meta.Height, WidthPt, HeightPt, EffDpi]))
else if EffDpi > 600.0 then
Add(Findings, fsInfo, 'IMG-BLOAT', PageNo,
Format('image is %.0f DPI at placed size; resampling would ' +
'shrink the file with no visible loss', [EffDpi]));
end;
end;
Τα κατώφλια (thresholds) αποτελούν πολιτική (policy), όχι φυσική: το 150 DPI είναι ένα πάτωμα κάτω από το οποίο η εκτύπωση γραφείου αρχίζει να εμφανίζει ορατά pixel (pixelates), το 300 είναι ο συνηθισμένος εμπορικός στόχος, και οτιδήποτε πάνω από 600 δεν αγοράζει καμία ορατή ποιότητα, ενώ διογκώνει το μέγεθος του αρχείου, γι' αυτό και αναφέρεται ως ενημερωτική διόγκωση (informational bloat) και όχι ως ελάττωμα. Μια ειλικρινής προειδοποίηση: το FPDFPageObj_GetBounds επιστρέφει το πλαίσιο που είναι ευθυγραμμισμένο με τους άξονες (axis-aligned box), επομένως, για μια εικόνα τοποθετημένη με περιστροφή (rotation), η υπολογισμένη τιμή υποεκτιμά (underestimates) την πραγματική πυκνότητα. Η δομή (struct) FPDF_IMAGEOBJ_METADATA φέρει επίσης τα πεδία horizontal_dpi και vertical_dpi τα οποία το PDFium παράγει από την πλήρη μήτρα μετασχηματισμού (transform matrix), και η σύγκριση των δύο αποτελεσμάτων είναι ένας φθηνός τρόπος για να εντοπίσετε περιστραμμένες (rotated) τοποθετήσεις. Η ίδια αριθμητική από στιγμές σε pixel (points-to-pixels) οδηγεί την απόδοση (rendering) στην αντίθετη κατεύθυνση, η οποία καλύπτεται στο άρθρο για την εξαγωγή JPEG
Κατάσταση ασφαλείας: κρυπτογράφηση και bits δικαιωμάτων
Η κρυπτογράφηση (encryption) του PDF ορίζει δύο κωδικούς πρόσβασης (passwords) με διαφορετικές εργασίες. Ο κωδικός πρόσβασης χρήστη ελέγχει την αποκρυπτογράφηση (decryption): χωρίς αυτόν, το αρχείο δεν θα ανοίξει καθόλου, και η FPDF_LoadDocument επιστρέφει nil με την FPDF_GetLastError να αναφέρει FPDF_ERR_PASSWORD. Ο κωδικός πρόσβασης κατόχου (owner password) ελέγχει τα δικαιώματα (permissions): ένα αρχείο που προστατεύεται μόνο με κωδικό πρόσβασης κατόχου ανοίγει χωρίς διαπιστευτήρια, αλλά φέρει bits περιορισμού τα οποία ένας συμμορφούμενος αναγνώστης (conforming reader) πρέπει να τιμήσει. Επομένως, η ίδια η απόπειρα φόρτωσης είναι ο πρώτος ανιχνευτής ασφαλείας, και η διάκριση αυτή αποφασίζει τον κωδικό εξόδου — ένα αρχείο με κωδικό πρόσβασης χρήστη δεν μπορεί να ελεγχθεί (unauditable, κωδικός 2), ενώ ένα αρχείο με κωδικό πρόσβασης κατόχου ελέγχεται κανονικά (audits normally) και απλώς συσσωρεύει ευρήματα
const
FPDF_ERR_PASSWORD = 4;
function AuditSecurity(const FileName: string;
Findings: TFindings): FPDF_DOCUMENT;
var
Perms: ULONG;
Revision: Integer;
begin
Result := FPDF_LoadDocument(PAnsiChar(AnsiString(FileName)), nil);
if Result = nil then
begin
if FPDF_GetLastError() = FPDF_ERR_PASSWORD then
Add(Findings, fsError, 'SEC-USERPW', 0,
'user (open) password required; audit cannot proceed')
else
Add(Findings, fsError, 'DOC-BROKEN', 0, 'file failed to parse');
Exit;
end;
Revision := FPDF_GetSecurityHandlerRevision(Result);
if Revision >= 0 then // -1 means the file is not encrypted
begin
// Opened with an empty password yet encrypted: owner-password-only.
// Anyone may read it, but the permission bits restrict what a
// conforming reader lets them do. Unencrypted files report all
// bits set, which is why the revision gate comes first.
Perms := FPDF_GetDocPermissions(Result);
Add(Findings, fsInfo, 'SEC-ENC', 0,
Format('encrypted, security handler revision %d', [Revision]));
if (Perms and 4) = 0 then // bit 3: print
Add(Findings, fsWarning, 'SEC-NOPRINT', 0,
'printing is not permitted');
if (Perms and 16) = 0 then // bit 5: copy / extract content
Add(Findings, fsInfo, 'SEC-NOCOPY', 0,
'content extraction is not permitted');
if (Perms and 2048) = 0 then // bit 12: high-resolution print
Add(Findings, fsWarning, 'SEC-LOWPRINT', 0,
'only low-resolution printing is permitted');
end;
end;
Οι μάσκες (masks) προέρχονται από τον Πίνακα 22 του ISO 32000-1, ο οποίος αριθμεί τα bits ξεκινώντας από το 1: το bit 3 της τιμής /P είναι η μάσκα 4, το bit 5 είναι το 16, το bit 12 είναι το 2048. Το εάν έχει σημασία ένα δεδομένο εύρημα είναι μια απόφαση δρομολόγησης (routing decision). Ένα γραφείο εκτυπώσεων (print bureau) θα έπρεπε να αναπηδήσει (bounce) ένα αρχείο SEC-NOPRINT κατά την παραλαβή (intake), όπου αυτός που το υποβάλλει λαμβάνει ένα ξεκάθαρο μήνυμα, και όχι στο RIP (Raster Image Processor) τρεις ώρες πριν από μια προθεσμία. Ένα αρχείο θα πρέπει να αντιμετωπίζει το SEC-ENC καθεαυτό ως αποκλεισμό (blocker), δεδομένου ότι η κρυπτογράφηση και η μακροπρόθεσμη διατήρηση (long-term preservation) δεν συνδυάζονται — ένα σημείο που ο έλεγχος προτύπων (standards check) πρόκειται να κάνει επίσημα
Δείκτες προτύπων: ανάγνωση ενός ισχυρισμού PDF/A
Ένα αρχείο δηλώνει συμμόρφωση PDF/A (PDF/A conformance) στο πακέτο μεταδεδομένων XMP, μέσω της ιδιότητας (property) pdfaid:part (1 έως 4) και της pdfaid:conformance (το γράμμα επιπέδου, όπως b για οπτική πιστότητα ή a για πλήρη δομική προσθήκη ετικετών). Το C API του PDFium δεν προσφέρει προσβάτη (accessor) για το XMP· η κλήση FPDF_GetMetaText διαβάζει μόνο το λεξικό πληροφοριών (Info dictionary), όπου δεν κατοικεί αυτή η αναγνώριση. Η διέξοδος ασφαλείας (escape hatch) είναι ένας κανόνας στο ίδιο το πρότυπο: το ISO 19005 απαιτεί η ροή (stream) των μεταδεδομένων XMP να αποθηκεύεται ασυμπίεστη, ακριβώς για να μπορούν τα εργαλεία να τη βρουν χωρίς έναν πλήρη αναλυτή (parser) PDF. Η σάρωση των ωμών (raw) bytes αποτελεί επομένως έναν νόμιμο ανιχνευτή ισχυρισμών — και ένα αρχείο του οποίου ο ισχυρισμός (claim) κρύβεται μέσα σε μια συμπιεσμένη (compressed) ροή έχει ήδη παραβιάσει το πρότυπο που ισχυρίζεται
function PdfAClaim(const FileName: string): string;
var
Bytes: TBytes;
S: RawByteString;
P, Limit: Integer;
begin
Result := ''; // empty = no PDF/A claim present
Bytes := TFile.ReadAllBytes(FileName);
if Length(Bytes) = 0 then
Exit;
SetString(S, PAnsiChar(@Bytes[0]), Length(Bytes));
P := Pos('pdfaid:part', S); // XMP identification schema
if P = 0 then
Exit;
// Handles both <pdfaid:part>2</pdfaid:part> and pdfaid:part="2":
// take the first digit after the property name.
Limit := Min(P + 32, Length(S));
Inc(P, Length('pdfaid:part'));
while (P <= Limit) and not (S[P] in ['1'..'4']) do
Inc(P);
if P <= Limit then
Result := 'PDF/A-' + Char(S[P]);
end;
Το εύρημα που αυτό παράγει είναι εσκεμμένα ενημερωτικό (informational), επειδή ένας ισχυρισμός (claim) είναι μια δήλωση, όχι μια ιδιότητα του αρχείου. Η καταχώρηση XMP είναι μια γραμμή XML που κάθε παραγωγός (producer) μπορεί να γράψει, συμπεριλαμβανομένου ενός χαλασμένου· η συμμόρφωση (conformance) σημαίνει ότι το αρχείο ικανοποιεί στην πραγματικότητα εκατοντάδες κανόνες σχετικά με ενσωματωμένες γραμματοσειρές, χρώματα ανεξάρτητα από τη συσκευή, και απαγορευμένα χαρακτηριστικά. Η ανίχνευση του ισχυρισμού σας λέει απλώς ποια αρχεία να δρομολογήσετε (route) για πραγματική επικύρωση (validation), και τίποτα περισσότερο. Η ενσωματωμένη μηχανή προελέγχου του στοιχείου εκτελεί αυτή την επικύρωση σε προφίλ PDF/A, PDF/UA, και PDF/X, και το άρθρο για το batch CLI δείχνει πώς να την ενσωματώσετε σε μια γραμμή επεξεργασίας (pipeline) με αναφορές που μπορεί να ανοίξει αργότερα ένας ελεγκτής (auditor)
Μια εκτέλεση σε ένα προβληματικό αρχείο
Ο οδηγός (driver) συνδέει τους ελέγχους μαζί: την ασφάλεια (security) πρώτα, διότι αυτή αποφασίζει αν ο έλεγχος (audit) θα τρέξει καθόλου, έπειτα τις συμπεριφορές (behaviors) επιπέδου εγγράφου και τον ισχυρισμό προτύπων (standards claim), στη συνέχεια έναν βρόχο (loop) σελίδων για ενέργειες και εικόνες
function AuditFile(const FileName: string; Findings: TFindings): Integer;
var
Doc: FPDF_DOCUMENT;
Page: FPDF_PAGE;
I: Integer;
Claim: string;
begin
Doc := AuditSecurity(FileName, Findings);
if Doc = nil then
Exit(2); // audit failure, not a verdict
try
AuditDocumentBehaviors(Doc, Findings);
Claim := PdfAClaim(FileName);
if Claim <> '' then
Add(Findings, fsInfo, 'STD-PDFA', 0,
Claim + ' conformance claimed (declaration only, not validated)');
for I := 0 to FPDF_GetPageCount(Doc) - 1 do
begin
Page := FPDF_LoadPage(Doc, I);
if Page = nil then
begin
Add(Findings, fsError, 'PAGE-BROKEN', I + 1, 'page failed to parse');
Continue;
end;
try
AuditPageActions(Doc, Page, I + 1, Findings);
AuditPageImages(Page, I + 1, Findings);
finally
FPDF_ClosePage(Page);
end;
end;
finally
FPDF_CloseDocument(Doc);
end;
if Findings.Count > 0 then
Result := 1
else
Result := 0;
end;
Σε ένα φυλλάδιο που επέστρεψε από ένα εξωτερικό πρακτορείο (outside agency), η έξοδος (output) φαίνεται κάπως έτσι
> preflight_audit brochure_final.pdf
brochure_final.pdf: 5 finding(s)
[ERROR] ACT-LAUNCH page 3 Launch action targets "..\tools\setup.exe"
[ERROR] JS-DOC doc 2 document-level JavaScript action(s) run on open
[WARNING] IMG-LOWRES page 7 image 412x287 px placed at 396.0x275.8 pt = 75 DPI effective
[WARNING] SEC-NOPRINT doc printing is not permitted
[INFO] STD-PDFA doc PDF/A-2 conformance claimed (declaration only, not validated)
exit code 1
Κάθε γραμμή είναι αξιοποιήσιμη (actionable) από μόνη της, αλλά ο συνδυασμός είναι η πραγματική ετυμηγορία (verdict). Αυτό το αρχείο ισχυρίζεται (claims) PDF/A-2, ενώ φέρει ένα λεξικό κρυπτογράφησης (encryption dictionary) και ζωντανή JavaScript, και το PDF/A απαγορεύει ρητά και τα δύο — άρα ο ισχυρισμός αποδεικνύεται ψευδής (provably false) προτού καν τρέξει κάποιος βαθύς επικυρωτής (validator). Αυτό είναι το είδος της αντίφασης που αναδεικνύει μια επίπεδη (flat) λίστα ευρημάτων και την οποία κρύβει ένα απλό boolean επιτυχίας/αποτυχίας (pass/fail)
Τι δεν μπορεί να σας πει αυτός ο έλεγχος (audit)
Η ειλικρίνεια σχετικά με την εμβέλεια (scope) είναι αυτό που διατηρεί ένα εργαλείο προελέγχου (preflight) αξιόπιστο. Όλα τα παραπάνω διαβάζουν όσα το αρχείο δηλώνει για τον εαυτό του: το PDFium αναλύει (parses) τη δομή, και αυτός ο έλεγχος (audit) την καταγράφει (inventories). Δεν εκτελεί επικύρωση (validation) PDF/A — δεν κάνει ελέγχους κάλυψης γλυφικών (glyph-coverage checks) σε σχέση με ενσωματωμένες γραμματοσειρές (embedded fonts), δεν κάνει ανάλυση του χρωματικού χώρου (color space analysis) σε σχέση με τις προθέσεις εξόδου (output intents), ούτε εφαρμόζει τους κανόνες επιπέδου ρήτρας (clause-level rules) που διαχωρίζουν έναν ισχυρισμό από τη συμμόρφωση (conformance)· γι' αυτό χρειάζεστε έναν ειδικό επικυρωτή (validator) όπως η μηχανή προελέγχου του στοιχείου ή το veraPDF. Τα bits δικαιωμάτων (Permission bits) είναι δηλώσεις που τιμούν οι συμμορφούμενοι αναγνώστες (conforming readers), όχι κρυπτογραφικοί τοίχοι, επομένως το SEC-NOPRINT περιγράφει την πρόθεση (intent) και όχι την επιβολή (enforcement). Η σάρωση ενεργειών (action scan) καλύπτει σημειώσεις συνδέσμων (link annotations) και σενάρια επιπέδου εγγράφου· τα σενάρια που είναι θαμμένα στα λεξικά συμβάντων (event dictionaries) των πεδίων φόρμας χρειάζονται επιπλέον τα APIs των φορμών. Και ένας έλεγχος υπογραφής (signature check), αν επεκτείνετε τον έλεγχο (audit) με έναν τέτοιο, αναφέρει δηλωμένη πρόθεση, όχι επαληθευμένη (verified) κρυπτογραφία — η επικύρωση της αλυσίδας πιστοποιητικών (certificate chain validation) είναι μια ξεχωριστή εργασία. Ένας έλεγχος προελέγχου (preflight audit) είναι η συνέντευξη παραλαβής (intake interview), όχι η δίκη: η δουλειά του είναι να κάνει την απόφαση δρομολόγησης (routing decision) ενημερωμένη, γρήγορη και επαναλήψιμη
Σημείωση: Τα APIs αντικειμένων εγγράφου, σελίδας, σημείωσης, και εικόνας που χρησιμοποιούνται σε όλο αυτόν τον έλεγχο, μαζί με ένα υψηλού επιπέδου Delphi wrapper και μια πλήρη μηχανή προελέγχου επικύρωσης προτύπων, αποστέλλονται με το PDFium Component