
com.itextpdf.io.image.Jpeg2000ImageHelper Maven / Gradle / Ivy
/*
This file is part of the iText (R) project.
Copyright (c) 1998-2023 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 .
*/
package com.itextpdf.io.image;
import com.itextpdf.io.exceptions.IOException;
import com.itextpdf.io.exceptions.IoExceptionMessageConstant;
import com.itextpdf.io.util.StreamUtil;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.ArrayList;
final class Jpeg2000ImageHelper {
private static class Jpeg2000Box {
int length;
int type;
}
private static class ZeroBoxSizeException extends java.io.IOException {
ZeroBoxSizeException(String s) {
super(s);
}
}
private static final int JPIP_JPIP = 0x6a706970;
private static final int JP2_JP = 0x6a502020;
private static final int JP2_IHDR = 0x69686472;
private static final int JP2_FTYP = 0x66747970;
private static final int JP2_JP2H = 0x6a703268;
private static final int JP2_COLR = 0x636f6c72;
private static final int JP2_JP2C = 0x6a703263;
private static final int JP2_URL = 0x75726c20;
private static final int JP2_DBTL = 0x6474626c;
private static final int JP2_BPCC = 0x62706363;
private static final int JP2_JP2 = 0x6a703220;
private static final int JPX_JPXB = 0x6a707862;
public static void processImage(ImageData image) {
if (image.getOriginalType() != ImageType.JPEG2000)
throw new IllegalArgumentException("JPEG2000 image expected");
processParameters((Jpeg2000ImageData) image);
image.setFilter("JPXDecode");
}
/**
* This method checks if the image is a valid JPEG and processes some parameters.
*/
private static void processParameters(Jpeg2000ImageData jp2) {
jp2.parameters = new Jpeg2000ImageData.Parameters();
try {
if (jp2.getData() == null) {
jp2.loadData();
}
InputStream jpeg2000Stream = new ByteArrayInputStream(jp2.getData());
Jpeg2000Box box = new Jpeg2000Box();
box.length = cio_read(4, jpeg2000Stream);
if (box.length == 0x0000000c) {
jp2.parameters.isJp2 = true;
box.type = cio_read(4, jpeg2000Stream);
if (JP2_JP != box.type) {
throw new IOException(IoExceptionMessageConstant.EXPECTED_JP_MARKER);
}
if (0x0d0a870a != cio_read(4, jpeg2000Stream)) {
throw new IOException(IoExceptionMessageConstant.ERROR_WITH_JP_MARKER);
}
jp2_read_boxhdr(box, jpeg2000Stream);
if (JP2_FTYP != box.type) {
throw new IOException(IoExceptionMessageConstant.EXPECTED_FTYP_MARKER);
}
StreamUtil.skip(jpeg2000Stream, 8);
for (int i = 4; i < box.length / 4; ++i) {
if (cio_read(4, jpeg2000Stream) == JPX_JPXB) {
jp2.parameters.isJpxBaseline = true;
}
}
jp2_read_boxhdr(box, jpeg2000Stream);
do {
if (JP2_JP2H != box.type) {
if (box.type == JP2_JP2C) {
throw new IOException(IoExceptionMessageConstant.EXPECTED_JP2H_MARKER);
}
StreamUtil.skip(jpeg2000Stream, box.length - 8);
jp2_read_boxhdr(box, jpeg2000Stream);
}
} while (JP2_JP2H != box.type);
jp2_read_boxhdr(box, jpeg2000Stream);
if (JP2_IHDR != box.type) {
throw new IOException(IoExceptionMessageConstant.EXPECTED_IHDR_MARKER);
}
jp2.setHeight(cio_read(4, jpeg2000Stream));
jp2.setWidth(cio_read(4, jpeg2000Stream));
jp2.parameters.numOfComps = cio_read(2, jpeg2000Stream);
jp2.setBpc(cio_read(1, jpeg2000Stream));
StreamUtil.skip(jpeg2000Stream, 3);
jp2_read_boxhdr(box, jpeg2000Stream);
if (box.type == JP2_BPCC) {
jp2.parameters.bpcBoxData = new byte[box.length - 8];
jpeg2000Stream.read(jp2.parameters.bpcBoxData, 0, box.length - 8);
} else if (box.type == JP2_COLR) {
do {
if (jp2.parameters.colorSpecBoxes == null)
jp2.parameters.colorSpecBoxes = new ArrayList();
jp2.parameters.colorSpecBoxes.add(jp2_read_colr(box, jpeg2000Stream));
try {
jp2_read_boxhdr(box, jpeg2000Stream);
} catch (ZeroBoxSizeException ioe) {
//Probably we have reached the contiguous codestream box which is the last in jpeg2000 and has no length.
}
} while (JP2_COLR == box.type);
}
} else if (box.length == 0xff4fff51) {
StreamUtil.skip(jpeg2000Stream, 4);
int x1 = cio_read(4, jpeg2000Stream);
int y1 = cio_read(4, jpeg2000Stream);
int x0 = cio_read(4, jpeg2000Stream);
int y0 = cio_read(4, jpeg2000Stream);
StreamUtil.skip(jpeg2000Stream, 16);
jp2.setColorEncodingComponentsNumber(cio_read(2, jpeg2000Stream));
jp2.setBpc(8);
jp2.setHeight(y1 - y0);
jp2.setWidth(x1 - x0);
} else {
throw new IOException(IoExceptionMessageConstant.INVALID_JPEG2000_FILE);
}
} catch (java.io.IOException e) {
throw new IOException(IoExceptionMessageConstant.JPEG2000_IMAGE_EXCEPTION, e);
}
}
private static Jpeg2000ImageData.ColorSpecBox jp2_read_colr(Jpeg2000Box box, InputStream jpeg2000Stream) throws java.io.IOException {
int readBytes = 8;
Jpeg2000ImageData.ColorSpecBox colorSpecBox = new Jpeg2000ImageData.ColorSpecBox();
for (int i = 0; i < 3; i++) {
colorSpecBox.add(cio_read(1, jpeg2000Stream));
readBytes++;
}
if (colorSpecBox.getMeth() == 1) {
colorSpecBox.add(cio_read(4, jpeg2000Stream));
readBytes += 4;
} else {
colorSpecBox.add(0);
}
if (box.length - readBytes > 0) {
byte[] colorProfile = new byte[box.length - readBytes];
jpeg2000Stream.read(colorProfile, 0, box.length - readBytes);
colorSpecBox.setColorProfile(colorProfile);
}
return colorSpecBox;
}
private static void jp2_read_boxhdr(Jpeg2000Box box, InputStream jpeg2000Stream) throws java.io.IOException {
box.length = cio_read(4, jpeg2000Stream);
box.type = cio_read(4, jpeg2000Stream);
if (box.length == 1) {
if (cio_read(4, jpeg2000Stream) != 0) {
throw new IOException(IoExceptionMessageConstant.CANNOT_HANDLE_BOX_SIZES_HIGHER_THAN_2_32);
}
box.length = cio_read(4, jpeg2000Stream);
if (box.length == 0)
throw new IOException(IoExceptionMessageConstant.UNSUPPORTED_BOX_SIZE_EQ_EQ_0);
} else if (box.length == 0) {
throw new ZeroBoxSizeException("Unsupported box size == 0");
}
}
private static int cio_read(int n, InputStream jpeg2000Stream) throws java.io.IOException {
int v = 0;
for (int i = n - 1; i >= 0; i--) {
v += jpeg2000Stream.read() << (i << 3);
}
return v;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy