HotPDF, den inbyggda Delphi PDF-komponenten, avkodar CCITTFaxDecode-bilder med fullt stöd för T.4/T.6:s okomprimerade läge och för skadarad-återställning styrd av DamagedRowsBeforeError. Okomprimerat läge nås bara via extensionkoden som slutar på 111, och skadade rader repareras bara för Group 3-data med EndOfLine satt. Allt annat fallerar rent i stället för att producera en sned sida
Faxkodade bilder är den långa svansen av PDF-bildavkodning. De flesta CCITT-strömmar du stöter på kommer från en skannerdrivrutin eller en dokumentinsamlingspipeline som bara emitterar vanlig Group 4, och en avkodare som hanterar det vanliga fallet klarar alla tester du kastar på den. Sedan anländer ett arkiv från en femton år gammal faxserver, en sida renderas som diagonalt brus från rad 400 och nedåt, och du upptäcker att de två delarna av ITU-T-rekommendationerna ingen implementerar är exakt de två delarna den filen använder. Den här artikeln går igenom de två delarna som HotPDF hanterar dem, och genom de ställen där en avkodare kan gå fel medan den fortfarande ser korrekt ut
Vilka CCITTFaxDecode-parametrar ändrar egentligen bitströmmen?
Fyra poster i DecodeParms-ordlistan ändrar hur avkodaren ramar in bitströmmen: K, EndOfLine, EncodedByteAlign och DamagedRowsBeforeError. ISO 32000-1 §7.4.6 (tabell 11) definierar dem, och att få någon en fel förskjuter varje rad efter den första mismatchen. BlackIs1, Columns, Rows och EndOfBlock spelar roll också, men de påverkar polaritet, geometri och var avkodningen stannar snarare än hur en radgräns hittas
Kväljer kodningsschemat: negativt betyder ren tvådimensionell Group 4 (T.6), noll betyder endimensionell Modified Huffman (Group 3 1D), och positivt betyder blandad Group 3 2D, där en taggbit efter varje EOL säger om nästa rad är 1D eller 2DEndOfLinesäger att varje Group 3-rad föregås av en EOL-kod, elva nollbitar följt av en ettaEncodedByteAlignsäger att varje kodad rad börjar på en bytegräns, så avkodaren måste kassera utfyllnadsbitarna innan den läser denDamagedRowsBeforeErrorär hur många korrupta rader avkodaren får tolerera innan den ger upp, med standardvärdet 0
HotPDF läser alla dessa ur bildordlistan när du anropar THotPDF.ExtractLoadedImage eller THotPDF.DecodeLoadedImageStream. En medveten avvikelse är värd att känna till: när Columns saknas faller HotPDF tillbaka på bildens Width i stället för specifikationens standard 1728, och när Rows saknas används Height. En producent som utelämnar Columns på en A4-skanning på 2480 pixlar har fel, men det är vanligt nog att en avkodare som litar på bildgeometrin ger en användbar sida där den bokstavliga standarden skulle ge skräp
Varför får 000000 inte behandlas som blockets slut?
I tvådimensionella rader är sex inledande nollbitar tvetydiga, och en avkodare som läser dem som början på EOFB feltolkar en giltig ingång i okomprimerat läge som bildens slut. Group 4 har inga EOL:er mellan raderna; de enda nolltunga koderna är EOFB, som är två EOL:er i rad, och extensionkoderna från ITU-T T.4 och T.6. Båda börjar med 000000, och den sjunde biten är där de skiljer sig
HotPDF:s CCITT-avkodare löser prefixet bit för bit. En sjunde bit på 1 betyder att nästa tre bitar är en extensionkod, hela mönstret är 0000001xxx. Bara xxx = 111 ger inträde i okomprimerat läge; alla andra extensionvärden behandlas som felformade, eftersom ett PDF-faxfilter ingen användning har för de andra. En sjunde bit på 0 kan bara fortsätta en EOL, så avkodaren kräver då fyra fler nollbitar och en avslutande 1, elva nollor och en etta totalt, innan den rapporterar EOFB. Allt kortare eller längre är ett fel, inte ett tillåtande slut på bilden
Den tillåtande versionen är frestande eftersom den får en hel klass av trasiga filer att se bra ut: avkodningen stannar bara tidigt och du får en kort bild utan undantag. Det är också exakt buggen som avkortar legitima fax vid det första sträckan av okomprimerad data, vilket på ett inskannat formulär oftast är en tät signatur eller en stämpel. HotPDF väljer den strikta läsningen, så en kort bild betyder att kodaren verkligen slutade där
Hur kodar okomprimerat läge pixlar?
Okomprimerat läge ersätter runlängdkodning med literala pixelmönster, där varje kodord bär högst fem pixlar, tills en utgångskod växlar tillbaka. Endimensionella rader går in i det med 12-bitarskoden 000000001111; tvådimensionella rader använder 10-bitars-extensionen 0000001111 som beskrivits ovan. Kodare växlar till det för halftone-liknande regioner där runlängder på en eller två pixlar skulle kosta mer än pixlarna själva
Inuti läget räknas kodorden efter sina inledande nollor. Noll till fyra nollor följt av en 1 betyder så många vita pixlar och därefter en svart pixel. Fem nollor följt av en 1 betyder fem vita pixlar och ingenting annat. Sex till tio nollor följt av en 1 är utgångskoder: nollorna utöver sex emitterar upp till fyra vita pixlar till, och den enskilda biten efter utgångskoden, taggbiten, sätter färgen på nästa run när den vanliga runkodningen återupptas
{ Kodord för okomprimerat läge (ITU-T T.4 / T.6)
W = vit pixel, B = svart pixel, T = taggbit
1 B
01 W B
001 W W B
0001 W W W B
00001 W W W W B
000001 W W W W W
0000001T avbryt, fortsätt sedan körningar med färg T
00000001T W, avbryt
000000001T W W, avbryt
0000000001T W W W, avbryt
00000000001T W W W W, avbryt
fler än tio nollor: felformat }
Taggbiten är den subtila delen. Runkodning har tillstånd: en 1D-rad alternerar vita och svarta runlängder, och en 2D-rad spårar färgen hos det aktuella förändringselementet a0. Efter utgångskoden måste avkodaren återuppta med vilken färg taggen namnger, vilket stämmer eller inte med den sista literala pixeln. HotPDF-avkodaren registrerar en färgändring vid aktuell position när taggen skiljer sig från färgen den spårade, så nästa run börjar från rätt färg. Ignorera taggen och resten av raden kommer ut färginverterad, och i 2D-läge ärver varje följande rad skadan genom sin referenslinje
Vad återställer DamagedRowsBeforeError, och vad gör den inte?
DamagedRowsBeforeError låter HotPDF ersätta en korrupt rad och fortsätta, men bara när K är 0 eller positivt och EndOfLine är sant. De två villkoren är inte godtyckliga. Återställning behöver ett sätt att hitta var nästa rad börjar när bitarna slutar ge mening, och det enda pålitliga riktmärket i en CCITT-ström är EOL-koden. Group 4-data har inga EOL:er, och Group 3-data utan obligatoriska EOL:er ger ingen garanti för att en finns, så i båda fallen avslutar en skadad rad avkodningen oavsett parametervärde
När en rad fallerar skannar HotPDF-avkodaren framåt till nästa EOL, en sekvens av minst elva nollbitar följt av en etta, och substituerar en ersättningsrad. Den första skadade raden efter en bra ersätts med en kopia av föregående rad, vilket på en inskannad textsida oftast är nära nog för att vara osynligt. En skadad rad som direkt följer en annan skadad rad ersätts i stället med en helvit rad, eftersom att kopiera en kopia skulle smeta ut en textrad till en hög svart bar. Substitutet blir sedan referenslinjen för nästa 2D-rad, vilket är det som håller en ström i blandat läge avkodningsbar efter reparationen
Två gränser är lätta att missa. Budgeten är kumulativ över hela bilden, inte per omgång dåliga rader, så DamagedRowsBeforeError 3 tolererar tre skadade rader totalt och den fjärde fallerar avkodningen. Och en misslyckad avkodning i HotPDF är allt eller inget: THotPDF.ExtractLoadedImage returnerar nil i stället för en bitmap med en naggad nederkant. THPDFLoadedImageInfo.Decodable rapporterar att filtret stöds, inte att just den här strömmen är intakt, så kontrollera alltid bitmappen mot nil
Varför skanningen måste stanna vid början av EOL
Resynkroniseringsskanningen lämnar läsaren vid den första nollbiten i EOL, inte efter den. Avkodaren har en enda ramningsloop som konsumerar EOL:er, kontrollerar den dubbla EOL som avslutar blocket och läser taggbiten för blandat läge. Sväljer återställningsskanningen själva EOL:en ser loopen en rad utan EOL, behandlar den också som skadad och bränner en andra enhet av budgeten medan den tappar en bra rad. Värre, en överhoppad taggbit innebär att en 2D-rad avkodas som 1D eller tvärtom. Samma regel gäller när en 1D-runavkodare träffar en oväntad EOL mitt i raden: den spolar tillbaka till där runkoden började, så markören finns kvar för återställningsskanningen
Inventera CCITT-bilder i en laddad PDF
Innan du litar på en batch arkiverade fax, lista vilka bilder som använder CCITTFaxDecode och vilka som inte går att avkoda. THotPDF.GetLoadedImageInfo ger dig geometri och filternamnet utan att avkoda något, och THotPDF.ExtractLoadedImage talar om huruvida strömmen överlevde. För den bredare bilden av hur bildhanteraren dirigerar varje filter, se guiden till att extrahera laddade PDF-bilder och deras avkodningsfilter
var
Pdf: THotPDF;
Info: THPDFLoadedImageInfo;
Bmp: TBitmap;
I: Integer;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('fax-archive.pdf', '') <= 0 then
Exit;
for I := 0 to Pdf.GetLoadedImageCount - 1 do
begin
if not Pdf.GetLoadedImageInfo(I, Info) then
Continue;
if Pos('CCITT', UpperCase(String(Info.Filter))) = 0 then
Continue;
Bmp := Pdf.ExtractLoadedImage(I); // nil när strömmen är för skadad
try
if Bmp = nil then
Writeln(Format('obj %d: %dx%d CCITT failed to decode',
[Info.ObjectNumber, Info.Width, Info.Height]))
else
Bmp.SaveToFile(Format('fax_%d.bmp', [I]));
finally
Bmp.Free; // anroparen äger bitmapen
end;
end;
finally
Pdf.Free;
end;
end;
För felen, spara bevisen. THotPDF.ExtractLoadedImageToStream med standardläget lismEncoded skriver de lagrade strömbytena orörda, vilket är vad du vill lämna till en andra avkodare, slå in i en TIFF-container för inspektion, eller bifoga till en buggrapport. Att koda om ett skadat fax gömmer bara var skadan satt
procedure SaveRawFax(Pdf: THotPDF; ImageIndex: Integer; const FileName: string);
var
Fs: TFileStream;
begin
Fs := TFileStream.Create(FileName, fmCreate);
try
if not Pdf.ExtractLoadedImageToStream(ImageIndex, Fs, lismEncoded) then
raise Exception.CreateFmt('Image %d has no stream data', [ImageIndex]);
finally
Fs.Free;
end;
end;
Går du själv igenom bildströmmar via THotPDF.CollectLoadedImageStreams, skicka varje THPDFStreamObject till THotPDF.DecodeLoadedImageStream och du får samma CCITT-väg, parametrar och återställningsregler. Frisläpp den returnerade listan men inte strömobjekten i den, som förblir ägda av dokumentet
Ska du höja DamagedRowsBeforeError på filer du inte styr?
Nej, inte genom att redigera PDF:en, och oftast inte alls. Parametern hör hemma hos producenten, den enda part som vet om dess överföringskanal tappar bitar. En fil som deklarerar DamagedRowsBeforeError 0 och sedan innehåller en skadad rad talar om för dig att arkivkopian är korrupt, och det ärliga resultatet är ett avkodningsfel du kan logga. Att tyst lappa rader på ett avtal eller en journal gör en detekterbar defekt till en bild som ser trolig ut med en saknad rad, vilket är ett sämre utfall för alla som senare litar på den
Det säkrare mönstret för opålitliga arkiv är att hålla avkodningen strikt, registrera vilka objektnummer som fallerade, och dirigera dem till en människa eller en separat avkodare. Är arkivet stort och kommer från många källor begränsar att köra bildavkodning utanför processen vad en fientlig ström kan göra mot värdapplikationen; artikeln om att isolera bildcodecs vid bearbetning av opålitliga PDF:er täcker det upplägget, och guiden för JBIG2 bilevel-komprimering jämför det andra bilevelfiltret du hittar i samma skannade arkiv
FAQ
Stöder HotPDF CCITT okomprimerat läge i Group 4-bilder?
Ja. HotPDF känner igen 10-bitars-extensionen 0000001111 i tvådimensionella rader och 12-bitars-ingångskoden 000000001111 i endimensionella rader, avkodar de literala kodorden och hedrar utgångens taggbit när runkodningen återupptas
Varför har DamagedRowsBeforeError ingen effekt på mitt Group 4-fax?
Återställning kräver EOL-markörer för att resynkronisera. HotPDF tillämpar DamagedRowsBeforeError bara när K är 0 eller positivt och EndOfLine är sant; Group 4-data (K negativt) har inga EOL:er, så varje skadad rad avslutar avkodningen
Vad returnerar ExtractLoadedImage för en skadad CCITT-bild?
THotPDF.ExtractLoadedImage returnerar nil när skadan överstiger vad DamagedRowsBeforeError tillåter eller inte kan återställas alls. Den returnerar aldrig en partiell bitmap, så testa mot nil och använd ExtractLoadedImageToStream för att behålla råa bytes
HotPDF hanterar CCITT-, JBIG2-, JPEG-, JPEG 2000- och Flate-bilder i laddade PDF:er från ren Delphi- och C++Builder-kod, utan extern DLL för faxvägen. Detaljer och utgåvor finns på sidan för HotPDF Delphi PDF component