CCITTFaxDecodeFilter.java
/*
This file is part of the iText (R) project.
Copyright (c) 1998-2025 Apryse Group NV
Authors: Apryse Software.
This program is offered under a commercial and under the AGPL license.
For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
AGPL licensing:
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itextpdf.kernel.pdf.filters;
import com.itextpdf.kernel.exceptions.PdfException;
import com.itextpdf.io.codec.TIFFConstants;
import com.itextpdf.io.codec.TIFFFaxDecoder;
import com.itextpdf.io.codec.TIFFFaxDecompressor;
import com.itextpdf.kernel.exceptions.KernelExceptionMessageConstant;
import com.itextpdf.kernel.pdf.PdfBoolean;
import com.itextpdf.kernel.pdf.PdfDictionary;
import com.itextpdf.kernel.pdf.PdfName;
import com.itextpdf.kernel.pdf.PdfNumber;
import com.itextpdf.kernel.pdf.PdfObject;
/**
* Handles CCITTFaxDecode filter
*/
public class CCITTFaxDecodeFilter implements IFilterHandler {
@Override
public byte[] decode(byte[] b, PdfName filterName, PdfObject decodeParams, PdfDictionary streamDictionary) {
PdfNumber wn = streamDictionary.getAsNumber(PdfName.Width);
PdfNumber hn = streamDictionary.getAsNumber(PdfName.Height);
if (wn == null || hn == null) {
throw new PdfException(KernelExceptionMessageConstant.FILTER_CCITTFAXDECODE_IS_ONLY_SUPPORTED_FOR_IMAGES);
}
int width = wn.intValue();
int height = hn.intValue();
PdfDictionary param = decodeParams instanceof PdfDictionary ? (PdfDictionary) decodeParams : null;
int k = 0;
boolean blackIs1 = false;
boolean byteAlign = false;
if (param != null) {
PdfNumber kn = param.getAsNumber(PdfName.K);
if (kn != null) {
k = kn.intValue();
}
PdfBoolean bo = param.getAsBoolean(PdfName.BlackIs1);
if (bo != null) {
blackIs1 = bo.getValue();
}
bo = param.getAsBoolean(PdfName.EncodedByteAlign);
if (bo != null) {
byteAlign = bo.getValue();
}
}
byte[] outBuf = new byte[(width + 7) / 8 * height];
TIFFFaxDecompressor decoder = new TIFFFaxDecompressor();
if (k == 0 || k > 0) {
int tiffT4Options = k > 0 ? TIFFConstants.GROUP3OPT_2DENCODING : 0;
tiffT4Options |= byteAlign ? TIFFConstants.GROUP3OPT_FILLBITS : 0;
decoder.SetOptions(1, TIFFConstants.COMPRESSION_CCITTFAX3, tiffT4Options, 0);
decoder.decodeRaw(outBuf, b, width, height);
if (decoder.fails > 0) {
byte[] outBuf2 = new byte[(width + 7) / 8 * height];
int oldFails = decoder.fails;
decoder.SetOptions(1, TIFFConstants.COMPRESSION_CCITTRLE, tiffT4Options, 0);
decoder.decodeRaw(outBuf2, b, width, height);
if (decoder.fails < oldFails) {
outBuf = outBuf2;
}
}
} else {
long tiffT6Options = 0;
tiffT6Options |= byteAlign ? TIFFConstants.GROUP4OPT_FILLBITS : 0;
TIFFFaxDecoder deca = new TIFFFaxDecoder(1, width, height);
deca.decodeT6(outBuf, b, 0, height, tiffT6Options);
}
if (!blackIs1) {
int len = outBuf.length;
for (int t = 0; t < len; ++t) {
outBuf[t] ^= 0xff;
}
}
b = outBuf;
return b;
}
}