
org.jpedal.images.ImageTransformer 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-2015 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
*
* ---------------
* ImageTransformer.java
* ---------------
*/
package org.jpedal.images;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import org.jpedal.color.ColorSpaces;
import org.jpedal.io.ColorSpaceConvertor;
import org.jpedal.objects.GraphicsState;
import org.jpedal.utils.LogWriter;
/**
* class to shrink and clip an extracted image
* On reparse just calculates co-ords
*/
public class ImageTransformer {
/**holds the actual image*/
private BufferedImage current_image;
/**matrices used in transformation*/
private final float[][] Trm, CTM;
/**image co-ords*/
private int i_x, i_y, i_w, i_h;
/**
* pass in image information and apply transformation matrix
* to image
*/
public ImageTransformer(
final GraphicsState current_graphics_state,
final BufferedImage new_image) {
//save global values
this.current_image = new_image;
final int w;
final int h;
w = current_image.getWidth(); //raw width
h = current_image.getHeight(); //raw height
CTM = current_graphics_state.CTM; //local copy of CTM
//build transformation matrix by hand to avoid errors in rounding
Trm = new float[3][3];
Trm[0][0] = (CTM[0][0] / w);
Trm[0][1] = -(CTM[0][1] / w);
Trm[0][2] = 0;
Trm[1][0] = -(CTM[1][0] / h);
Trm[1][1] = (CTM[1][1] / h);
Trm[1][2] = 0;
Trm[2][0] = CTM[2][0];
Trm[2][1] = CTM[2][1];
Trm[2][2] = 1;
//round numbers if close to 1
for (int y = 0; y < 3; y++) {
for (int x = 0; x < 3; x++) {
if ((Trm[x][y] > .99) & (Trm[x][y] < 1)) {
Trm[x][y] = 1;
}
}
}
scale(w,h);
completeImage();
}
private void scale(final int w, final int h){
/**
* transform the image only if needed
*/
if (Trm[0][0] != 1.0 || Trm[1][1] != 1.0 || Trm[0][1] != 0.0 || Trm[1][0] != 0.0) {
//workout transformation for the image
AffineTransform image_at =new AffineTransform(Trm[0][0],Trm[0][1],Trm[1][0],Trm[1][1],0,0);
//apply it to the shape first so we can align
final Area r =new Area(new Rectangle(0,0,w,h));
r.transform(image_at);
//make sure it fits onto image (must start at 0,0)
final double ny = r.getBounds2D().getY();
final double nx = r.getBounds2D().getX();
float a = Trm[0][0];
float b=Trm[0][1];
float c=Trm[1][0];
float d=Trm[1][1];
image_at =new AffineTransform(a,b,c,d,-nx,-ny);
/**
* avoid upscaling
*/
if(a<0) {
a = -a;
}
if(b<0) {
b = -b;
}
if(c<0) {
c = -c;
}
if(d<0) {
d = -d;
}
//avoid large figures
if(a>5 || b>5 || c>5 || d>5) {
return;
}
//Create the affine operation.
//ColorSpaces.hints causes single lines to vanish);
AffineTransformOp invert;
if(w>1 && h>1){
//fix image inversion if matrix (0,x,-y,0)
if(CTM[0][0]==0 && CTM[1][1]==0 && CTM[0][1]>0 && CTM[1][0]<0){
image_at.scale(-1,1);
image_at.translate(-current_image.getWidth(),0);
}
invert = new AffineTransformOp(image_at, ColorSpaces.hints);
}else{
//allow for line with changing values
boolean isSolid=true;
if(h==1){
//test all pixels set so we can keep a solid line
final Raster ras=current_image.getRaster();
final int bands=ras.getNumBands();
final int width=ras.getWidth();
final int[] elements=new int[(width*bands)+1];
ras.getPixels(0,0,width,1, elements);
for(int j=0;j1){
boolean failed=false;
//allow for odd behaviour on some files
try{
invert.filter(current_image,destImage);
current_image=destImage;
}catch(final Exception e){
LogWriter.writeLog("Exception: " + e.getMessage());
failed=true;
}
if(failed){
try{
invert = new AffineTransformOp(image_at,null);
current_image = invert.filter(current_image,null);
}catch(final Exception e){
LogWriter.writeLog("Exception: " + e.getMessage());
}
}
}
}
/**
* complete image
*/
private void completeImage(){
/**
* now workout correct screen co-ords allow for rotation
*
if ((CTM[1][0] == 0) &( (CTM[0][1] == 0))){
i_w =(int) Math.sqrt((CTM[0][0] * CTM[0][0]) + (CTM[0][1] * CTM[0][1]));
i_h =(int) Math.sqrt((CTM[1][1] * CTM[1][1]) + (CTM[1][0] * CTM[1][0]));
}else{
i_h =(int) Math.sqrt((CTM[0][0] * CTM[0][0]) + (CTM[0][1] * CTM[0][1]));
i_w =(int) Math.sqrt((CTM[1][1] * CTM[1][1]) + (CTM[1][0] * CTM[1][0]));
}
if (CTM[1][0] < 0)
i_x = (int) (CTM[2][0] + CTM[1][0]);
else
i_x = (int) CTM[2][0];
if (CTM[0][1] < 0) {
i_y = (int) (CTM[2][1] + CTM[0][1]);
} else {
i_y = (int) CTM[2][1];
}
//alter to allow for back to front or reversed
if (CTM[1][1] < 0)
i_y = i_y - i_h;
if (CTM[0][0] < 0)
i_x = i_x - i_w;
*/
calcCoordinates();
}
/**
* workout correct screen co-ords allow for rotation
*/
private void calcCoordinates(){
if (CTM[1][0] == 0 && CTM[0][1] == 0){
i_x = (int) CTM[2][0];
i_y = (int) CTM[2][1];
i_w =(int) CTM[0][0];
i_h =(int) CTM[1][1];
if(i_w<0) {
i_w = -i_w;
}
if(i_h<0) {
i_h = -i_h;
}
}else{ //some rotation/skew
i_w=(int) (Math.sqrt((CTM[0][0] * CTM[0][0]) + (CTM[0][1] * CTM[0][1])));
i_h =(int) (Math.sqrt((CTM[1][1] * CTM[1][1]) + (CTM[1][0] * CTM[1][0])));
if(CTM[1][0]>0 && CTM[0][1]<0){
i_x = (int) (CTM[2][0]);
i_y = (int) (CTM[2][1]+CTM[0][1]);
//System.err.println("AA "+i_w+" "+i_h);
}else if(CTM[1][0]<0 && CTM[0][1]>0){
i_x = (int) (CTM[2][0]+CTM[1][0]);
i_y = (int) (CTM[2][1]);
//System.err.println("BB "+i_w+" "+i_h);
}else if(CTM[1][0]>0 && CTM[0][1]>0){
i_x = (int) (CTM[2][0]);
i_y = (int) (CTM[2][1]);
//System.err.println("CC "+i_w+" "+i_h);
}else{
//System.err.println("DD "+i_w+" "+i_h);
i_x = (int) (CTM[2][0]);
i_y =(int) (CTM[2][1]);
}
}
//alter to allow for back to front or reversed
if ( CTM[1][1]< 0) {
i_y -= i_h;
}
if ( CTM[0][0]< 0) {
i_x -= i_w;
}
}
/**
* get y of image (x1,y1 is top left)
*/
public final int getImageY() {
return i_y;
}
/**
* get image
*/
public final BufferedImage getImage() {
return current_image;
}
//////////////////////////////////////////////////////////////////////////
/**
* get width of image
*/
public final int getImageW() {
return i_w;
}
//////////////////////////////////////////////////////////////////////////
/**
* get height of image
*/
public final int getImageH() {
return i_h;
}
//////////////////////////////////////////////////////////////////////////
/**
* get X of image (x,y is top left)
*/
public final int getImageX() {
return i_x;
}
/////////////////////////////////////////////////////////////////////////
/**
* clip the image
*/
public final void clipImage(final Area current_shape) {
//create a copy of clip (so we don't alter clip)
final Area final_clip = (Area) current_shape.clone();
//actual size so we can trap any rounding error
final int image_w = current_image.getWidth();
final int image_h = current_image.getHeight();
//shape of final image
final double shape_x = final_clip.getBounds2D().getX();
final double shape_y = final_clip.getBounds2D().getY();
final double shape_h = final_clip.getBounds2D().getHeight();
final double d_y = (image_h - shape_h);
final AffineTransform upside_down = new AffineTransform();
upside_down.translate(-shape_x, -shape_y); //center
upside_down.scale(1, -1); //reflect in x axis
upside_down.translate(shape_x, - (shape_y + shape_h));
final_clip.transform(upside_down);
//line up to shape
final AffineTransform align_clip = new AffineTransform();
//if not working at 72 dpi, alter clip to fit
align_clip.translate(-i_x, i_y + d_y);
final_clip.transform(align_clip);
//co-ords of transformed shape
//reset sizes to remove area clipped
double x = final_clip.getBounds2D().getX();
double y = final_clip.getBounds2D().getY();
double w = final_clip.getBounds2D().getWidth();
double h = final_clip.getBounds2D().getHeight();
//get type of image used
int image_type = current_image.getType();
//set type so ICC and RGB uses ARGB
if ((image_type == 0)) {
image_type = BufferedImage.TYPE_INT_ARGB; //
} else if ((image_type == BufferedImage.TYPE_INT_RGB)) {
image_type = BufferedImage.TYPE_INT_ARGB; //
}
//draw image onto graphic (with clip) and then re-extract
final BufferedImage offscreen =
new BufferedImage(image_w, image_h, image_type);
//image of 'canvas'
final Graphics2D image_g2 = offscreen.createGraphics(); //g2 of canvas
//if not transparent make background white
if (!offscreen.getColorModel().hasAlpha()) {
image_g2.setBackground(Color.white);
image_g2.fill(new Rectangle(0, 0, image_w, image_h));
}
image_g2.setClip(final_clip);
try {
//redraw image clipped and extract as rectangular shape
image_g2.drawImage(current_image, 0, 0,null);
} catch (final Exception e) {
LogWriter.writeLog("Exception " + e + " plotting clipping image");
}
//get image (now clipped )
//check for rounding errors
if (y < 0) {
h += y;
y = 0;
}
if (x < 0) {
w += x;
x = 0;
}
if (w > image_w) {
w = image_w;
}
if (h > image_h) {
h = image_h;
}
if (y + h > image_h) {
h = image_h - y;
}
if (x + w > image_w) {
w = image_w - x;
}
try {
current_image = offscreen.getSubimage((int)x, (int)y, (int)(w), (int)(h));
} catch (final Exception e) {
LogWriter.writeLog("Exception " + e + " extracting clipped image with values x="+x+" y="+y+" w="+w+" h="+h+" from image ");
}
//work out new co-ords from shape and current
final double x1;
final double y1;
if (i_x > shape_x) {
x1 = i_x;
} else {
x1 = shape_x;
}
if (i_y > shape_y) {
y1 = i_y;
} else {
y1 = shape_y;
}
i_x = (int) (x1);
i_y = (int) (y1);
i_w = (int) w;
i_h = (int) h;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy