All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jpedal.objects.SwingShape Maven / Gradle / Ivy

There is a newer version: 20151002
Show newest version
/*
 * ===========================================
 * 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


 *
 * ---------------
 * SwingShape.java
 * ---------------
 */
package org.jpedal.objects;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.GeneralPath;
import java.io.Serializable;

import org.jpedal.parser.Cmd;
import org.jpedal.utils.LogWriter;
import org.jpedal.utils.repositories.Vector_Float;
import org.jpedal.utils.repositories.Vector_Int;

/**
 * 

* defines the current shape which is created by command stream *

*

* This class is NOT part of the API *

. Shapes can be drawn onto pdf or used as a clip on other * image/shape/text. Shape is built up by storing commands and then turning * these commands into a shape. Has to be done this way as Winding rule is not * necessarily declared at start. */ public class SwingShape implements Serializable, PdfShape { /**used to stop lots of huge, complex shapes. * Note we DO NOT reset as we reuse this object and * it stores cumulative count */ int complexClipCount; private static final int initSize=1000; /**we tell user we have not used some shapes only ONCE*/ private final Vector_Float shape_primitive_x2 = new Vector_Float( initSize ); private final Vector_Float shape_primitive_y = new Vector_Float( initSize ); /**store shape currently being assembled*/ private final Vector_Int shape_primitives = new Vector_Int( initSize ); /**type of winding rule used to draw shape*/ private int winding_rule = GeneralPath.WIND_NON_ZERO; private final Vector_Float shape_primitive_x3 = new Vector_Float( initSize ); private final Vector_Float shape_primitive_y3 = new Vector_Float( initSize ); /**used when trying to choose which shapes to use to test furniture*/ private final Vector_Float shape_primitive_y2 = new Vector_Float( initSize ); private final Vector_Float shape_primitive_x = new Vector_Float( initSize ); private static final int H = 3; private static final int L = 2; private static final int V = 6; /**flags for commands used*/ private static final int M = 1; private static final int Y = 4; private static final int C = 5; /**flag to show if image is for clip*/ private boolean isClip; /**flag to show if S.java needs to adjust lineWidth because we have modified the shape*/ private boolean adjustLineWidth; @Override public boolean isClosed() { return isClosed; } private boolean isClosed; ///////////////////////////////////////////////////////////////////////// /** * end a shape, storing info for later */ @Override public final void closeShape() { shape_primitives.addElement( H ); //add empty values shape_primitive_x.addElement( 0 ); shape_primitive_y.addElement( 0 ); shape_primitive_x2.addElement( 0 ); shape_primitive_y2.addElement( 0 ); shape_primitive_x3.addElement( 0 ); shape_primitive_y3.addElement( 0 ); } ////////////////////////////////////////////////////////////////////////// /** * add a curve to the shape */ @Override public final void addBezierCurveC( final float x, final float y, final float x2, final float y2, final float x3, final float y3 ) { shape_primitives.addElement( C ); shape_primitive_x.addElement( x ); shape_primitive_y.addElement( y ); //add empty values to keep in sync //add empty values shape_primitive_x2.addElement( x2 ); shape_primitive_y2.addElement( y2 ); shape_primitive_x3.addElement( x3 ); shape_primitive_y3.addElement( y3 ); } ////////////////////////////////////////////////////////////////////////// /** * set winding rule - non zero */ @Override public final void setNONZEROWindingRule() { winding_rule = GeneralPath.WIND_NON_ZERO; } ////////////////////////////////////////////////////////////////////////// /** * add a line to the shape */ @Override public final void lineTo( final float x, final float y ) { shape_primitives.addElement( L ); shape_primitive_x.addElement( x ); shape_primitive_y.addElement( y ); //add empty values to keep in sync //add empty values shape_primitive_x2.addElement( 0 ); shape_primitive_y2.addElement( 0 ); shape_primitive_x3.addElement( 0 ); shape_primitive_y3.addElement( 0 ); } /////////////////////////////////////////////////////////////////////////// /** * add a curve to the shape */ @Override public final void addBezierCurveV( final float x2, final float y2, final float x3, final float y3 ) { shape_primitives.addElement( V ); shape_primitive_x.addElement( 200 ); shape_primitive_y.addElement( 200 ); //add empty values to keep in sync //add empty values shape_primitive_x2.addElement( x2 ); shape_primitive_y2.addElement( y2 ); shape_primitive_x3.addElement( x3 ); shape_primitive_y3.addElement( y3 ); } ////////////////////////////////////////////////////////////////////////// /** * turn shape commands into a Shape object, storing info for later. Has to * be done this way because we need the winding rule to initialise the shape * in Java, and it could be set anywhere in the command stream */ @Override public final Shape generateShapeFromPath( final float[][] CTM, final float thickness, final int cmd, final int type){ isClosed=false; //will be set to true if closed boolean is_clip=this.isClip; if(cmd==Cmd.n) { is_clip = false; } //create the shape - we have to do it this way //because we get the WINDING RULE last and we need it //to initialise the shape GeneralPath current_path = null; Area current_area = null; final Shape current_shape; adjustLineWidth = false; //init points final float[] x = shape_primitive_x.get(); final float[] y = shape_primitive_y.get(); final float[] x2 = shape_primitive_x2.get(); final float[] y2 = shape_primitive_y2.get(); final float[] x3 = shape_primitive_x3.get(); final float[] y3 = shape_primitive_y3.get(); final int[] command=shape_primitives.get(); //float lx=0,ly=0; //float xs=0,ys=0; final int end = shape_primitives.size() - 1; //code to fix rounding issue in clipping if rect and boundary just over 0.5 //tweaked for abacus/Zebra_als_PDF_OK.pdf if(end==6 && cmd== Cmd.B && thickness>=0.9f){ for(int aa=0;aa<8;aa++){ final float diff=x[aa]-(int)x[aa]; if(diff>0.5f){ x[aa]=(int)x[aa]-1f; } } } //used to debug final boolean show = false; final boolean debug= false; //loop through commands and add to shape for(int i = 0; i < end; i++){ if( current_path == null ){ current_path = new GeneralPath( winding_rule ); current_path.moveTo( x[i], y[i] ); //lx=x[i]; //ly=y[i]; //xs=lx; //ys=ly; if( show) { LogWriter.writeLog("==START=" + x[i] + ' ' + y[i]); } } //only used to create clips if(command[i]== H){ isClosed=true; current_path.closePath(); if(is_clip){ //current_path.lineTo(xs,ys); //current_path.closePath(); if(show) { LogWriter.writeLog("==H\n\n" + current_area + ' ' + current_path.getBounds2D() + ' ' + new Area(current_path).getBounds2D()); } if( current_area == null ){ current_area = new Area( current_path ); //trap for apparent bug in Java where small paths create a 0 size Area if((current_area.getBounds2D().getWidth()<=0.0)|| (current_area.getBounds2D().getHeight()<=0.0)) { current_area = new Area(current_path.getBounds2D()); } }else { current_area.add(new Area(current_path)); } current_path = null; }else{ if(show) { LogWriter.writeLog("close shape " + command[i] + " i=" + i); } } } if( command[i]== L ){ current_path.lineTo( x[i], y[i] ); //lx=x[i]; //ly=y[i]; if(show) { LogWriter.writeLog("==L" + x[i] + ',' + y[i] + " "); } }else if( command[i] == M ){ current_path.moveTo( x[i], y[i] ); //lx=x[i]; //ly=y[i]; if(show) { LogWriter.writeLog("==M" + x[i] + ',' + y[i] + " "); } }else{ //cubic curves which use 2 control points if( command[i] == Y ){ if(show) { LogWriter.writeLog("==Y " + x[i] + ' ' + y[i] + ' ' + x3[i] + ' ' + y3[i] + ' ' + x3[i] + ' ' + y3[i]); } current_path.curveTo( x[i], y[i], x3[i], y3[i], x3[i], y3[i] ); //lx=x3[i]; //ly=y3[i]; }else if( command[i] == C ){ if(show) { LogWriter.writeLog("==C " + x[i] + ' ' + y[i] + ' ' + x2[i] + ' ' + y2[i] + ' ' + x3[i] + ' ' + y3[i]); } current_path.curveTo( x[i], y[i], x2[i], y2[i], x3[i], y3[i] ); //lx=x3[i]; //ly=y3[i]; }else if( command[i] == V ){ final float c_x = (float)current_path.getCurrentPoint().getX(); final float c_y = (float)current_path.getCurrentPoint().getY(); if(show) { LogWriter.writeLog("==v " + c_x + ',' + c_y + ',' + x2[i] + ',' + y2[i] + ',' + x3[i] + ',' + y3[i]); } current_path.curveTo( c_x, c_y, x2[i], y2[i], x3[i], y3[i] ); //lx=x3[i]; //ly=y3[i]; } } if(debug){ try{ final java.awt.image.BufferedImage img=new java.awt.image.BufferedImage(700,700, java.awt.image.BufferedImage.TYPE_INT_ARGB); final Graphics2D gg2= img.createGraphics(); gg2.setPaint(Color.RED); gg2.translate(current_path.getBounds().width+10,current_path.getBounds().height+10); gg2.draw(current_path); org.jpedal.gui.ShowGUIMessage.showGUIMessage("path",img,"path "+current_path.getBounds()); }catch(final Exception e){ LogWriter.writeLog("Exception: " + e.getMessage()); } } } //second part hack for artus file with thin line if((current_path!=null)&&(current_path.getBounds().getHeight()==0 || (thickness>0.8 && thickness<0.9 && current_path.getBounds2D().getHeight()<0.1f))){ if(current_path.getBounds2D().getWidth()==0 && current_path.getBounds2D().getHeight()==0){ //ignore this case }else if(thickness>1 && current_path.getBounds2D().getWidth()<=1){ //make <1 into <=1 current_path.moveTo(0,-thickness/2); current_path.lineTo(0,thickness/2); adjustLineWidth = true;//If we are doing this, then we need to remove the large lineWidth that's set. (In S.java) }else { current_path.moveTo(0, 1);//@Mark 13908 } } if((current_path!=null)&&(current_path.getBounds().getWidth()==0)) { current_path.moveTo(1, 0);//@Mark 13908 } //transform matrix only if needed if((CTM[0][0] == (float)1.0)&&(CTM[1][0] == (float)0.0)&& (CTM[2][0] == (float)0.0)&&(CTM[0][1] == (float)0.0)&& (CTM[1][1] == (float)1.0)&&(CTM[2][1] == (float)0.0)&& (CTM[0][2] == (float)0.0)&&(CTM[1][2] == (float)0.0)&&(CTM[2][2] == (float)1.0)){ //don't waste time if not needed }else{ final AffineTransform CTM_transform = new AffineTransform( CTM[0][0], CTM[0][1], CTM[1][0], CTM[1][1], CTM[2][0], CTM[2][1]); //apply CTM alterations if( current_path != null ){ //transform current_path.transform( CTM_transform ); //if(CTM[0][0]==0 && CTM[1][1]==0 && CTM[0][1]<0 && CTM[1][0]>0){ // current_path.transform(AffineTransform.getTranslateInstance(0,current_path.getBounds().height/CTM[0][1])); //System.out.println("transforms "+CTM_transform+" "+current_path.getBounds()); //} }else if( current_area != null ) { current_area.transform(CTM_transform); } } /** * fix for single rotated lines with thickness */ if(current_path!=null && CTM[0][0]==1 && CTM[1][1]==-1 && current_path.getBounds().height==1 && thickness>10 ){ final Rectangle currentBounds=current_path.getBounds(); current_path = new GeneralPath( winding_rule ); current_path.moveTo(currentBounds.x,currentBounds.y-thickness/2); current_path.lineTo(currentBounds.x,currentBounds.y+thickness/2); current_path.closePath(); } //set to current or clip if(!is_clip){ if( current_area == null ) { current_shape = current_path; } else { current_shape = current_area; } }else { current_shape = current_area; } //track complex clips if(cmd==Cmd.n && getSegmentCount()>2500){ complexClipCount++; } return current_shape; } ////////////////////////////////////////////////////////////////////////// /** * add a rectangle to set of shapes */ @Override public final void appendRectangle( final float x, final float y, final float w, final float h ) { moveTo( x, y ); lineTo( x + w, y ); lineTo( x + w, y + h ); lineTo( x, y + h ); lineTo( x, y ); closeShape(); } /** * start a shape by creating a shape object */ @Override public final void moveTo( final float x, final float y ) { shape_primitives.addElement( M ); shape_primitive_x.addElement( x ); shape_primitive_y.addElement( y ); //add empty values shape_primitive_x2.addElement( 0 ); shape_primitive_y2.addElement( 0 ); shape_primitive_x3.addElement( 0 ); shape_primitive_y3.addElement( 0 ); //delete lines for grouping over boxes } /** * add a curve to the shape */ @Override public final void addBezierCurveY( final float x, final float y, final float x3, final float y3 ) { shape_primitives.addElement( Y ); shape_primitive_x.addElement( x ); shape_primitive_y.addElement( y ); //add empty values to keep in sync //add empty values shape_primitive_x2.addElement( 0 ); shape_primitive_y2.addElement( 0 ); shape_primitive_x3.addElement( x3 ); shape_primitive_y3.addElement( y3 ); } /** * reset path to empty */ @Override public final void resetPath() { //reset the store shape_primitives.clear(); shape_primitive_x.clear(); shape_primitive_y.clear(); shape_primitive_x2.clear(); shape_primitive_y2.clear(); shape_primitive_x3.clear(); shape_primitive_y3.clear(); //and reset winding rule winding_rule = GeneralPath.WIND_NON_ZERO; } /////////////////////////////////////////////////////////////////////////// /** * set winding rule - even odd */ @Override public final void setEVENODDWindingRule() { winding_rule = GeneralPath.WIND_EVEN_ODD; } /** * show the shape segments for debugging * static final private void showShape( Shape current_shape ) { PathIterator xx = current_shape.getPathIterator( null ); double[] coords = new double[6]; while( xx.isDone() == false ) { int type = xx.currentSegment( coords ); xx.next(); switch( type ) { case PathIterator.SEG_MOVETO: LogWriter.writeLog( "MoveTo" + coords[0] + ' ' + coords[1] ); if( ( coords[0] == 0 ) & ( coords[1] == 0 ) ) LogWriter.writeLog( "xxx" ); break; case PathIterator.SEG_LINETO: LogWriter.writeLog( "LineTo" + coords[0] + ' ' + coords[1] ); if( ( coords[0] == 0 ) & ( coords[1] == 0 ) ) LogWriter.writeLog( "xxx" ); break; case PathIterator.SEG_CLOSE: LogWriter.writeLog( "CLOSE" ); break; default: LogWriter.writeLog( "Other" + coords[0] + ' ' + coords[1] ); break; } } }/**/ /** * number of segments in current shape (0 if no shape or none) */ @Override public int getSegmentCount() { if( shape_primitives==null) { return 0; } else{ return shape_primitives.size() - 1; } } @Override public void setClip(final boolean b) { this.isClip=b; } @Override public boolean isClip() { return isClip; } @Override public int getComplexClipCount() { return complexClipCount; } /** * Allow S.java to detect that we have modified the shape, and therefore the lineWidth needs to be adjusted. * @return */ @Override public boolean adjustLineWidth() { return adjustLineWidth; } @Override public Object getPath() { return null; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy