PDF objektų srautas gali išsiplėsti be klaidos, tačiau grąžinti triukšmą, jei praleistas ISO 32000-1 Predictor atkūrimas/DecodeParms/Predictor
FlateDecode vien savaime nepakanka
FlateDecode atlieka tik DEFLATE išskleidimą, o Predictor aprašo papildomą transformaciją srauto /DecodeParms žodyne/DecodeParms
Predictor pakeisti baitai atrodo beveik tinkami, todėl naivus analizatorius sugenda tik pasiekęs negalimą PDF žetoną
Ką iš tikrųjų daro PDF Predictor parametras
/Predictor reikšmė 1 reiškia jokio prognozavimo, 2 parenka TIFF, o 10–15 parenka PNG atkūrimą kartu su /Colors, /BitsPerComponent ir /Columns geometrija/Predictor/DecodeParms/Colors/BitsPerComponent/Columns
TIFF Predictor 2 atkuria skirtumą eilutėje, o PNG kiekvienoje eilutėje skaito filtro žymą ir taiko vieną iš penkių filtrų/Predictor/Predictor
Kodėl objektų srautuose praleistas Predictor lieka nepastebėtas
Objektų srautai gali supakuoti katalogą, /OutputIntents ar /Metadata į /ObjStm, todėl praleistas atkūrimas paslepia objektus be klaidos/ObjStm/OutputIntents/Metadata/Predictor
PDFiumPas iki v2.16.0 išplėsdavo /Predictor 12 srautą į skirtuminius baitus, kurių struktūros skaitytuvas negalėjo analizuoti/PredictorPdfExpandObjectStreams/OutputIntents/Metadatastraipsnį apie objektų ir xref srautų validavimą su PDFiumPas
PNG ir TIFF Predictor eilučių atkūrimas Pascal kalboje
PdfApplyPredictor apskaičiuoja eilutės plotį ir pikselio baitų plotį pagal /Columns, /Colors ir /BitsPerComponentPdfApplyPredictor/Predictor/Predictor
function PdfApplyPredictor(const Src: TBytes;
Predictor, Colors, Bpc, Columns: Integer): TBytes;
var
RowLen, Bpp, R, I: Integer;
begin
Result:= Src;
if Predictor< 2 then
Exit; // 1 = no prediction, nothing to undo
if Colors<= 0 then Colors:= 1;
if Bpc<= 0 then Bpc:= 8;
if Columns<= 0 then Columns:= 1;
if (Colors> 64)or (Bpc> 32)or (Columns> 1 shl 24) then
Exit; // reject hostile row geometries
RowLen:= (Columns* Colors* Bpc+ 7) div 8; // ceil(), per ISO 32000-1 Table 8
Bpp:= (Colors* Bpc+ 7) div 8;
if Predictor= 2 then
begin
if Bpc<> 8 then
Exit; // only the 8-bit layout is reconstructed
Result:= Copy(Src, 0, Length(Src));
R:= 0;
while R+ RowLen<= Length(Result) do
begin
for I:= R+ Bpp to R+ RowLen- 1 do
Result[I]:= Byte(Result[I]+ Result[I- Bpp]);
Inc(R, RowLen);
end;
Exit;
end;
// Predictor >= 10 falls through to PNG row-filter reconstruction below
end;
// Continues inside PdfApplyPredictor once Predictor>= 10 (PNG row filters).
// Rows:= Length(Src) div (RowLen+ 1); each row is a 1-byte filter tag
// followed by RowLen data bytes, decoded left to right.
for R:= 0 to Rows- 1 do
begin
SrcOfs:= R* (RowLen+ 1);
DstOfs:= R* RowLen;
Tag:= Src[SrcOfs];
Inc(SrcOfs);
for I:= 0 to RowLen- 1 do
begin
if I>= Bpp then A:= Result[DstOfs+ I- Bpp] else A:= 0; // byte to the left
if R> 0 then B:= Result[DstOfs+ I- RowLen] else B:= 0; // byte above
case Tag of
1: Result[DstOfs+ I]:= Byte(Src[SrcOfs+ I]+ A); // Sub
2: Result[DstOfs+ I]:= Byte(Src[SrcOfs+ I]+ B); // Up
3: Result[DstOfs+ I]:= Byte(Src[SrcOfs+ I]+ (A+ B) div 2); // Average
// Paeth (tag 4) adds whichever of A, B or the byte above-left sits
// closest to the linear predictor A+ B- C; tag 0 (None) copies the
// filtered byte through unchanged
else Result[DstOfs+ I]:= Src[SrcOfs+ I];
end;
end;
end;
Kas pasikeitė PDFiumPas v2.16.0
PdfReadAndDecodeStream taiko atkūrimą tik vienam FlateDecode filtrui ir saugiai perskaito Predictor parametrus tame pačiame žodynePdfReadAndDecodeStream/Filter/Predictor/Colors/BitsPerComponent/ColumnsPdfDictRefNumsusijusį straipsnį apie saugų PDF žodynų analizavimą
// Inside PdfReadAndDecodeStream, right after PdfInflate() has already run:
if PdfFilterIsPureFlate(DictTxt) then
begin
Inflated:= PdfInflate(Raw);
Predictor:= PdfDictRefNum(Data, DS, DE, 'Predictor');
if Predictor>= 2 then
begin
PColors:= PdfDictRefNum(Data, DS, DE, 'Colors');
PBpc:= PdfDictRefNum(Data, DS, DE, 'BitsPerComponent');
PColumns:= PdfDictRefNum(Data, DS, DE, 'Columns');
Result:= PdfApplyPredictor(Inflated, Predictor, PColors, PBpc, PColumns);
end
else
Result:= Inflated;
end;
Po pataisos objektų srautai atkuriami teisingai, o per didelės /Colors, /BitsPerComponent ir /Columns reikšmės atmetamos/Predictor/OutputIntents/MetadataPdfApplyPredictor/Colors/BitsPerComponent/Columns
var
Pdf: TPdf;
Report: TPdfAValidationResult;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := 'incoming.pdf';
Pdf.Active := True;
Report := Pdf.ValidatePdfA;
if not Report.IsCompliant then
LogNonCompliance(Report); // caller-supplied handler
finally
Pdf.Free;
end;
end;
Verta žinoti ribas
TIFF atkūrimas apima tik 8 bitų komponentus, o PNG atkūrimas rekonstruoja visas penkias apibrėžtas filtro rūšis/Predictor/BitsPerComponent/Predictor
Vietinis PDFium atvaizdavimas jau tinkamai rekonstruoja tokius duomenis, tačiau baitų lygio validavimo ir pasirašymo kodas turi atlikti tą patįPDFiumPas