org.jpedal.color.ICCColorSpace Maven / Gradle / Ivy
/*
* ===========================================
* Java Pdf Extraction Decoding Access Library
* ===========================================
*
* Project Info: http://www.idrsolutions.com
* Help section for developers at http://www.idrsolutions.com/support/
*
* (C) Copyright 1997-2016 IDRsolutions and Contributors.
*
* This file is part of JPedal/JPDF2HTML5
*
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* ---------------
* ICCColorSpace.java
* ---------------
*/
package org.jpedal.color;
import java.awt.color.ColorSpace;
import java.awt.color.ICC_ColorSpace;
import java.awt.color.ICC_Profile;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.Raster;
import java.io.ByteArrayInputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import org.jpedal.exception.PdfException;
import org.jpedal.objects.raw.PdfObject;
import org.jpedal.utils.LogWriter;
/**
* handle ICCColorSpace
*/
public class ICCColorSpace
extends GenericColorSpace {
//cache values to speed up translation
private final int[] a1,b1,c1;
private final Map cache=new HashMap();
private float[] prevFloat;
public ICCColorSpace(final PdfObject colorSpace) {
//set cache to -1 as flag
a1=new int[256];
b1=new int[256];
c1=new int[256];
for(int i=0;i<256;i++){
a1[i]=-1;
b1[i]=-1;
c1[i]=-1;
}
setType(ColorSpaces.ICC);
cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
final byte[] icc_data=colorSpace.getDecodedStream();
if (icc_data == null){
LogWriter.writeLog("Error in ICC data");
}else {
try{
cs = new ICC_ColorSpace(ICC_Profile.getInstance(icc_data));
type=cs.getType();
}catch(final Exception e){
LogWriter.writeLog("[PDF] Problem "+e.getMessage()+" with ICC data ");
failed=true;
}
}
componentCount=cs.getNumComponents();
}
/**
* set color (in terms of rgb)
*/
@Override
public final void setColor(final String[] number_values, final int items) {
//if(isCached)
// System.out.println("BsetColor "+size);
final float[] colValues=new float[items];
for(int ii=0;ii1) {
lookup[i]=(int)(val);
} else {
lookup[i]=(int)(val*255);
}
}
if(size==3 && (a1[lookup[0]]!=-1) &&
(b1[lookup[1]]!=-1)&&(c1[lookup[2]]!=-1))
{
currentColor=new PdfColor(a1[lookup[0]],b1[lookup[1]],c1[lookup[2]]);
//System.out.println("cached "+operand[0]+" "+operand[1]+" "+operand[2]+" "+this);
}else if(size==4 && cache.get((lookup[0] << 24) + (lookup[1] << 16) + (lookup[2] << 8) + lookup[3])!=null){
final Integer val=cache.get((lookup[0] << 24) + (lookup[1] << 16) + (lookup[2] << 8) + lookup[3]);
final int raw = val;
final int rr = ((raw >> 16) & 255);
final int gg = ((raw >> 8) & 255);
final int bb = ((raw) & 255);
currentColor=new PdfColor(rr,gg,bb);
}else{
try{
values=cs.toRGB(values);
}catch(final Exception ee){
//file with invalid values appears to work if we just replace
values=new float[]{values[0],values[0],values[0]};
LogWriter.writeLog("Invalid ICC values "+ee);
}
currentColor=new PdfColor(values[0],values[1],values[2]);
if(size==3){
a1[lookup[0]]=(int)(values[0]*255);
b1[lookup[1]]=(int)(values[1]*255);
c1[lookup[2]]=(int)(values[2]*255);
}else if(size==4){ //not used except as flag
final int raw = ((int)(values[0]*255) << 16) +
((int)(values[1]*255) << 8) +
(int)(values[2]*255);
//store values in cache
cache.put((lookup[0] << 24) + (lookup[1] << 16) + (lookup[2] << 8) + lookup[3], raw);
}
}
}
/**
* convert Index to RGB
*/
@Override
public byte[] convertIndexToRGB(final byte[] data){
isConverted=true;
if(componentCount==4) {
return convert4Index(data);
} else {
return data;
}
}
/**
*
* Convert DCT encoded image bytestream to sRGB
*
*
* It uses the internal Java classes and the Adobe icm to convert CMYK and
* YCbCr-Alpha - the data is still DCT encoded.
*
*
* The Sun class JPEGDecodeParam.java is worth examining because it contains
* lots of interesting comments
*
*
* I tried just using the new IOImage.read() but on type 3 images, all my
* clipping code stopped working so I am still using 1.3
*
*/
@Override
public BufferedImage JPEGToRGBImage(
final byte[] data, final int w, final int h, final int pX, final int pY) {
if(data.length>9 && data[6] == 'J' && data[7] == 'F' && data[8] == 'I' && data[9] == 'F'){
return nonRGBJPEGToRGBImage(data,w,h, pX,pY);
}else {
return algorithmicICCToRGB(data,w,h,pX,pY);
}
}
/**
* convert byte[] datastream JPEG to an image in RGB
* @throws org.jpedal.exception.PdfException
*/
@Override
public BufferedImage JPEG2000ToRGBImage(final byte[] data,int w,int h,
final int pX, final int pY, final int d) throws PdfException {
byte[] index=this.getIndexedMap();
if(cs.getNumComponents()==3 || index!=null) {
return super.JPEG2000ToRGBImage(data, w, h, pX, pY,d);
}else{
return JPEG2000ToImage(data, pX, pY);
}
}
private BufferedImage algorithmicICCToRGB(
byte[] data, int w, int h, final int pX, final int pY) {
BufferedImage image = null;
ImageReader iir=null;
@SuppressWarnings("UnusedAssignment") ImageInputStream iin=null;
final ByteArrayInputStream in = new ByteArrayInputStream(data);
try{
//suggestion from Carol
final Iterator iterator = ImageIO.getImageReadersByFormatName("JPEG");
while (iterator.hasNext())
{
final ImageReader o = iterator.next();
iir = o;
if (iir.canReadRaster()) {
break;
}
}
ImageIO.setUseCache(false);
iin = ImageIO.createImageInputStream((in));
iir.setInput(iin, true); //new MemoryCacheImageInputStream(in));
Raster ras=iir.readRaster(0,null);
//some images need this
if(iir.getRawImageType(0)==null || alternative==-1) {
return nonRGBJPEGToRGBImage(data,w,h, pX,pY);
}
ras=cleanupRaster(ras,pX,pY,componentCount);
w=ras.getWidth();
h=ras.getHeight();
final byte[] new_data = new byte[w * h * 3];
//reuse variable
data=((DataBufferByte)ras.getDataBuffer()).getData();
final int pixelCount = w * h*3;
float lastR=0,lastG=0,lastB=0;
int pixelReached = 0;
float lastIn1=-1,lastIn2=-1,lastIn3=-1;
for (int i = 0; i < pixelCount; i += 3) {
final float in1 = ((data[i] & 255))/255f;
final float in2 = ((data[1+i] & 255))/255f;
final float in3 = ((data[2+i] & 255))/255f;
final float[] outputValues;
if((lastIn1==in1)&&(lastIn2==in2)&&(lastIn3==in3)){
//use existing values
}else{//work out new
final float[] inputValues={in1,in2,in3};
outputValues=cs.toRGB(inputValues);
//outputValues=inputValues;
//reset values
lastR=(outputValues[0]*255);
lastG=(outputValues[1]*255);
lastB=(outputValues[2]*255);
lastIn1=in1;
lastIn2=in2;
lastIn3=in3;
}
new_data[pixelReached++] =(byte) lastR;
new_data[pixelReached++] = (byte)lastG;
new_data[pixelReached++] = (byte)lastB;
}
final int[] bands = {0,1,2};
final DataBuffer db = new DataBufferByte(new_data, new_data.length);
image =new BufferedImage(w,h,BufferedImage.TYPE_INT_RGB);
final Raster raster =Raster.createInterleavedRaster(db,w,h,w * 3,3,bands,null);
image.setData(raster);
}catch(final Exception e){
LogWriter.writeLog("Problem with color conversion "+e);
}finally{
try {
in.close();
iir.dispose();
iin.close();
} catch (final Exception ee) {
LogWriter.writeLog("Problem closing " + ee);
}
}
return image;
}
}