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: 7.15.25
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-2017 IDRsolutions and Contributors.
 *
 * This file is part of JPedal/JPDF2HTML5
 *
 @LICENSE@
 *
 * ---------------
 * SwingShape.java
 * ---------------
 */
package org.jpedal.objects;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Shape;
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; ShapeMetrics metrics = new ShapeMetrics(); /** * 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 Shape currentShape; public SwingShape(final Shape currentShape) { this.currentShape = currentShape; } public SwingShape() { } @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) { 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; currentShape = null; //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 && is_clip) { 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) { if (command[i] != M) { continue; } 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()); } } } //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) { currentShape = current_path; } else { currentShape = current_area; } } else { currentShape = current_area; } //track complex clips if (cmd == Cmd.n && getSegmentCount() > 2500) { complexClipCount++; } return currentShape; } ////////////////////////////////////////////////////////////////////////// /** * 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(); metrics.incrementRectangleCount(); } /** * 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; this.metrics.setRectangleCount(0); } /////////////////////////////////////////////////////////////////////////// /** * set winding rule - even odd */ @Override public final void setEVENODDWindingRule() { winding_rule = GeneralPath.WIND_EVEN_ODD; } /** * 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; } @Override public Object getPath() { return null; } @Override public void setShape(final Shape currentShape) { this.currentShape = currentShape; } @Override public Shape getShape() { return currentShape; } public ShapeMetrics getMetrics() { return metrics; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy