diff --git a/.gitignore b/.gitignore index 2bf24c098..9b40cc14a 100644 --- a/.gitignore +++ b/.gitignore @@ -21,4 +21,5 @@ /libsrc/jsyntaxpane/jsyntaxpane/src/target/ hs_err_pid*.log *.~* -/revision.txt \ No newline at end of file +/revision.txt +/libsrc/gnujpdf/dist/ \ No newline at end of file diff --git a/lib/gnujpdf.jar b/lib/gnujpdf.jar new file mode 100644 index 000000000..bc765e18c Binary files /dev/null and b/lib/gnujpdf.jar differ diff --git a/libsrc/gnujpdf/build.xml b/libsrc/gnujpdf/build.xml new file mode 100644 index 000000000..129873d08 --- /dev/null +++ b/libsrc/gnujpdf/build.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + Builds, tests, and runs the project gnujpdf. + + + diff --git a/libsrc/gnujpdf/manifest.mf b/libsrc/gnujpdf/manifest.mf new file mode 100644 index 000000000..1574df4a2 --- /dev/null +++ b/libsrc/gnujpdf/manifest.mf @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +X-COMMENT: Main-Class will be added automatically by build + diff --git a/libsrc/gnujpdf/nbproject/build-impl.xml b/libsrc/gnujpdf/nbproject/build-impl.xml new file mode 100644 index 000000000..37c89a9a2 --- /dev/null +++ b/libsrc/gnujpdf/nbproject/build-impl.xml @@ -0,0 +1,1413 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set src.dir + Must set test.src.dir + Must set build.dir + Must set dist.dir + Must set build.classes.dir + Must set dist.javadoc.dir + Must set build.test.classes.dir + Must set build.test.results.dir + Must set build.classes.excludes + Must set dist.jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No tests executed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set JVM to use for profiling in profiler.info.jvm + Must set profiler agent JVM arguments in profiler.info.jvmargs.agent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + java -jar "${dist.jar.resolved}" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + Must select one file in the IDE or set profile.class + This target only works when run from inside the NetBeans IDE. + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + + + Must select some files in the IDE or set test.includes + + + + + Must select one file in the IDE or set run.class + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + Must select some files in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + Must select one file in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + + + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libsrc/gnujpdf/nbproject/genfiles.properties b/libsrc/gnujpdf/nbproject/genfiles.properties new file mode 100644 index 000000000..f953a5337 --- /dev/null +++ b/libsrc/gnujpdf/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +build.xml.data.CRC32=fb275268 +build.xml.script.CRC32=7438dbfb +build.xml.stylesheet.CRC32=8064a381@1.74.2.48 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=fb275268 +nbproject/build-impl.xml.script.CRC32=1ec9d904 +nbproject/build-impl.xml.stylesheet.CRC32=876e7a8f@1.74.2.48 diff --git a/libsrc/gnujpdf/nbproject/project.properties b/libsrc/gnujpdf/nbproject/project.properties new file mode 100644 index 000000000..03e8ba9ed --- /dev/null +++ b/libsrc/gnujpdf/nbproject/project.properties @@ -0,0 +1,75 @@ +annotation.processing.enabled=true +annotation.processing.enabled.in.editor=false +annotation.processing.processors.list= +annotation.processing.run.all.processors=true +annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output +application.title=gnujpdf +application.vendor=Jindra +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.dir=${build.dir}/generated +build.generated.sources.dir=${build.dir}/generated-sources +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.results.dir=${build.dir}/test/results +# Uncomment to specify the preferred debugger connection transport: +#debug.transport=dt_socket +debug.classpath=\ + ${run.classpath} +debug.test.classpath=\ + ${run.test.classpath} +# Files in build.classes.dir which should be excluded from distribution jar +dist.archive.excludes= +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.jar=../../lib/gnujpdf.jar +dist.javadoc.dir=${dist.dir}/javadoc +endorsed.classpath= +excludes= +includes=** +jar.compress=false +javac.classpath= +# Space-separated list of extra javac options +javac.compilerargs= +javac.deprecation=false +javac.processorpath=\ + ${javac.classpath} +javac.source=1.7 +javac.target=1.7 +javac.test.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +javac.test.processorpath=\ + ${javac.test.classpath} +javadoc.additionalparam= +javadoc.author=false +javadoc.encoding=${source.encoding} +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +main.class=gnu.jpdf.Test +manifest.file=manifest.mf +meta.inf.dir=${src.dir}/META-INF +mkdist.disabled=false +platform.active=default_platform +run.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +# Space-separated list of JVM arguments used when running the project. +# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value. +# To set system properties for unit tests define test-sys-prop.name=value: +run.jvmargs= +run.test.classpath=\ + ${javac.test.classpath}:\ + ${build.test.classes.dir} +source.encoding=UTF-8 +src.dir=src +test.src.dir=test diff --git a/libsrc/gnujpdf/nbproject/project.xml b/libsrc/gnujpdf/nbproject/project.xml new file mode 100644 index 000000000..d93419e09 --- /dev/null +++ b/libsrc/gnujpdf/nbproject/project.xml @@ -0,0 +1,15 @@ + + + org.netbeans.modules.java.j2seproject + + + gnujpdf + + + + + + + + + diff --git a/libsrc/gnujpdf/src/gnu/jpdf/BoundingBox.java b/libsrc/gnujpdf/src/gnu/jpdf/BoundingBox.java new file mode 100644 index 000000000..aebd23c3e --- /dev/null +++ b/libsrc/gnujpdf/src/gnu/jpdf/BoundingBox.java @@ -0,0 +1,1025 @@ +/* + * $Id: BoundingBox.java,v 1.2 2007/08/26 18:56:35 gil1 Exp $ + * + * $Date: 2007/08/26 18:56:35 $ + * + * Copyright (c) Eric Z. Beard, ericzbeard@hotmail.com + * + * 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 + * + */ +package gnu.jpdf; + +import java.awt.*; +import java.util.*; + +/** + *

This class simplifies the placement of Strings within + * a canvas area where the placement of objects is absolute

+ * + *

A BoundingBox is just a Rectangle that knows how to + * find the coordinates for a String based on the desired alignment and + * FontMetrics. For each new String, a new child + * BoundingBox is made that can be subtracted from the + * original box so new Strings can be added

+ * + *

One of the more helpful features of this class is the string wrap + * feature of getStringBounds. The box returned by that method + * will contain an array of strings that have been broken down to fit the + * box. The box's coordinates and size will reflect the size of the + * entire group of strings if it is laid out as expected. Using the + * returned box and iterating through the array of strings from top to + * bottom, getting new bounding boxes for each one (with upper left + * alignment and no padding) will result in the correct string wrap.

+ * + *

Note that you will need to have Xvfb running on a Unix server to + * use this class

+ * + * @author Eric Z. Beard, ericzbeard@hotmail.com + * @version $Revision: 1.2 $, $Date: 2007/08/26 18:56:35 $ + */ +public class BoundingBox extends Rectangle +{ + /** Percent f line height to space lines */ + public static final int LINE_SPACING_PERCENTAGE = 20; + + /** Used to a align a String centered vertically */ + public static final int VERT_ALIGN_CENTER = 0; + + /** Used to align a String at the top of the box */ + public static final int VERT_ALIGN_TOP = 1; + + /** Used to align a String at the bottom of the box */ + public static final int VERT_ALIGN_BOTTOM = 2; + + /** Used to align a String horizontally in the center of the box */ + public static final int HORIZ_ALIGN_CENTER = 3; + + /** Used to align a String to the left in the box */ + public static final int HORIZ_ALIGN_LEFT = 4; + + /** Used to aling a String to the right in a box */ + public static final int HORIZ_ALIGN_RIGHT = 5; + + /** Used to subtract a child from a box, *leaving* the top portion */ + public static final int SUBTRACT_FROM_TOP = 6; + + /** Used to subtract a child from a box, *leaving* the bottom portion */ + public static final int SUBTRACT_FROM_BOTTOM = 7; + + /** Used to subtract a child from a box, *leaving* the left portion */ + public static final int SUBTRACT_FROM_LEFT = 8; + + /** Used to subtract a child from a box, *leaving" the right portion */ + public static final int SUBTRACT_FROM_RIGHT = 9; + + private static final int[] VERT_ALIGNS = {VERT_ALIGN_CENTER, + VERT_ALIGN_TOP, + VERT_ALIGN_BOTTOM}; + + private static final int[] HORIZ_ALIGNS = {HORIZ_ALIGN_CENTER, + HORIZ_ALIGN_LEFT, + HORIZ_ALIGN_RIGHT}; + + private static final int[] SUBTRACTS = {SUBTRACT_FROM_TOP, + SUBTRACT_FROM_BOTTOM, + SUBTRACT_FROM_LEFT, + SUBTRACT_FROM_RIGHT}; + + /** The point to use for Graphics.drawString() */ + private Point drawingPoint; + + /** The absolute, world location of the box */ + private Point absoluteLocation; + + /** Link to parent box */ + private BoundingBox parent; + + /** + * If this box was the result of a getStringBounds call, this + * array will hold the broken strings + */ + private String[] stringArray; + + /** The string specified in getStringBounds */ + private String fullString; + + /** + * Creates a new BoundingBox instance. + * + * @param p a Point, upper left coords + * @param d a Dimension, used to determine height and width + */ + public BoundingBox(Point p, Dimension d) { + super(p, d); + this.drawingPoint = this.getLocation(); + this.absoluteLocation = this.getLocation(); + } + + + + /** + *

Returns true if this box has a parent. The 'world', or + * enclosing canvas is not considered a parent

+ * + * @return a boolean value + */ + public boolean hasParent() { + return parent != null; + } + + /** + *

Get this box's parent box

+ * + * @return a BoundingBox value + */ + public BoundingBox getParent() { + return parent; + } + + + + /** + *

Make the specified box this box's child. Equivalent to + * child.setParent(parent) where the specified 'parent' is + * this instance

+ * + * @param child a BoundingBox, any box that can fit inside + * this one. The results of calling + * getAbsoluteLocation() on the child will be + * altered after this to take into account the child's + * new location in the 'world' + * + */ + public void add(BoundingBox child) { + child.setParent(this); + } + + + + /** + *

Make the specified box this box's parent

+ * + * @param parent a BoundingBox value + */ + public void setParent(BoundingBox parent) { + // Prevent infinite recursion + if (this == parent) { + return; + } + this.parent = parent; + + // If this box was created empty, without a String inside, + // determine its absolute location + if (this.getLocation().equals(this.getAbsoluteLocation())) { + int ancestorTranslateX = 0; + int ancestorTranslateY = 0; + + BoundingBox ancestor = this; + while (ancestor.hasParent()) { + BoundingBox oldRef = ancestor; + ancestor = ancestor.getParent(); + // Prevent infinite recursion + if (ancestor == oldRef) { + break; + } + ancestorTranslateX += (int)ancestor.getLocation().getX(); + ancestorTranslateY += (int)ancestor.getLocation().getY(); + } + + this.getAbsoluteLocation().translate(ancestorTranslateX, + ancestorTranslateY); + } // end if + } // end setParent + + + + + + /** + *

Get the wrapped strings if this box was from a call to getStringBounds, + * otherwise this method returns null

+ * + * @return a String[] array of strings, top to bottom in layout + */ + public String[] getStringArray() { + return stringArray; + } // end getStringArray + + + + /** + *

Set the value of the string array

+ * + * @param strArray a String array + * + */ + public void setStringArray(String[] strArray) { + this.stringArray = strArray; + } + + + /** + *

Set the absolute upper left world location point for this box

+ * + * @param point a Point value + */ + public void setAbsoluteLocation(Point point) { + this.absoluteLocation = point; + } + + + + /** + *

Returns false if for any reason this box has negative dimensions

+ * + * @return true if the box has positive height and width, false otherwise. + */ + public boolean boxExists() { + return (this.getHeight() > 0 && this.getWidth() > 0); + } // end boxExists + + + + + /** + *

Get the absolute upper left location point for this box

+ * + * @return a Point value + */ + public Point getAbsoluteLocation() { + return absoluteLocation; + } + + + /** + *

Returns the full string associated with a call to + * getStringBounds

+ * + * @return the full string. + */ + public String getFullString() { + return fullString; + } + + + /** + *

Sets the full string associated with getStringBounds

+ * + * @param string a String + */ + public void setFullString(String string) { + this.fullString = string; + } + + /** + *

Gets the location of a String after it is adjusted for + * alignment within this box. The point's coordinates are + * either within this box or within the enclosing area.

+ * + * @param string a String, the String to be placed + * @param hAlign an int, HORIZ_ALIGN_CENTER, + * HORIZ_ALIGN_LEFT, HORIX_ALIGN_RIGHT + * @param vAlign an int, VERT_ALIGN_CENTER, + * VERT_ALIGN_TOP, VERT_ALIGN_BOTTOM + * @param fm a FontMetrics object for this String + * @param padding an int, the padding around the String + * @param enforce a boolean, if true the method will throw + * an exception when the string is too big, if not true it will break + * the string down and overrun the bottom of the box. If the box + * is too small for even one word, the exception will be thrown + * @return a Point, the coords to use in drawString() + * @see #HORIZ_ALIGN_LEFT + * @see #HORIZ_ALIGN_CENTER + * @see #HORIZ_ALIGN_RIGHT + * @see #VERT_ALIGN_TOP + * @see #VERT_ALIGN_CENTER + * @see #VERT_ALIGN_BOTTOM + * @throws IllegalArgumentException if the args are invalid + * @throws StringTooLongException if the string won't fit + * and enforce is set to true. The exception can still be thrown + * if enforce is false, but only in cases such as the box having + * no height or width + */ + public BoundingBox getStringBounds(String string, + int hAlign, + int vAlign, + FontMetrics fm, + int padding, + boolean enforce) + throws IllegalArgumentException, StringTooLongException { + // Check to make sure the values passed in are valid + if (!checkHAlign(hAlign)) { + throw new IllegalArgumentException("BoundingBox.getStringBounds, " + + "hAlign invalid : " + hAlign); + } + if (!checkVAlign(vAlign)) { + throw new IllegalArgumentException("BoundingBox.getStringBounds, " + + "vAlign invalid : " + hAlign); + } + if (fm == null) { + throw new IllegalArgumentException("BoundingBox.getStringBounds, " + + "FontMetrics null"); + } + if (string == null) { + throw new IllegalArgumentException("BoundingBox.getStringBounds, " + + "String null"); + } + + // NOTE: For this portion of the method, parent refers + // to this object and child refers to the object about + // to be created. When the absolute point for drawing the + // String is determined, this object's ancestors are checked. + Dimension parentSize = this.getSize(); + + Point childLocation; + Dimension childSize; + + // String ascent, width, height, parent, child width, height + int sa, sw, sh, pw, ph, cw, ch; + + // Child, parent x, y coords for upper left + int cx, cy, px, py; + + sa = fm.getMaxAscent(); + sw = fm.stringWidth(string); + sh = sa + fm.getMaxDescent(); + pw = (int)parentSize.getWidth(); + ph = (int)parentSize.getHeight(); + if (pw < 0) { + throw new StringTooLongException("The parent box has a negative width " + + " (" + pw + ")"); + } + if (ph < 0) { + throw new StringTooLongException("The parent box has a negative height"+ + " (" + ph + ")"); + } + cw = sw + padding*2; + ch = sh + padding*2; + px = (int)this.getX(); + py = (int)this.getY(); + + String[] childStrArray = null; + + if ((cw > pw) || (string.indexOf("\n") != -1)) { + cw = pw - (padding * 2); + childStrArray = createStringArray(string, fm, padding, pw); + ch = getWrappedHeight(childStrArray, fm, padding); + if (ch > ph) { + // If enforce is not true, it means we want the box to + // be returned anyway (along with the strings in the array) + // so we can chop them manually and try again + if (enforce) { + throw new StringTooLongException("The wrapped strings do not " + + "fit into the parent box, pw=" + pw + + ", ph=" + ph + ", ch=" + ch + ", cw=" + cw + + ", string: " + string); + } + } + } + + // Need to have child width and height, and string array set + + // Child location is relative to this (parent) box, not the world + if (vAlign == VERT_ALIGN_TOP) { + cy = 0; + } + else if (vAlign == VERT_ALIGN_CENTER) { + cy = (ph/2) - (ch/2); + } + else { + cy = ph - ch; + } + + if (hAlign == HORIZ_ALIGN_LEFT) { + cx = 0; + } + else if (hAlign == HORIZ_ALIGN_CENTER) { + cx = (pw/2) - (cw/2); + } + else { + cx = pw - cw; + } + + childLocation = new Point(cx, cy); + childSize = new Dimension(cw, ch); + + // Drawing location is based on the baseline of the String, and + // relative to the world, not this box. The drawing point differs + // from the absolute box location because of padding and ascent + int dpx, dpy, abx, aby; + + // If this object also has a parent (maybe grandparents), iterate + // through them and find the absolute 'world' location + int ancestorTranslateX = 0; + int ancestorTranslateY = 0; + + BoundingBox ancestor = this; + while (ancestor.hasParent()) { + BoundingBox oldRef = ancestor; + ancestor = ancestor.getParent(); + // Prevent infinite recursion + if (ancestor == oldRef) { + break; + } + ancestorTranslateX += (int)ancestor.getLocation().getX(); + ancestorTranslateY += (int)ancestor.getLocation().getY(); + } + + // Determine the absolute location for the box + abx = px + cx + ancestorTranslateX; + aby = py + cy + ancestorTranslateY; + + // Determine the absolute drawing point for the String + dpx = abx + padding; + dpy = aby + padding + sa; + + Point drawingPoint = new Point(dpx, dpy); + BoundingBox returnChild = new BoundingBox(childLocation, + childSize, + drawingPoint, + new Point(abx, aby)); + this.add(returnChild); + returnChild.setFullString(string); + returnChild.setStringArray(childStrArray); + return returnChild; + + } // end getStringBounds + + + /** + *

Gets the location of a String after it is adjusted for + * alignment within this box. The point's coordinates are + * either within this box or within the enclosing area.

+ * + *

By default, this method enforces string length and throws the + * exception if it is too long

+ * + * @param string a String, the String to be placed + * @param hAlign an int, HORIZ_ALIGN_CENTER, + * HORIZ_ALIGN_LEFT, HORIX_ALIGN_RIGHT + * @param vAlign an int, VERT_ALIGN_CENTER, + * VERT_ALIGN_TOP, VERT_ALIGN_BOTTOM + * @param fm a FontMetrics object for this String + * @param padding an int, the padding around the String + * @return a Point, the coords to use in drawString() + * @throws IllegalArgumentException if the args are invalid + * @throws StringTooLongException if the string won't fit + */ + public BoundingBox getStringBounds(String string, + int hAlign, + int vAlign, + FontMetrics fm, + int padding) + throws StringTooLongException, IllegalArgumentException { + return getStringBounds(string, hAlign, vAlign, fm, padding, true); + } // end getStringBounds (enforce true by default) + + + + /** + *

This method is called after getting the box by calling + * getStringBounds on the parent. Wraps the string at + * word boundaries and draws it to the specified Graphics + * context. Make sure padding is the same as specified for the + * getStringBounds call, or you may get an unexpected + * {@link gnu.jpdf.StringTooLongException}

+ * + * @param g the Graphics object + * @param fm the FontMetrics to use for sizing + * @param padding an int, the padding around the strings + * @param hAlign the int horizontal alignment + * @throws IllegalArgumentException if the args are invalid + * @throws StringTooLongException if the string + * won't fit this will only happen if the fm or padding has + * been changed since getStringBounds was called succesfully + */ + public void drawWrappedString(Graphics g, + FontMetrics fm, + int padding, + int hAlign) + throws IllegalArgumentException, StringTooLongException { + if (getStringArray() == null) { + Point p = getDrawingPoint(); + int xx = (int)p.getX(); + int yy = (int)p.getY(); + g.drawString(getFullString(), xx, yy); + } + else { + int len = stringArray.length; + for (int i = 0; i < len; i++) { + BoundingBox wrappedBox = null; + wrappedBox = getStringBounds(stringArray[i], + hAlign, + BoundingBox.VERT_ALIGN_TOP, + fm, + 0); + Point pp = wrappedBox.getDrawingPoint(); + int xx = (int)pp.getX(); + if (hAlign == BoundingBox.HORIZ_ALIGN_RIGHT) { + xx -= padding; + } + if (hAlign == BoundingBox.HORIZ_ALIGN_LEFT) { + xx += padding; + } + int yy = (int)pp.getY() + padding; + g.drawString(stringArray[i], xx, yy); + subtract(wrappedBox, BoundingBox.SUBTRACT_FROM_BOTTOM); + } + } + } // end drawWrappedString + + + /** + *

Draws lines from the wrapped string until there is no more room and + * then stops. If there is no string or the box is too small for + * anything to be drawn, does nothing

+ * + * @param g the Graphics object to draw to + * @param fm the FontMetrics object to use for string sizing + * @param padding the int amount of padding around the string + * @param hAlign the int horizontal alignment + * + */ + public void drawWrappedStringTruncate(Graphics g, + FontMetrics fm, + int padding, + int hAlign) { + + if (getStringArray() == null) { + Point p = getDrawingPoint(); + int xx = (int)p.getX(); + int yy = (int)p.getY(); + if (getFullString() != null) { + g.drawString(getFullString(), xx, yy); + } + else { + System.err.println("getStringArray and getFullString are null"); + } + } + else { + int totalHeight = 0; + int len = stringArray.length; + for (int i = 0; i < len; i++) { + BoundingBox wrappedBox = null; + try { + wrappedBox = getStringBounds(stringArray[i], + hAlign, + BoundingBox.VERT_ALIGN_TOP, + fm, + 0, + false); + totalHeight += (int)wrappedBox.getHeight(); + if (getParent() != null) { + if (totalHeight > (int)(getParent().getHeight())) { + return; + } + } + } + catch (StringTooLongException stle) { + stle.printStackTrace(); + return; + } + wrappedBox.drawChoppedString(g, fm, padding, hAlign); + subtract(wrappedBox, BoundingBox.SUBTRACT_FROM_BOTTOM); + } + } + } // end drawWrappedStringTruncate + + + /** + *

Take the first line of the string (if it is wrapped, otherwise just + * take the whole string) and chop the end of it off to make it fit in the + * box. If the box is smaller than one letter, draw nothing

+ * + * @param g the Graphics object to draw to + * @param fm the FontMetrics object to use for string sizing + * @param padding the int amount of padding around the string + * @param hAlign the int horizontal alignment + */ + public void drawChoppedString(Graphics g, + FontMetrics fm, + int padding, + int hAlign) { + + String string = ""; + if (getStringArray() != null) { + string = new String(getStringArray()[0]); + } + else { + string = new String(getFullString()); + } + BoundingBox choppedBox = null; + try { + choppedBox = getStringBounds(string, + hAlign, + VERT_ALIGN_TOP, + fm, + padding); + Point p = choppedBox.getDrawingPoint(); + int x = (int)p.getX(); + int y = (int)p.getY(); + g.drawString(string, x, y); + } + catch (StringTooLongException stle) { + // Doesn't fit - start cutting from the end until it does + StringBuffer buf = new StringBuffer().append(string); + if (buf.length() == 0) { + System.out.println("BoundingBox.drawChoppedString, buf len 0 ??"); + //return; + throw new RuntimeException(); + } + buf.deleteCharAt(buf.length()-1); + while ((fm.stringWidth(buf.toString()) > (int)getWidth()) && + (buf.length() > 0)) { + buf.deleteCharAt(buf.length()-1); + } + + try { + choppedBox = getStringBounds(buf.toString(), + hAlign, + VERT_ALIGN_TOP, + fm, + padding); + Point pp = choppedBox.getDrawingPoint(); + int xx = (int)pp.getX(); + int yy = (int)pp.getY(); + g.drawString(string, xx, yy); + } + catch (StringTooLongException sstle) { + // Must be a really small box! + sstle.printStackTrace(); + } + } + } // end drawChoppedString + + + + + + + + + + /** + *

Get the total height of the box needed to contain the strings in + * the specified array

+ */ + private int getWrappedHeight(String[] strings, FontMetrics fm, int padding) { + int ma = fm.getMaxAscent(); + int md = fm.getMaxDescent(); + int sh = ma + md; + int hPad = sh / LINE_SPACING_PERCENTAGE; + sh += hPad; + int total = sh * strings.length; + + return total + (padding*2); + } // end getWrappedHeight + + + + /** + * + *

Make a string array from a string, wrapped to fit the box

+ * + *

If the line width is too short, the array is just a + * tokenized version of the string

+ * + * @param string - the String to convert to an array + * @param + */ + private String[] createStringArray(String string, + FontMetrics fm, + int padding, + int pw) { + if (string == null) { + System.err.println("Tried createStringArray with null String"); + return null; + } + if (fm == null) { + System.err.println("Tried createStringArray with null FontMetrics"); + } + + int lw = pw - (padding*2); + + + + Vector returnVector = new Vector(); + // Return delimiters as tokens + StringTokenizer st = new StringTokenizer(string, " \t\n\r\f", true); + StringBuffer tempBuffer = new StringBuffer(); + StringBuffer finalBuffer = new StringBuffer(); + + while(st.hasMoreTokens()) { + // Get the next word and add a space after it + String tempString = st.nextToken(); + tempBuffer.append(tempString); + + // If we haven't reached the width with our current + // line, keep adding tokens. Also, check for hard returns + if ((fm.stringWidth(tempBuffer.toString()) < lw) && + (tempBuffer.toString() + .charAt(tempBuffer.toString().length() - 1) != '\n') && + (tempBuffer.toString() + .charAt(tempBuffer.toString().length() - 1) != '\r')) { + finalBuffer.append(tempString); + continue; + } + returnVector.addElement(finalBuffer.toString()); + finalBuffer.delete(0, finalBuffer.length()); + tempBuffer.delete(0, tempBuffer.length()); + if ((tempString.charAt(0) != '\n') && + (tempString.charAt(0) != '\r')) { + tempBuffer.append(tempString); + finalBuffer.append(tempString); + } + continue; + + } // end while + returnVector.addElement(finalBuffer.toString()); + + int len = returnVector.size(); + // Init the class member field stringArray + String[] childStrArray = new String[len]; + for (int i = 0; i < len; i++) { + String curStr = (String)returnVector.get(i); + childStrArray[i] = curStr; + } + + return childStrArray; + + } // end createStringArray + + + + /** + *

Removes the child box from this parent box. The child must + * have this object as its parent or the method does nothing. + * The BoundingBox returned will be cut by an area equal to + * the child area plus the horizontal or vertical strip in + * which it sits, depending on the 'subtractFrom' value passed + * in

+ * + * @param child a BoundingBox value + * @param subtractFrom an int, SUBTRACT_FROM_LEFT, + SUBTRACT_FROM_RIGHT, SUBTRACT_FROM_TOP, + SUBTRACT_FROM_BOTTOM + * @return a BoundingBox value + * @see #SUBTRACT_FROM_LEFT + * @see #SUBTRACT_FROM_RIGHT + * @see #SUBTRACT_FROM_TOP + * @see #SUBTRACT_FROM_BOTTOM + */ + public BoundingBox subtract(BoundingBox child, int subtractFrom) { + // First, check to see if the params are valid + if (child == null) { + throw new IllegalArgumentException("BoundingBox.subtract, " + + "BoundingBox child is null"); + } + if (!child.hasParent()) { + throw new IllegalArgumentException("BoundingBox.subtract, " + + "BoundingBox child has no parent"); + } + if (!(child.getParent() == this)) { + throw new IllegalArgumentException("BoundingBox.subtract, " + + "this is not BoundingBox child's parent"); + } + // Now that we know the child is this object's child, we continue + // and check the subtractFrom param + int len = SUBTRACTS.length; + boolean valid = false; + for (int i = 0; i < len; i++) { + if (subtractFrom == SUBTRACTS[i]) { + valid = true; + } + } + if (!valid) { + throw new IllegalArgumentException("BoundingBox.subtract, " + + "subtractFrom invalid: " + subtractFrom); + } + + // Now we know the child is valid, and if the subtractFrom + // preference was invalid, we subtract from the bottom + + // The child should no longer be used, since the parent + // reference will be invalid + child.setParent(null); + + int cx = (int) child.getLocation().getX(); + int cy = (int) child.getLocation().getY(); + int cw = (int) child.getSize().getWidth(); + int ch = (int) child.getSize().getHeight(); + int px = (int) this.getLocation().getX(); + int py = (int) this.getLocation().getY(); + int pw = (int) this.getSize().getWidth(); + int ph = (int) this.getSize().getHeight(); + + switch (subtractFrom) { + case SUBTRACT_FROM_LEFT: + // This will be useful for right-justified Strings in tables + pw = cx; + this.setSize(new Dimension(pw, ph)); + return this; + + case SUBTRACT_FROM_RIGHT: + // This will be useful for left justified Strings in tables + px = px + cw + cx; + pw = pw - cw - cx; + this.setLocation(new Point(px, py)); + this.setSize(new Dimension(pw, ph)); + return this; + + case SUBTRACT_FROM_BOTTOM: + py = py + ch + cy; + ph = ph - ch - cy; + this.setLocation(new Point(px, py)); + this.setSize(new Dimension(pw, ph)); + return this; + + case SUBTRACT_FROM_TOP: + ph = cy; + this.setSize(new Dimension(pw, ph)); + return this; + + default: // Should never happen + break; + } // end switch + return this; + } // end subtract + + + + + /** + *

+ * Gets the drawing point to use in Graphics drawing methods. After getting + * a new BoundingBox with getStringBounds(), calling this method will give + * you an absolute point, accounting for alignment and padding, etc, from + * which to start drawing the String + *

+ * + *

+ * If getStringBounds was not called (this is a parent box), the upper left + * coordinates will be returned (this.getLocation()) + *

+ * + * @return a Point + */ + public Point getDrawingPoint() { + return drawingPoint; + } + + + + + + // main method is for testing ///////////////// + + + + /** + * For testing + * + * @param args a String[] value + */ + public static void main(String[] args) { + Point upperLeft = new Point(5, 5); + Dimension bounds = new Dimension(100, 100); + BoundingBox parent = new BoundingBox(upperLeft, bounds); + String string = "Hello World!"; + Font font = new Font("SansSerif", Font.PLAIN, 12); + Frame frame = new Frame(); + frame.addNotify(); + try { + Image image = frame.createImage(100, 100); + if (image == null) { + System.err.println("image is null"); + } + Graphics graphics = image.getGraphics(); + FontMetrics fm = graphics.getFontMetrics(font); + BoundingBox child = parent + .getStringBounds(string, + BoundingBox.HORIZ_ALIGN_LEFT, + BoundingBox.VERT_ALIGN_TOP, + fm, + 5); + System.out.println("Drawing Point: " + + child.getDrawingPoint().toString()); + System.out.println("Now testing subtract() method..."); + + parent = new BoundingBox(new Point(10, 10), new Dimension(300, 300)); + System.out.println("parent: " + parent.toString()); + child = new BoundingBox(new Point(90, 110), new Dimension(100, 100)); + parent.add(child); + System.out.println("child: " + child.toString()); + System.out.println(); + System.out.println("subtracting the child from the parent"); + System.out.println("SUBTRACT_FROM_TOP: "); + parent = parent.subtract(child, SUBTRACT_FROM_TOP); + System.out.println("new parent: " + parent.toString()); + System.out.println(); + System.out.println("Resetting parent"); + parent = new BoundingBox(new Point(10, 10), new Dimension(300, 300)); + parent.add(child); + System.out.println("SUBTRACT_FROM_BOTTOM"); + parent.subtract(child, SUBTRACT_FROM_BOTTOM); + System.out.println("new parent: " + parent.toString()); + System.out.println(); + System.out.println("Resetting parent"); + parent = new BoundingBox(new Point(10, 10), new Dimension(300, 300)); + parent.add(child); + System.out.println("SUBTRACT_FROM_LEFT"); + parent.subtract(child, SUBTRACT_FROM_LEFT); + System.out.println("new parent: " + parent.toString()); + System.out.println(); + System.out.println("Resetting parent"); + parent = new BoundingBox(new Point(10, 10), new Dimension(300, 300)); + parent.add(child); + System.out.println("SUBTRACT_FROM_RIGHT"); + parent.subtract(child, SUBTRACT_FROM_RIGHT); + System.out.println("new parent: " + parent.toString()); + System.out.println(); + + + + System.exit(0); + } + catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + } + + + // Private methods ///////////////////////////// + + /** + * Creates a new BoundingBox instance. + * + * @param p a Point value + * @param d a Dimension value + * @param drawingPoint a Point value + */ + private BoundingBox(Point p, + Dimension d, + Point drawingPoint, + Point absolute) { + super(p, d); + this.drawingPoint = drawingPoint; + this.absoluteLocation = absolute; + } + + + /** + *

Checks the horizontal alignment passed into a + * method to make sure it is one of the valid values

+ * + * @param hAlign an int value + * @return a boolean value + */ + private boolean checkHAlign(int hAlign) { + int len = HORIZ_ALIGNS.length; + for (int i = 0; i < len; i++) { + if (hAlign == HORIZ_ALIGNS[i]) { + return true; + } + } + return false; + } + + + + /** + *

Checks the vertical alignment passed into a + * method to make sure it is one of the valid values

+ * + * @param vAlign an int value + * @return a boolean value + */ + private boolean checkVAlign(int vAlign) { + int len = VERT_ALIGNS.length; + for (int i = 0; i < len; i++) { + if (vAlign == VERT_ALIGNS[i]) { + return true; + } + } + return false; + } + +} // end class BoundingBox + + + + diff --git a/libsrc/gnujpdf/src/gnu/jpdf/PDFAnnot.java b/libsrc/gnujpdf/src/gnu/jpdf/PDFAnnot.java new file mode 100644 index 000000000..910bb70cd --- /dev/null +++ b/libsrc/gnujpdf/src/gnu/jpdf/PDFAnnot.java @@ -0,0 +1,272 @@ +/* + * $Id: PDFAnnot.java,v 1.2 2007/08/26 18:56:35 gil1 Exp $ + * + * $Date: 2007/08/26 18:56:35 $ + * + * 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 + */ +package gnu.jpdf; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.Serializable; + +/** + *

This class defines an annotation (commonly known as a Bookmark).

+ * + * @author Eric Z. Beard, ericzbeard@hotmail.com + * @author Peter T Mount, http://www.retep.org.uk/pdf/ + * @version $Revision: 1.2 $, $Date: 2007/08/26 18:56:35 $ + */ +public class PDFAnnot extends PDFObject implements Serializable +{ + /* + * NOTE: The original class is the work of Peter T. Mount, who released it + * in the uk.org.retep.pdf package. It was modified by Eric Z. Beard as + * follows: the package name was changed to gnu.pdf. It is still + * licensed under the LGPL. + */ + + /** + * Solid border. The border is drawn as a solid line. + */ + public static final short SOLID = 0; + + /** + * The border is drawn with a dashed line. + */ + public static final short DASHED = 1; + + /** + * The border is drawn in a beveled style (faux three-dimensional) such + * that it looks as if it is pushed out of the page (opposite of INSET) + */ + public static final short BEVELED = 2; + + /** + * The border is drawn in an inset style (faux three-dimensional) such + * that it looks as if it is inset into the page (opposite of BEVELED) + */ + public static final short INSET = 3; + + /** + * The border is drawn as a line on the bottom of the annotation rectangle + */ + public static final short UNDERLINED = 4; + + /** + * The subtype of the outline, ie text, note, etc + */ + private String subtype; + + /** + * The size of the annotation + */ + private int l,b,r,t; + + /** + * The text of a text annotation + */ + private String s; + + /** + * flag used to indicate that the destination should fit the screen + */ + private static final int FULL_PAGE = -9999; + + /** + * Link to the Destination page + */ + private PDFObject dest; + + /** + * If fl!=FULL_PAGE then this is the region of the destination page shown. + * Otherwise they are ignored. + */ + private int fl,fb,fr,ft; + + /** + * the border for this annotation + */ + private PDFBorder border; + + /** + * This is used to create an annotation. + * @param s Subtype for this annotation + * @param l Left coordinate + * @param b Bottom coordinate + * @param r Right coordinate + * @param t Top coordinate + */ + protected PDFAnnot(String s,int l,int b,int r,int t) { + super("/Annot"); + subtype = s; + this.l = l; + this.b = b; + this.r = r; + this.t = t; + } + + /** + * Creates a text annotation + * @param l Left coordinate + * @param b Bottom coordinate + * @param r Right coordinate + * @param t Top coordinate + * @param s Text for this annotation + */ + public PDFAnnot(int l,int b,int r,int t,String s) { + this("/Text",l,b,r,t); + this.s = s; + } + + /** + * Creates a link annotation + * @param l Left coordinate + * @param b Bottom coordinate + * @param r Right coordinate + * @param t Top coordinate + * @param dest Destination for this link. The page will fit the display. + */ + public PDFAnnot(int l,int b,int r,int t,PDFObject dest) { + this("/Link",l,b,r,t); + this.dest = dest; + this.fl = FULL_PAGE; // this is used to indicate a full page + } + + /** + * Creates a link annotation + * @param l Left coordinate + * @param b Bottom coordinate + * @param r Right coordinate + * @param t Top coordinate + * @param dest Destination for this link + * @param fl Left coordinate + * @param fb Bottom coordinate + * @param fr Right coordinate + * @param ft Top coordinate + *

Rectangle describing what part of the page to be displayed + * (must be in User Coordinates) + */ + public PDFAnnot(int l,int b,int r,int t, + PDFObject dest, + int fl,int fb,int fr,int ft + ) { + this("/Link",l,b,r,t); + this.dest = dest; + this.fl = fl; + this.fb = fb; + this.fr = fr; + this.ft = ft; + } + + /** + * Sets the border for the annotation. By default, no border is defined. + * + *

If the style is DASHED, then this method uses PDF's default dash + * scheme {3} + * + *

Important: the annotation must have been added to the document before + * this is used. If the annotation was created using the methods in + * PDFPage, then the annotation is already in the document. + * + * @param style Border style SOLID, DASHED, BEVELED, INSET or UNDERLINED. + * @param width Width of the border + */ + public void setBorder(short style,double width) { + border = new PDFBorder(style,width); + pdfDocument.add(border); + } + + /** + * Sets the border for the annotation. Unlike the other method, this + * produces a dashed border. + * + *

Important: the annotation must have been added to the document before + * this is used. If the annotation was created using the methods in + * PDFPage, then the annotation is already in the document. + * + * @param width Width of the border + * @param dash Array of lengths, used for drawing the dashes. If this + * is null, then the default of {3} is used. + */ + public void setBorder(double width,double dash[]) { + border = new PDFBorder(width,dash); + pdfDocument.add(border); + } + + /** + * Should this be public?? + * + * @param os OutputStream to send the object to + * @exception IOException on error + */ + public void write(OutputStream os) throws IOException { + // Write the object header + writeStart(os); + + // now the objects body + os.write("/Subtype ".getBytes()); + os.write(subtype.getBytes()); + os.write("\n/Rect [".getBytes()); + os.write(Integer.toString(l).getBytes()); + os.write(" ".getBytes()); + os.write(Integer.toString(b).getBytes()); + os.write(" ".getBytes()); + os.write(Integer.toString(r).getBytes()); + os.write(" ".getBytes()); + os.write(Integer.toString(t).getBytes()); + os.write("]\n".getBytes()); + + // handle the border + if(border==null) { + os.write("/Border [0 0 0]\n".getBytes()); + //if(pdf.defaultOutlineBorder==null) + //pdf.add(pdf.defaultOutlineBorder = new border(SOLID,0.0)); + //os.write(pdf.defaultOutlineBorder.toString().getBytes()); + } else { + os.write("/BS ".getBytes()); + os.write(border.toString().getBytes()); + os.write("\n".getBytes()); + } + + // Now the annotation subtypes + if(subtype.equals("/Text")) { + os.write("/Contents ".getBytes()); + os.write(PDFStringHelper.makePDFString(s).getBytes()); + os.write("\n".getBytes()); + } else if(subtype.equals("/Link")) { + os.write("/Dest [".getBytes()); + os.write(dest.toString().getBytes()); + if(fl==FULL_PAGE) + os.write(" /Fit]".getBytes()); + else { + os.write(" /FitR ".getBytes()); + os.write(Integer.toString(fl).getBytes()); + os.write(" ".getBytes()); + os.write(Integer.toString(fb).getBytes()); + os.write(" ".getBytes()); + os.write(Integer.toString(fr).getBytes()); + os.write(" ".getBytes()); + os.write(Integer.toString(ft).getBytes()); + os.write("]".getBytes()); + } + os.write("\n".getBytes()); + } + + // finish off with its footer + writeEnd(os); + } +} diff --git a/libsrc/gnujpdf/src/gnu/jpdf/PDFBorder.java b/libsrc/gnujpdf/src/gnu/jpdf/PDFBorder.java new file mode 100644 index 000000000..f96e7b3cf --- /dev/null +++ b/libsrc/gnujpdf/src/gnu/jpdf/PDFBorder.java @@ -0,0 +1,112 @@ +/* + * $Id: PDFBorder.java,v 1.2 2007/08/26 18:56:35 gil1 Exp $ + * + * $Date: 2007/08/26 18:56:35 $ + * + * 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 + */ +package gnu.jpdf; + +import java.io.IOException; +import java.io.OutputStream; + +/** + *

A border around an annotation

+ * + * + * @author Peter T Mount, http://www.retep.org.uk/pdf/ + * @author Eric Z. Beard, ericzbeard@hotmail.com + * @version $Revision: 1.2 $ + */ +public class PDFBorder extends PDFObject +{ + /* + * NOTE: The original class is the work of Peter T. Mount, who released it + * in the uk.org.retep.pdf package. It was modified by Eric Z. Beard as + * follows: the package name was changed to gnu.pdf. It is still + * licensed under the LGPL. + */ + + /** + * The style of the border + */ + private short style; + + /** + * The width of the border + */ + private double width; + + /** + * This array allows the definition of a dotted line for the border + */ + private double dash[]; + + /** + * Creates a border using the predefined styles in PDFAnnot. + *

Note: Do not use PDFAnnot.DASHED with this method. + * Use the other constructor. + * + * @param style The style of the border + * @param width The width of the border + * @see PDFAnnot + */ + public PDFBorder(short style,double width) { + super("/Border"); + this.style = style; + this.width = width; + } + + /** + * Creates a border of style PDFAnnot.DASHED + * + * @param width The width of the border + * @param dash The line pattern definition + */ + public PDFBorder(double width,double dash[]) { + super("/Border"); + this.style = PDFAnnot.DASHED; + this.width = width; + this.dash = dash; + } + + /** + * @param os OutputStream to send the object to + * @exception IOException on error + */ + public void write(OutputStream os) throws IOException { + //writeStart(os); + os.write(Integer.toString(objser).getBytes()); + os.write(" 0 obj\n".getBytes()); + + os.write("[/S /".getBytes()); + os.write("SDBIU".substring(style,style+1).getBytes()); + os.write(" /W ".getBytes()); + os.write(Double.toString(width).getBytes()); + if(dash!=null) { + os.write(" /D [".getBytes()); + os.write(Double.toString(dash[0]).getBytes()); + for(int i=1;iThis class implements the PDF Catalog, + * also known as the root node

+ * + * @author Peter T. Mount + * @author Eric Z. Beard, ericzbeard@hotmail.com + * @version $Revision: 1.2 $, $Date: 2007/08/26 18:56:35 $ + */ +public class PDFCatalog extends PDFObject +{ + /** + * The pages of the document + */ + private PDFPageList pdfPageList; + + /** + * The outlines of the document + */ + private PDFOutline outlines; + + /** + * The initial page mode + */ + private int pagemode; + + /** + * This constructs a PDF Catalog object + * + * @param pdfPageList The PDFPageList object that's the root + * of the documents page tree + * @param pagemode How the document should appear when opened. + * Allowed values are USENONE, USEOUTLINES, USETHUMBS or FULLSCREEN. + */ + public PDFCatalog(PDFPageList pdfPageList,int pagemode) { + super("/Catalog"); + this.pdfPageList = pdfPageList; + this.pagemode = pagemode; + } + + /** + * This sets the root outline object + * @param outline The root outline + */ + protected void setOutline(PDFOutline outline) { + this.outlines = outline; + } + + /** + * @param os OutputStream to send the object to + * @exception IOException on error + */ + public void write(OutputStream os) throws IOException { + // Write the object header + writeStart(os); + + // now the objects body + + // the /Pages object + os.write("/Pages ".getBytes()); + os.write(pdfPageList.toString().getBytes()); + os.write("\n".getBytes()); + + // the Outlines object + if(outlines!=null) { + //if(outlines.getLast()>-1) { + os.write("/Outlines ".getBytes()); + os.write(outlines.toString().getBytes()); + os.write("\n".getBytes()); + //} + } + + // the /PageMode setting + os.write("/PageMode ".getBytes()); + os.write(PDFDocument.PDF_PAGE_MODES[pagemode].getBytes()); + os.write("\n".getBytes()); + + // finish off with its footer + writeEnd(os); + } +} // end class PDFCatalog + diff --git a/libsrc/gnujpdf/src/gnu/jpdf/PDFDocument.java b/libsrc/gnujpdf/src/gnu/jpdf/PDFDocument.java new file mode 100644 index 000000000..78e12e326 --- /dev/null +++ b/libsrc/gnujpdf/src/gnu/jpdf/PDFDocument.java @@ -0,0 +1,317 @@ +/* + * $Id: PDFDocument.java,v 1.4 2007/09/22 12:58:40 gil1 Exp $ + * + * $Date: 2007/09/22 12:58:40 $ + * + * 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 + * + */ +package gnu.jpdf; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.Serializable; +import java.util.Vector; + +/** + *

This class is the base of the PDF generator. A PDFDocument class is + * created for a document, and each page, object, annotation, + * etc is added to the document. + * Once complete, the document can be written to an OutputStream, and the PDF + * document's internal structures are kept in sync.

+ * + *

Note that most programmers using this package will NEVER access + * one of these objects directly. Most everything can be done using + * PDFJob and PDFGraphics, so you don't need + * to directly instantiate a PDFDocument

+ * + *

ezb - 20011115 - Wondering if the constructors should even be public. + * When would someone want to make one of these and manipulate it outside + * the context of a job and graphics object?

+ * + * @author Peter T Mount, http://www.retep.org.uk/pdf/ + * @author Eric Z. Beard, ericzbeard@hotmail.com + * @author Gilbert DeLeeuw, gil1@users.sourceforge.net + * @version $Revision: 1.4 $, $Date: 2007/09/22 12:58:40 $ + */ +public class PDFDocument implements Serializable +{ + + /* + * NOTE: This class originated in uk.org.retep.pdf, by Peter T. Mount, and it + * has been modified by Eric Z. Beard, ericzbeard@hotmail.com. The + * package name was changed to gnu.jpdf and several inner classes were + * moved out into their own files. + */ + + /** + * This is used to allocate objects a unique serial number in the document. + */ + protected int objser; + + /** + * This vector contains each indirect object within the document. + */ + protected Vector objects; + + /** + * This is the Catalog object, which is required by each PDF Document + */ + private PDFCatalog catalog; + + /** + * This is the info object. Although this is an optional object, we + * include it. + */ + private PDFInfo info; + + /** + * This is the Pages object, which is required by each PDF Document + */ + private PDFPageList pdfPageList; + + /** + * This is the Outline object, which is optional + */ + private PDFOutline outline; + + /** + * This holds a PDFObject describing the default border for annotations. + * It's only used when the document is being written. + */ + protected PDFObject defaultOutlineBorder; + + /** + *

This page mode indicates that the document + * should be opened just with the page visible. This is the default

+ */ + public static final int USENONE = 0; + + /** + *

This page mode indicates that the Outlines + * should also be displayed when the document is opened.

+ */ + public static final int USEOUTLINES = 1; + + /** + *

This page mode indicates that the Thumbnails should be visible when the + * document first opens.

+ */ + public static final int USETHUMBS = 2; + + /** + *

+ * This page mode indicates that when the document is opened, it is displayed + * in full-screen-mode. There is no menu bar, window controls nor any other + * window present.

+ */ + public static final int FULLSCREEN = 3; + + /** + *

+ * These map the page modes just defined to the pagemodes setting of PDF. + *

+ */ + public static final String PDF_PAGE_MODES[] = { + "/UseNone", + "/UseOutlines", + "/UseThumbs", + "/FullScreen" + }; + + /** + * This is used to provide a unique name for a font + */ + private int fontid = 0; + + /** + *

This is used to provide a unique name for an image

+ */ + private int imageid = 0; + + /** + * This holds the current fonts + */ + private Vector fonts; + + + /** + *

This creates a PDF document with the default pagemode

+ */ + public PDFDocument() { + this(USENONE); + } + + /** + *

This creates a PDF document

+ * @param pagemode an int, determines how the document will present itself to + * the viewer when it first opens. + */ + public PDFDocument(int pagemode) { + objser = 1; + objects = new Vector(); + fonts = new Vector(); + + // Now create some standard objects + add(pdfPageList = new PDFPageList()); + add(catalog = new PDFCatalog(pdfPageList,pagemode)); + add(info = new PDFInfo()); + + // Acroread on linux seems to die if there is no root outline + add(getOutline()); + } + + + + /** + * This adds a top level object to the document. + * + *

Once added, it is allocated a unique serial number. + * + *

Note: Not all object are added directly using this method. + * Some objects which have Kids (in PDF sub-objects or children are + * called Kids) will have their own add() method, which will call this + * one internally. + * + * @param obj The PDFObject to add to the document + * @return the unique serial number for this object. + */ + public synchronized int add(PDFObject obj) + { + objects.addElement(obj); + obj.objser=objser++; // create a new serial number + obj.pdfDocument = this; // so they can find the document they belong to + + // If its a page, then add it to the pages collection + if(obj instanceof PDFPage) + pdfPageList.add((PDFPage)obj); + + return obj.objser; + } + + /** + *

This returns a specific page. It's used mainly when using a + * Serialized template file.

+ * + * ?? How does a serialized template file work ??? + * + * @param page page number to return + * @return PDFPage at that position + */ + public PDFPage getPage(int page) { + return pdfPageList.getPage(page); + } + + + /** + * @return the root outline + */ + public PDFOutline getOutline() + { + if(outline==null) { + outline = new PDFOutline(); + catalog.setOutline(outline); + } + return outline; + } + + /** + * This returns a font of the specified type and font. If the font has + * not been defined, it creates a new font in the PDF document, and + * returns it. + * + * @param type PDF Font Type - usually "/Type1" + * @param font Java font name + * @param style java.awt.Font style (NORMAL, BOLD etc) + * @return PDFFont defining this font + */ + public PDFFont getFont(String type,String font,int style) { + for(PDFFont ft : fonts) { + if(ft.equals(type,font,style)) + return ft; + } + + // the font wasn't found, so create it + fontid++; + PDFFont ft = new PDFFont("/F"+fontid,type,font,style); + add(ft); + fonts.addElement(ft); + return ft; + } + + /** + * Sets a unique name to a PDFImage + * @param img PDFImage to set the name of + * @return the name given to the image + */ + public String setImageName(PDFImage img) { + imageid++; + img.setName("/Image"+imageid); + return img.getName(); + } + + + /** + *

Set the PDFInfo object, which contains author, title, + * keywords, etc

+ * @param info a PDFInof object + */ + public void setPDFInfo(PDFInfo info) { + this.info = info; + } + + + /** + *

Get the PDFInfo object, which contains author, title, keywords, + * etc

+ * @return the PDFInfo object for this document. + */ + public PDFInfo getPDFInfo() { + return this.info; + } + + + /** + * This writes the document to an OutputStream. + * + *

Note: You can call this as many times as you wish, as long as + * the calls are not running at the same time. + * + *

Also, objects can be added or amended between these calls. + * + *

Also, the OutputStream is not closed, but will be flushed on + * completion. It is up to the caller to close the stream. + * + * @param os OutputStream to write the document to + * @exception IOException on error + */ + public void write(OutputStream os) throws IOException + { + PDFOutput pos = new PDFOutput(os); + + // Write each object to the OutputStream. We call via the output + // as that builds the xref table + for(PDFObject o : objects) { + pos.write(o); + } + + // Finally close the output, which writes the xref table. + pos.close(); + + // and flush the output stream to ensure everything is written. + os.flush(); + } + +} // end class PDFDocument diff --git a/libsrc/gnujpdf/src/gnu/jpdf/PDFFont.java b/libsrc/gnujpdf/src/gnu/jpdf/PDFFont.java new file mode 100644 index 000000000..73147d65d --- /dev/null +++ b/libsrc/gnujpdf/src/gnu/jpdf/PDFFont.java @@ -0,0 +1,250 @@ +/* + * $Id: PDFFont.java,v 1.3 2007/08/26 19:00:11 gil1 Exp $ + * + * $Date: 2007/08/26 19:00:11 $ + * + * 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 + */ +package gnu.jpdf; + +import java.awt.Font; +import java.io.IOException; +import java.io.OutputStream; +import java.io.Serializable; + +/** + * This class defines a font within a PDF document. + * + * @author Peter T Mount,http://www.retep.org.uk/pdf/ + * @author Eric Z. Beard, ericzbeard@hotmail.com + * @author Gilbert DeLeeuw, gil1@users.sourceforge.net + * @version $Revision: 1.3 $, $Date: 2007/08/26 19:00:11 $ + */ +public class PDFFont extends PDFObject implements Serializable +{ + + /* + * NOTE: The original class is the work of Peter T. Mount, who released it + * in the uk.org.retep.pdf package. It was modified by Eric Z. Beard as + * follows: + * The package name was changed to gnu.pdf. + * The formatting was changed a little bit + * It is still licensed under the LGPL. + */ + + /** + * The PDF document name of the font + */ + private String name; + + /** + * The PDF type of the font, usually /Type1 + */ + private String type; + + /** + * The font's real name + */ + private String font; + + /** + * The name of the equivalent Java font + */ + private String javaFont; + + /** + * The PDF Style, ie: BOLD, ITALIC, etc + */ + private int style; + + /** + * This constructs a default PDFFont. In this case Helvetica + */ + protected PDFFont() { + this("/F1","/Type1","Helvetica",Font.PLAIN); + } + + /** + * Constructs a PDFFont. This will attempt to map the font from a known + * Java font name to that in PDF, defaulting to Helvetica if not possible. + * + * @param name The document name, ie /F1 + * @param type The pdf type, ie /Type1 + * @param font The font name, ie Helvetica + * @param style The java.awt.Font style, ie: Font.PLAIN + */ + public PDFFont(String name,String type,String font,int style) { + super("/Font"); + this.name = name; + this.type = type; + this.style = style; + + String f = font.toLowerCase(); + + // default PDF Font name +// this.font = base14[0][1]; +// this.javaFont = base14[0][0]; + this.font = font; + this.javaFont = "/" + font; + + // attempt to translate the font name from Java to PDF + for(int i=0;i usedImages = new WeakHashMap(); private Color background; /** * This is true for any Graphics instance that didn't create the stream. * @see #create */ private boolean child; private Area clip; /** * This holds the current clipRectangle */ protected Rectangle clipRectangle; private Composite composite; private Graphics2D dg2 = new BufferedImage(2, 2, BufferedImage.TYPE_INT_RGB).createGraphics(); /** * This is the current font (in Java format) */ private Font font; /** * Part of the optimizer: * When true, we are drawing a path. */ private boolean inStroke; /** * Part of the optimizer: * When true, we are within a Text Block. */ private boolean inText; // true if within a Text Block - see newTextBlock() /** * The stroke line cap code; */ private int lineCap = 0; /** * The stroke line join code */ private int lineJoin = 0; /** * The stroke line width */ private float lineWidth = 1.0f; /** * Part of the optimizer: * The last known moveto/lineto x coordinate * @see #moveto * @see #lineto */ private float lx; // last known moveto/lineto coordinates /** * Part of the optimizer: * The last known moveto/lineto y coordinate * @see #moveto * @see #lineto */ private float ly; // last known moveto/lineto coordinates private float miterLimit = 10.0f; /** * Part of the optimizer: * When true, the font has changed. */ private boolean newFont; // true if the font changes - see newTextBlock() private Stroke originalStroke; // Original transform private AffineTransform oTransform; /** * This is a reference to the PDFPage we are rendering to. */ private PDFPage page; /** * This is the current pen/fill color */ private Paint paint; /** * This is the current font (in PDF format) */ private PDFFont pdffont; /** * Part of the optimizer: * This is written to the stream when the newPath() is called. np then clears * this value. */ private String pre_np; // PDF space transform private AffineTransform pTransform; /** * This is the PrintWriter used to write PDF drawing commands to the Stream */ private PrintWriter pw; /** * RenderingHints */ private RenderingHints rhints = new RenderingHints(null); private Stroke stroke; // Start of Graphics2D properties // Java space transform private AffineTransform transform; /** * This is used to translate coordinates */ protected float trax; /** * This is used to translate coordinates */ protected float tray; /** * Part of the optimizer: * The last x coordinate when rendering text */ private float tx; // the last coordinate for text rendering /** * Part of the optimizer: * The last y coordinate when rendering text */ private float ty; // the last coordinate for text rendering /** * @see Graphics2D#addRenderingHints(Map) */ public void addRenderingHints(Map hints) { rhints.putAll(hints); } /** * This produces an arc by breaking it down into one or more Bezier curves. * It is used internally to implement the drawArc and fillArc methods. * * @param axc X coordinate of arc centre * @param ayc Y coordinate of arc centre * @param width of bounding rectangle * @param height of bounding rectangle * @param ang1 Start angle * @param ang2 End angle * @param clockwise true to draw clockwise, false anti-clockwise */ public void arc(double axc,double ayc, double width,double height, double ang1,double ang2, boolean clockwise) { double adiff; double x0, y0; double x3r, y3r; boolean first = true; // may not need this //if( ar < 0 ) { //ang1 += fixed_180; //ang2 += fixed_180; //ar = - ar; //} double ang1r = (ang1%360.0)*degrees_to_radians; double sin0 = Math.sin(ang1r); double cos0 = Math.cos(ang1r); x0 = axc + width*cos0; y0 = ayc + height*sin0; // NB: !clockwise here as Java Space is inverted to User Space if( !clockwise ) { // Quadrant reduction while ( ang1 < ang2 ) ang2 -= 360.0; while ( (adiff = ang2 - ang1) < -90.0 ) { double w = sin0; sin0 = -cos0; cos0 = w; x3r = axc + width*cos0; y3r = ayc + height*sin0; arc_add(first, width, height, x0, y0, x3r, y3r, (x0 + width*cos0), (y0 + height*sin0) ); x0 = x3r; y0 = y3r; ang1 -= 90.0; first = false; } } else { // Quadrant reduction while ( ang2 < ang1 ) ang2 += 360.0; while ( (adiff = ang2 - ang1) > 90.0 ) { double w = cos0; cos0 = -sin0; sin0 = w; x3r = axc + width*cos0; y3r = ayc + height*sin0; arc_add(first, width, height, x0, y0, x3r, y3r, (x0 + width*cos0), (y0 + height*sin0) ); x0 = x3r; y0 = y3r; ang1 += 90.0; first = false; } } // Compute the intersection of the tangents. // We know that -fixed_90 <= adiff <= fixed_90. double trad = Math.tan(adiff * (degrees_to_radians / 2)); double ang2r = ang2 * degrees_to_radians; double xt = x0 - trad * width*sin0; double yt = y0 + trad * height*cos0; arc_add(first, width, height, x0, y0, (axc + width * Math.cos(ang2r)), (ayc + height * Math.sin(ang2r)), xt, yt); } /** * Used by the arc method to actually add an arc to the path * Important: We write directly to the stream here, because this method * operates in User space, rather than Java space. * @param first true if the first arc * @param w width * @param h height * @param x0 coordinate * @param y0 coordinate * @param x3 coordinate * @param y3 coordinate * @param xt coordinate * @param yt coordinate */ private void arc_add(boolean first, double w,double h, double x0,double y0, double x3,double y3, double xt,double yt) { double dx = xt - x0, dy = yt - y0; double dist = dx*dx + dy*dy; double w2 = w*w, h2=h*h; double r2 = w2+h2; double fw = 0.0, fh = 0.0; if(dist < (r2*1.0e8)) { // JM fw = (w2 != 0.0) ? ((4.0/3.0)/(1+Math.sqrt(1+dist/w2))) : 0.0; fh = (h2 != 0.0) ? ((4.0/3.0)/(1+Math.sqrt(1+dist/h2))) : 0.0; } // The path must have a starting point if(first) moveto(x0,y0); double x = x0+((xt-x0)*fw); double y = y0+((yt-y0)*fh); x0 = x3+((xt-x3)*fw); y0 = y3+((yt-y3)*fh); // Finally the actual curve. curveto(x,y,x0,y0,x3,y3); } /** * This simply draws a White Rectangle to clear the area * @param x coordinate * @param y coordinate * @param w width * @param h height */ public void clearRect(int x,int y,int w,int h) { closeBlock(); pw.print("q 1 1 1 RG ");// save state, set colour to White drawRect(x,y,w,h); closeBlock("B Q"); // close fill & stroke, then restore state } /** * @see Graphics2D#clip(Shape) */ public void clip(Shape s) { if (s == null) { setClip(null); return; } s = transform.createTransformedShape(s); if (clip == null) clip = new Area(s); else clip.intersect(new Area(s)); // followPath(s, CLIP); } /** * This extra method allows PDF users to clip to a Polygon. * *

In theory you could use setClip(), except that java.awt.Graphics * only supports Rectangle with that method, so we will have an extra * method. * @param p Polygon to clip to */ public void clipPolygon(Polygon p) { closeBlock(); // finish off any existing path polygon(p.xpoints,p.ypoints,p.npoints); closeBlock("W"); // clip to current path clipRectangle = p.getBounds(); } /** * Clips to a set of coordinates * @param x coordinate * @param y coordinate * @param w width * @param h height */ public void clipRect(int x,int y,int w,int h) { setClip(x,y,w,h); } /** * All functions should call this to close any existing optimized blocks. */ void closeBlock() { closeBlock("S"); } /** *

This is used by code that use the path in any way other than Stroke * (like Fill, close path & Stroke etc). Usually this is used internally.

* * @param code PDF operators that will close the path */ void closeBlock(String code) { if(inText) { pw.println("ET Q"); // setOrientation(); // fixes Orientation matrix } if(inStroke) { pw.println(code); } inStroke=inText=false; } /** * This is unsupported - how do you do this with Vector graphics? * @param x coordinate * @param y coordinate * @param w width * @param h height * @param dx coordinate * @param dy coordinate */ public void copyArea(int x,int y,int w,int h,int dx,int dy) { // Hmm... Probably need to keep track of everything // that has been drawn so far to get the contents of an area } //============ Line operations ======================= /** *

This returns a child instance of this Graphics object. As with AWT, the * affects of using the parent instance while the child exists, is not * determined.

* *

Once complete, the child should be released with it's dispose() * method which will restore the graphics state to it's parent.

* * @return Graphics object to render onto the page */ public Graphics create() { closeBlock(); PDFGraphics g = createGraphic(page,pw); // The new instance inherits a few items // g.media = new Rectangle(media); g.trax = trax; g.tray = tray; g.clipRectangle = new Rectangle(clipRectangle); return (Graphics) g; } // end create() /** * This method creates a new instance of the class based on the page * and a print writer. * * @param page the page to attach to * @param pw the PrintWriter to attach to. */ protected PDFGraphics createGraphic(PDFPage page, PrintWriter pw) { PDFGraphics g = new PDFGraphics(); g.init(page,pw); return g; } /** * This extension appends a Bezier curve to the path. The curve * extends from the current point to (x2,y2) using the current * point and (x1,y1) as the Bezier control points. *

The new current point is (x2,y2) * * @param x1 Second control point * @param y1 Second control point * @param x2 Destination point * @param y2 Destination point */ public void curveto(double x1,double y1,double x2,double y2) { newPath(); pw.println(cxy(x1,y1)+cxy(x2,y2)+"v"); lx=(float)x2; ly=(float)y2; } /** * This extension appends a Bezier curve to the path. The curve * extends from the current point to (x3,y3) using (x1,y1) and * (x2,y2) as the Bezier control points. *

The new current point is (x3,y3) * * @param x1 First control point * @param y1 First control point * @param x2 Second control point * @param y2 Second control point * @param x3 Destination point * @param y3 Destination point */ public void curveto(double x1,double y1,double x2,double y2,double x3,double y3) { newPath(); pw.println(cxy(x1,y1)+cxy(x2,y2)+cxy(x3,y3)+"c"); lx=(float)x3; ly=(float)y3; } /** * This extension appends a Bezier curve to the path. The curve * extends from the current point to (x2,y2) using the current * point and (x1,y1) as the Bezier control points. *

The new current point is (x2,y2) * * @param x1 Second control point * @param y1 Second control point * @param x2 Destination point * @param y2 Destination point */ public void curveto(int x1,int y1,int x2,int y2) { newPath(); pw.println(cxy(x1,y1)+cxy(x2,y2)+"v"); lx=x2; ly=y2; } /** * This extension appends a Bezier curve to the path. The curve * extends from the current point to (x3,y3) using (x1,y1) and * (x2,y2) as the Bezier control points. *

The new current point is (x3,y3) * * @param x1 First control point * @param y1 First control point * @param x2 Second control point * @param y2 Second control point * @param x3 Destination point * @param y3 Destination point */ public void curveto(int x1,int y1,int x2,int y2,int x3,int y3) { newPath(); pw.println(cxy(x1,y1)+cxy(x2,y2)+cxy(x3,y3)+"c"); lx=x3; ly=y3; } /** * This extension appends a Bezier curve to the path. The curve * extends from the current point to (x2,y2) using (x1,y1) and * the end point as the Bezier control points. *

The new current point is (x2,y2) * * @param x1 Second control point * @param y1 Second control point * @param x2 Destination point * @param y2 Destination point */ public void curveto2(double x1,double y1,double x2,double y2) { newPath(); pw.println(cxy(x1,y1)+cxy(x2,y2)+"y"); lx=(float)x2; ly=(float)y2; } // Arcs are horrible and complex. They are at the end of the // file, because they are the largest. This is because, unlike // Postscript, PDF doesn't have any arc operators, so we must // implement them by converting into one or more Bezier curves // (which is how Postscript does them internally). /** * This extension appends a Bezier curve to the path. The curve * extends from the current point to (x2,y2) using (x1,y1) and * the end point as the Bezier control points. *

The new current point is (x2,y2) * * @param x1 Second control point * @param y1 Second control point * @param x2 Destination point * @param y2 Destination point */ public void curveto2(int x1,int y1,int x2,int y2) { newPath(); pw.println(cxy(x1,y1)+cxy(x2,y2)+"y"); lx=x2; ly=y2; } /** * Converts the Java space dimension into pdf. * @param w width * @param h height * @return String containing the coordinates in PDF space */ private String cwh(double w,double h) { double nw=w,nh=h; // scratch // switch(mediaRot) { // case PageFormat.PORTRAIT: // Portrait //nw = w; nh = -h; // break; // // case PageFormat.LANDSCAPE: // // Landscape // nw = h; // nh = w; // break; // //// case 180: //// // Inverse Portrait //// nw = -w; //// //nh = h; //// break; // // case PageFormat.REVERSE_LANDSCAPE: // // Seascape // nw = -h; // nh = -w; // break; // } return ""+df.format(nw)+" "+df.format(nh)+" "; } /** * Converts the Java space dimension into pdf. * @param w width * @param h height * @return String containing the coordinates in PDF space */ private String cwh(int w,int h) { return cwh((double)w,(double)h); } /** * Converts the Java space coordinates into pdf. * @param x coordinate * @param y coordinate * @return String containing the coordinates in PDF space */ private String cxy(double x, double y) { // double nx = x, ny = y; // scratch // double mh = page.getPageFormat().getHeight(); Point2D ptSrc = new Point2D.Double(x, y); Point2D ptDst = new Point2D.Double(); transform.transform(ptSrc, ptDst); // x += trax; // y += tray; // // nx = x; // ny = mh - y; // // System.out.println("\ncxy(" + ptSrc.getX() + ", " + ptSrc.getY() + ")"); // System.out.println("Old [" + nx + "," + ny + "]"); // System.out.println("Trn [" + ptDst.getX() + ", " + ptDst.getY() + "]"); // // return "" + df.format(nx) + " " + df.format(ny) + " "; return ""+df.format(ptDst.getX())+" "+df.format(ptDst.getY())+" "; } /** * Converts the Java space coordinates into pdf. * @param x coordinate * @param y coordinate * @return String containing the coordinates in PDF space */ private String cxy(int x,int y) { return cxy((double)x,(double)y); } /** *

This releases any resources used by this Graphics object. You must use * this method once finished with it. Leaving it open will leave the PDF * stream in an inconsistent state, and will produce errors.

* *

If this was created with Graphics.create() then the parent instance * can be used again. If not, then this closes the graphics operations for * this page when used with PDFJob.

* *

When using PDFPage, you can create another fresh Graphics instance, * which will draw over this one.

* */ public void dispose() { closeBlock(); if(child) { pw.println("Q"); // restore graphics context } else { pw.close(); // close the stream if were the parent } } // ********************************************* // **** Implementation of java.awt.Graphics **** // ********************************************* //============ Rectangle operations ======================= /** * @see Graphics2D#draw(Shape) */ public void draw(Shape s) { followPath(s, STROKE); } /** *

Not implemented

* *

Draws a 3-D highlighted outline of the specified rectangle. * The edges of the rectangle are highlighted so that they appear * to be beveled and lit from the upper left corner. * The colors used for the highlighting effect are determined based on * the current color. The resulting rectangle covers an area that * is width + 1 pixels wide by height + 1 pixels tall. *

* * @param x an int value * @param y an int value * @param width an int value * @param height an int value * @param raised a boolean value */ public void draw3DRect(int x, int y, int width, int height, boolean raised) { // Not implemented } /** * Draws an arc * @param x coordinate * @param y coordinate * @param w width * @param h height * @param sa Start angle * @param aa End angle */ public void drawArc(int x,int y,int w,int h,int sa,int aa) { w=w>>1; h=h>>1; x+=w; y+=h; arc((double)x,(double)y, (double)w,(double)h, (double)-sa,(double)(-sa-aa), false); } /** *

Not implemented

* * @param data a byte[] value * @param offset an int value * @param length an int value * @param x an int value * @param y an int value */ public void drawBytes(byte[] data, int offset, int length, int x, int y) { } //============ Optimizers ======================= /** * @see Graphics2D#drawGlyphVector(GlyphVector, float, float) */ public void drawGlyphVector(GlyphVector g, float x, float y) { Shape s = g.getOutline(x, y); fill(s); } /** * @see Graphics2D#drawImage(BufferedImage, BufferedImageOp, int, int) */ public void drawImage(BufferedImage img, BufferedImageOp op, int x, int y) { BufferedImage result = img; if (op != null) { result = op.createCompatibleDestImage(img, img.getColorModel()); result = op.filter(img, result); } drawImage(result, x, y, null); } /** * @see Graphics2D#drawImage(Image, AffineTransform, ImageObserver) */ public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) { // return drawImage(img, null, xform, null, obs); return true; } /** *

Draw's an image onto the page, with a backing colour.

* * @param img The java.awt.Image * @param x coordinate on page * @param y coordinate on page * @param bgcolor Background colour * @param obs ImageObserver * @return true if drawn */ public boolean drawImage(Image img,int x,int y,Color bgcolor, ImageObserver obs) { return drawImage(img,x,y,img.getWidth(obs),img.getHeight(obs), bgcolor,obs); } /** * Draw's an image onto the page * @param img The java.awt.Image * @param x coordinate on page * @param y coordinate on page * @param obs ImageObserver * @return true if drawn */ public boolean drawImage(Image img,int x,int y,ImageObserver obs) { return drawImage(img,x,y,img.getWidth(obs),img.getHeight(obs),obs); } /** *

Draw's an image onto the page, with a backing colour.

* * @param img The java.awt.Image * @param x coordinate on page * @param y coordinate on page * @param w Width on page * @param h height on page * @param bgcolor Background colour * @param obs ImageObserver * @return true if drawn */ public boolean drawImage(Image img,int x,int y,int w,int h, Color bgcolor,ImageObserver obs) { closeBlock(); pw.print("q "); // save state Color c = getColor(); // save current colour setColor(bgcolor); // change the colour drawRect(x,y,w,h); closeBlock("B Q"); // fill stroke, restore state paint = c; // restore original colour return drawImage(img,x,y,img.getWidth(obs),img.getHeight(obs),obs); } /** *

Draws an image onto the page.

* *

This method is implemented with ASCIIbase85 encoding and the * zip stream deflater. It results in a stream that is anywhere * from 3 to 10 times as big as the image. This obviously needs some * improvement, but it works well for small images

* * @param img The java.awt.Image * @param x coordinate on page * @param y coordinate on page * @param w Width on page * @param h height on page * @param obs ImageObserver * @return true if drawn */ public boolean drawImage(Image img,int x,int y,int w,int h, ImageObserver obs) { closeBlock(); PDFImage image; if(usedImages.containsKey(img)){ image = usedImages.get(img); }else{ image = new PDFImage(img,obs); // The image needs to be registered in several places page.getPDFDocument().setImageName(image); page.getPDFDocument().add(image); usedImages.put(img, image); } page.addToProcset("/ImageC"); page.addImageResource(image.getName() + " " + image.getSerialID() + " 0 R"); // JM /*page.addResource("/XObject << " + image.getName() + " " + image.getSerialID() + " 0 R >>");*/ // q w 0 0 h x y cm % the coordinate matrix pw.print("q " + w+ " 0 0 " + h+ " " + x + " " + ((int)page.getDimension().getHeight()-y-h) + " cm \n" + image.getName() + " Do\nQ\n"); return false; } /** * Draw's an image onto the page, with scaling *

This is not yet supported. * * @param img The java.awt.Image * @param dx1 coordinate on page * @param dy1 coordinate on page * @param dx2 coordinate on page * @param dy2 coordinate on page * @param sx1 coordinate on image * @param sy1 coordinate on image * @param sx2 coordinate on image * @param sy2 coordinate on image * @param bgcolor Background colour * @param obs ImageObserver * @return true if drawn */ public boolean drawImage(Image img,int dx1,int dy1,int dx2, int dy2,int sx1,int sy1,int sx2,int sy2, Color bgcolor,ImageObserver obs) { return false; } //============ Clipping operations ======================= /** * Draw's an image onto the page, with scaling *

This is not yet supported. * * @param img The java.awt.Image * @param dx1 coordinate on page * @param dy1 coordinate on page * @param dx2 coordinate on page * @param dy2 coordinate on page * @param sx1 coordinate on image * @param sy1 coordinate on image * @param sx2 coordinate on image * @param sy2 coordinate on image * @param obs ImageObserver * @return true if drawn */ public boolean drawImage(Image img,int dx1,int dy1,int dx2, int dy2,int sx1,int sy1,int sx2,int sy2, ImageObserver obs) { // This shouldn't be too bad, just change the coordinate matrix return false; } /** * Draws a line between two coordinates. * * If the first coordinate is the same as the last one drawn * (i.e. a previous drawLine, moveto, etc) it is ignored. * @param x1 coordinate * @param y1 coordinate * @param x2 coordinate * @param y2 coordinate */ public void drawLine(int x1,int y1,int x2,int y2) { moveto(x1,y1); lineto(x2,y2); } //============ Arcs operations ============================== // These are the standard Graphics operators. They use the // arc extension operators to achieve the affect. /** *

Draws an oval

* * @param x coordinate * @param y coordinate * @param w width * @param h height */ public void drawOval(int x,int y,int w,int h) { drawArc(x, y, w, h, 0, 360); } /** * Draws a polygon, linking the first and last coordinates. * @param xp Array of x coordinates * @param yp Array of y coordinates * @param np number of points in polygon */ public void drawPolygon(int[] xp,int[] yp,int np) { polygon(xp,yp,np); closeBlock("s"); // close path and stroke } /** * Draws a polyline. The first and last coordinates are not linked. * @param xp Array of x coordinates * @param yp Array of y coordinates * @param np number of points in polyline */ public void drawPolyline(int[] xp,int[] yp,int np) { polygon(xp,yp,np); // no stroke, as we keep the optimizer in stroke state } /** * We override Graphics.drawRect as it doesn't join the 4 lines. * Also, PDF provides us with a Rectangle operator, so we will use that. * @param x coordinate * @param y coordinate * @param w width * @param h height */ public void drawRect(int x,int y,int w,int h) { newPath(); pw.print(cxy(x,y)+cwh(w,h)+"re "); // rectangle lx=x; // I don't know if this is correct, but lets see if PDF ends ly=y; // the rectangle at it's start. // stroke (optimized) } /** * @see Graphics2D#drawRenderableImage(RenderableImage, AffineTransform) */ public void drawRenderableImage(RenderableImage img, AffineTransform xform) { drawRenderedImage(img.createDefaultRendering(), xform); } /** * @see Graphics2D#drawRenderedImage(RenderedImage, AffineTransform) */ public void drawRenderedImage(RenderedImage img, AffineTransform xform) { BufferedImage image = null; if (img instanceof BufferedImage) { image = (BufferedImage)img; } else { ColorModel cm = img.getColorModel(); int width = img.getWidth(); int height = img.getHeight(); WritableRaster raster = cm.createCompatibleWritableRaster(width, height); boolean isAlphaPremultiplied = cm.isAlphaPremultiplied(); Hashtable properties = new Hashtable(); String[] keys = img.getPropertyNames(); if (keys!=null) { for (int i = 0; i < keys.length; i++) { properties.put(keys[i], img.getProperty(keys[i])); } } BufferedImage result = new BufferedImage(cm, raster, isAlphaPremultiplied, properties); img.copyData(raster); image=result; } drawImage(image, xform, null); } /** * This is not yet implemented * * @param x coordinate * @param y coordinate * @param w width * @param h height * @param aw a-width * @param ah a-height */ public void drawRoundRect(int x,int y,int w,int h,int aw,int ah) { } //============ Oval operations ======================= /** * Draws a string using a AttributedCharacterIterator. *

This is not supported yet, as I have no idea what an * AttributedCharacterIterator is. *

This method is new to the Java2 API. */ public void drawString(java.text.AttributedCharacterIterator aci, float x,float y) { } /** * Draws a string using a AttributedCharacterIterator. *

This is not supported yet, as I have no idea what an * AttributedCharacterIterator is. *

This method is new to the Java2 API. */ public void drawString(java.text.AttributedCharacterIterator aci, int x,int y) { } public void drawString(String s,float x,float y) { newTextBlock(x, y); pw.println(PDFStringHelper.makePDFString(s)+" Tj"); } /** * This draws a string. * * @oaran s String to draw * @param x coordinate * @param y coordinate */ public void drawString(String s,int x,int y) { newTextBlock(x,y); pw.println(PDFStringHelper.makePDFString(s)+" Tj"); } /** * @see Graphics2D#fill(Shape) */ public void fill(Shape s) { followPath(s, FILL); } /** *

Not implemented

* * @param x an int value * @param y an int value * @param width an int value * @param height an int value * @param raised a boolean value */ public void fill3DRect(int x, int y, int width, int height, boolean raised) { // Not implemented } /** * Fills an arc, joining the start and end coordinates * @param x coordinate * @param y coordinate * @param w width * @param h height * @param sa Start angle * @param aa End angle */ public void fillArc(int x,int y,int w,int h,int sa,int aa) { // here we fool the optimizer. We force any open path to be closed, // then draw the arc. Finally, as the optimizer hasn't stroke'd the // path, we close and fill it, and mark the Stroke as closed. // // Note: The lineto to the centre of the object is required, because // the fill only fills the arc. Skipping this includes an extra // chord, which isn't correct. Peter May 31 2000 closeBlock(); drawArc(x,y,w,h,sa,aa); lineto(x+(w>>1),y+(h>>1)); closeBlock("b"); // closepath and fill } //============ Extension operations ============================== // These are extensions, and provide access to PDF Specific // operators. /** *

Draws a filled oval

* * @param x coordinate * @param y coordinate * @param w width * @param h height */ public void fillOval(int x,int y,int w,int h) { fillArc(x, y, w, h, 0, 360); } //============ Polygon operations ======================= /** * Fills a polygon. * @param xp Array of x coordinates * @param yp Array of y coordinates * @param np number of points in polygon */ public void fillPolygon(int[] xp,int[] yp,int np) { closeBlock(); // finish off any previous paths polygon(xp,yp,np); closeBlock("b"); // closepath, fill and stroke } //============ Image operations ======================= /** * Fills a rectangle with the current colour * * @param x coordinate * @param y coordinate * @param w width * @param h height */ public void fillRect(int x,int y,int w,int h) { // end any path & stroke. This ensures the fill is on this // rectangle, and not on any previous graphics closeBlock(); drawRect(x,y,w,h); closeBlock("B"); // rectangle, fill stroke } //============ Round Rectangle operations ======================= /** * This is not yet implemented * * @param x coordinate * @param y coordinate * @param w width * @param h height * @param aw a-width * @param ah a-height */ public void fillRoundRect(int x,int y,int w,int h,int aw,int ah) { } /////////////////////////////////////////////// // // // implementation specific methods // // private void followPath(Shape s, int drawType) { PathIterator points; if (s==null) return; if (drawType==STROKE) { if (!(stroke instanceof BasicStroke)) { s = stroke.createStrokedShape(s); followPath(s, FILL); return; } } // if (drawType==STROKE) { // setStrokeDiff(stroke, oldStroke); // oldStroke = stroke; // setStrokePaint(); // } // else if (drawType==FILL) // setFillPaint(); points = s.getPathIterator(IDENTITY); int segments = 0; float[] coords = new float[6]; while(!points.isDone()) { segments++; int segtype = points.currentSegment(coords); switch(segtype) { case PathIterator.SEG_CLOSE: pw.print("h "); break; case PathIterator.SEG_CUBICTO: curveto(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]); break; case PathIterator.SEG_LINETO: lineto(coords[0], coords[1]); break; case PathIterator.SEG_MOVETO: moveto(coords[0], coords[1]); break; case PathIterator.SEG_QUADTO: curveto(coords[0], coords[1], coords[2], coords[3]); break; } points.next(); } switch (drawType) { case FILL: if (segments > 0) { if (points.getWindingRule() == PathIterator.WIND_EVEN_ODD) closeBlock("B*"); else closeBlock("B"); } break; case STROKE: if (segments > 0) closeBlock("S"); break; case CLIP: default: //drawType==CLIP if (segments == 0) drawRect(0, 0, 0, 0); if (points.getWindingRule() == PathIterator.WIND_EVEN_ODD) closeBlock("W*"); else closeBlock("W"); } } /** * @see Graphics2D#getBackground() */ public Color getBackground() { return background; } /** * Returns the Shape of the clipping region * As my JDK docs say, this may break with Java 2D. * @return Shape of the clipping region */ public Shape getClip() { return null; } /** * Returns the Rectangle that fits the current clipping region * @return the Rectangle that fits the current clipping region */ public Rectangle getClipBounds() { return clipRectangle; } //============ Color operations ======================= /** * Returns the current pen Colour * @return the current pen Colour */ public Color getColor() { return (paint instanceof Color) ? (Color) paint : Color.black; } /** * @see Graphics2D#getComposite() */ public Composite getComposite() { return composite; } /** * @see Graphics2D#getDeviceConfiguration() */ public GraphicsConfiguration getDeviceConfiguration() { return dg2.getDeviceConfiguration(); } /** * Return's the current font. * @return the current font. */ public Font getFont() { if(font==null) setFont(new Font("SansSerif",Font.PLAIN,12)); return font; } /** * Returns the FontMetrics for a font. *

This doesn't work correctly. Perhaps having some way of mapping * the base 14 fonts to our own FontMetrics implementation? * @param font The java.awt.Font to return the metrics for * @return FontMetrics for a font */ public FontMetrics getFontMetrics(Font font) { Frame dummy = new Frame(); dummy.addNotify(); Image image = dummy.createImage(100, 100); if (image == null) { System.err.println("getFontMetrics: image is null"); } Graphics graphics = image.getGraphics(); return graphics.getFontMetrics(font); } /** * @see Graphics2D#getFontRenderContext() */ public FontRenderContext getFontRenderContext() { boolean antialias = RenderingHints.VALUE_TEXT_ANTIALIAS_ON.equals(getRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING)); boolean fractions = RenderingHints.VALUE_FRACTIONALMETRICS_ON.equals(getRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS)); return new FontRenderContext(new AffineTransform(), antialias, fractions); } /** * Returns the associated PDFPage for this graphic * @return the associated PDFPage for this graphic */ public PDFPage getPage() { return page; } /** * Returns the current pen Colour * @return the current pen Colour */ public Paint getPaint() { return paint; } /** * @param arg0 a key * @return the rendering hint */ public Object getRenderingHint(Key arg0) { return rhints.get(arg0); } /** * @see Graphics2D#getRenderingHints() */ public RenderingHints getRenderingHints() { return rhints; } /** * @see Graphics2D#getStroke() */ public Stroke getStroke() { return originalStroke; } /** * @see Graphics2D#getTransform() */ public AffineTransform getTransform() { return new AffineTransform(oTransform); } /** * Returns the PrintWriter handling the underlying stream * @return the PrintWriter handling the underlying stream */ public PrintWriter getWriter() { return pw; } /** * @see Graphics2D#hit(Rectangle, Shape, boolean) */ public boolean hit(Rectangle rect, Shape s, boolean onStroke) { if (onStroke) { s = stroke.createStrokedShape(s); } s = transform.createTransformedShape(s); Area area = new Area(s); if (clip != null) area.intersect(clip); return area.intersects(rect.x, rect.y, rect.width, rect.height); } /** * This initialises the stream by saving the current graphics state, and * setting up the default line width (for us). * * It also sets up the instance ready for graphic operations and any * optimisations. * *

For child instances, the stream is already open, so this should keep * things happy. */ private void init() { PageFormat pf = page.getPageFormat(); // save graphics state (restored by dispose) if(child) { pw.print("q "); } // now initialise the instance //setColor(Color.black); paint = Color.black; // possible: if parent.color is not black, then force black? // must check to see what AWT does? // Original User Space Transform (identity) oTransform = new AffineTransform(); // Transform from Java Space to PDF Space pTransform = new AffineTransform(); pTransform.translate(0, pf.getHeight()); pTransform.scale(1d, -1d); // Combined Transform User->Java->PDF transform = new AffineTransform(oTransform); transform.concatenate(pTransform); // Set the line width setStroke(DEF_STROKE); } /** * This is called by PDFPage when creating a Graphcis instance. * @param page The PDFPage to draw onto. */ protected void init(PDFPage page) { this.page = page; // We are the parent instance child = false; // Now create a stream to store the graphics in PDFStream stream = new PDFStream(); // To view detail in uncompressed format comment out the next line stream.setDeflate(true); page.getPDFDocument().add(stream); page.add(stream); pw = stream.getWriter(); // initially, we are limited to the page size clipRectangle = page.getImageableArea(); // finally initialise the stream init(); } /** * This method is used internally by create() and by the PDFJob class * @param page PDFPage to draw into * @param pw PrintWriter to use */ protected void init(PDFPage page,PrintWriter pw) { this.page = page; this.pw = pw; // In this case, we didn't create the stream (our parent did) // so child is true (see dispose) child = true; // finally initialise the stream init(); } /** * This adds a line segment to the current path * @param x coordinate * @param y coordinate */ public void lineto(double x,double y) { newPath(); // no optimisation here as it may introduce errors on decimal coordinates. pw.print(cxy(x,y)+"l "); lx=(float)x; ly=(float)y; } /** * This adds a line segment to the current path * @param x coordinate * @param y coordinate */ public void lineto(int x,int y) { newPath(); if(lx!=x && ly!=y) pw.print(cxy(x,y)+"l "); lx=x; ly=y; } /** * This moves the current drawing point. * @param x coordinate * @param y coordinate */ public void moveto(double x,double y) { newPath(); // no optimisation here as it may introduce errors on decimal coordinates. pw.print(cxy(x,y)+"m "); lx=(float)x; ly=(float)y; } /** * This moves the current drawing point. * @param x coordinate * @param y coordinate */ public void moveto(int x,int y) { newPath(); if(lx!=x || ly!=y) pw.print(cxy(x,y)+"m "); lx=x; ly=y; } /** * Functions that draw lines should start by calling this. It starts a * new path unless inStroke is set, in that case it uses the existing path */ void newPath() { if(inText) { closeBlock(); } if(!inStroke) { if(pre_np!=null) { pw.print(pre_np); // this is the prefix set by setOrientation() pre_np = null; } pw.print("n "); } inText=false; inStroke=true; // an unlikely coordinate to fool the moveto() optimizer lx = ly = -9999; } /** *

Functions that draw text should start by calling this. It starts a text * block (accounting for media orientation) unless we are already in a Text * block.

* *

It also handles if the font has been changed since the current text * block was started, so your function will be current.

* * @param x x coordinate in java space * @param y y coordinate in java space */ void newTextBlock(float x,float y) { // close the current path if there is one if(inStroke) { closeBlock(); } // create the text block if one is not current. If we are, the newFont // condition at the end catches font changes if(!inText) { // This ensures that there is a font available getFont(); pw.print("q BT "); tx=ty=0; // produce the text matrix for the media // switch(mediaRot) { // case PageFormat.PORTRAIT: // Portrait // //pw.println("1 0 0 1 0 0 Tm"); // break; // // case PageFormat.LANDSCAPE: // Landscape // pw.println("0 1 -1 0 0 0 Tm"); // rotate // break; // // case 180: // Inverted Portrait // pw.println("1 0 0 -1 0 0 Tm"); // break; // // case PageFormat.REVERSE_LANDSCAPE: // Seascape // pw.println("0 -1 1 0 0 0 Tm"); // rotate // break; // } // move the text cursor by an absolute amount pw.print(txy(x,y)+"Td "); } else { // move the text cursor by a relative amount //int ox=x-tx, oy=ty-y; //pw.print(""+ox+" "+oy+" Td "); //pw.print(cwh(x-tx,y-ty)+"Td "); pw.print(twh(x,y,tx,ty)+"Td "); } // preserve the coordinates for the next time tx = x; ty = y; if(newFont || !inText) pw.print(pdffont.getName()+" "+font.getSize()+" Tf "); // later add colour changes here (if required) inStroke = newFont = false; inText = true; } /** * This is used to add a polygon to the current path. * Used by drawPolygon(), drawPolyline() and fillPolygon() etal * @param xp Array of x coordinates * @param yp Array of y coordinates * @param np number of points in polygon * @see #drawPolygon * @see #drawPolyline * @see #fillPolygon */ public void polygon(int[] xp,int[] yp,int np) { // newPath() not needed here as moveto does it ;-) moveto(xp[0],yp[0]); for(int i=1;iSets the clipping region to that of a Shape. * @param s Shape to clip to. */ public void setClip(Shape s) { Rectangle r = s.getBounds(); setClip(r.x,r.y,r.width,r.height); } /** * Sets the color for drawing * @param c Color to use */ public void setColor(Color c) { setPaint(c); } /** * @see Graphics2D#setComposite(Composite) */ public void setComposite(Composite comp) { this.composite = comp; } /** * This extension sets the line width to the default of 1mm which is what * Java uses when drawing to a PrintJob. */ public void setDefaultLineWidth() { closeBlock(); // draw any path before we change the line width pw.println("1 w"); } /** * This sets the font. * @param f java.awt.Font to set to. */ public void setFont(Font f) { // optimize: Save some space if the font is already the current one. if(font!=f) { font = f; pdffont = page.getFont("/Type1",f.getName(),f.getStyle()); // mark the font as changed newFont = true; } } private void setLineCap(int cap) { int lineCap = 0; switch (cap) { case BasicStroke.JOIN_MITER: lineCap = 0; break; case BasicStroke.JOIN_ROUND: lineCap = 1; break; case BasicStroke.JOIN_BEVEL: lineCap = 2; break; } if (this.lineCap != lineCap) { closeBlock(); // draw any path before we change the line width this.lineCap = lineCap; pw.println(""+lineCap+" J"); } } private void setLineJoin(int join) { int lineJoin = 0; switch (join) { case BasicStroke.JOIN_MITER: lineJoin = 0; break; case BasicStroke.JOIN_ROUND: lineJoin = 1; break; case BasicStroke.JOIN_BEVEL: lineJoin = 2; break; } if (this.lineJoin != lineJoin) { closeBlock(); // draw any path before we change the line width this.lineJoin = lineJoin; pw.println(""+lineJoin+" j"); } } /** * This extension allows the width of the drawn line to be set * @param width Line width in pdf graphic units (points) */ public void setLineWidth(float width) { if (width != this.lineWidth) { closeBlock(); // draw any path before we change the line width this.lineWidth = width; pw.println(""+width+" w"); } } private void setMiterLimit(float limit) { if (limit != this.miterLimit) { closeBlock(); // draw any path before we change the line width this.miterLimit = limit; pw.println(""+limit+" M"); } } /** * Sets the paint for drawing * @param paint Paint to use */ public void setPaint(Paint paint) { this.paint = paint; if (paint instanceof Color) { Color c = (Color) paint; double r = ((double) c.getRed()) / 255.0; double g = ((double) c.getGreen()) / 255.0; double b = ((double) c.getBlue()) / 255.0; closeBlock(); // This ensures any paths are drawn in the previous // colours pw.println("" + r + " " + g + " " + b + " rg " + r + " " + g + " " + b + " RG"); } } /** * Not implemented, as this is not supported in the PDF specification. */ public void setPaintMode() { } /** * Sets a rendering hint * @param arg0 * @param arg1 */ public void setRenderingHint(Key arg0, Object arg1) { if (arg1 != null) { rhints.put(arg0, arg1); } else { rhints.remove(arg0); } } // Add Graphics2D methods. /** * @see Graphics2D#setRenderingHints(Map) */ public void setRenderingHints(Map hints) { rhints.clear(); rhints.putAll(hints); } /** * @see Graphics2D#setStroke(Stroke) */ public void setStroke(Stroke s) { originalStroke = s; this.stroke = transformStroke(s); if (stroke instanceof BasicStroke) { BasicStroke bs = (BasicStroke) stroke; setLineCap(bs.getEndCap()); setLineJoin(bs.getLineJoin()); setLineWidth(bs.getLineWidth()); setMiterLimit(bs.getMiterLimit()); // TODO: Line dash pattern } } /** * @see Graphics2D#setTransform(AffineTransform) */ public void setTransform(AffineTransform t) { // Save copy of original transform. oTransform = t; // Working copy of transform transform = new AffineTransform(t); // Concatenate Java Space to PDF Space transform transform.concatenate(pTransform); this.stroke = transformStroke(originalStroke); } /** * Not implemented, as this is not supported in the PDF specification. * @param c1 Color to xor with */ public void setXORMode(Color c1) { } //============ Text operations ======================= /** * @see Graphics2D#shear(double, double) */ public void shear(double shx, double shy) { transform.shear(shx, shy); } /** * @see Graphics2D#transform(AffineTransform) */ public void transform(AffineTransform tx) { transform.concatenate(tx); this.stroke = transformStroke(originalStroke); } private Stroke transformStroke(Stroke stroke) { if (!(stroke instanceof BasicStroke)) return stroke; BasicStroke st = (BasicStroke)stroke; float scale = (float)Math.sqrt(Math.abs(transform.getDeterminant())); float dash[] = st.getDashArray(); if (dash != null) { for (int k = 0; k < dash.length; ++k) dash[k] *= scale; } return new BasicStroke(st.getLineWidth() * scale, st.getEndCap(), st.getLineJoin(), st.getMiterLimit(), dash, st.getDashPhase() * scale); } /** * @see Graphics2D#translate(double, double) */ public void translate(double tx, double ty) { transform.translate(tx, ty); trax = (float) tx; tray = (float) ty; } /** * @see Graphics#translate(int, int) */ public void translate(int x, int y) { translate((double)x, (double)y); } /** * Converts the Java space coordinates into pdf text space. * @param x coordinate * @param y coordinate * @param tx coordinate * @param ty coordinate * @return String containing the coordinates in PDF text space */ private String twh(float x,float y,float tx,float ty) { float nx=x, ny=y; float ntx=tx, nty=ty; int mh = (int) page.getPageFormat().getHeight(); int sx=1,sy=1; // switch(mediaRot) // { // case PageFormat.PORTRAIT: // Portrait //nx = x; ny = mh - y; nty = mh - ty; // break; // // case PageFormat.LANDSCAPE: // // Landscape // //nx = y; // //ny = x; // //ntx = ty; // //nty = tx; // //sy=-1; // nx = x; // ny = -y; // ntx = tx; // nty = -ty; // //sy=-1; // break; // // case 180: // // Inverse Portrait // // to be completed // nx = mw - x; // //ny = y; // break; // // case PageFormat.REVERSE_LANDSCAPE: // // Seascape // // to be completed // nx = mw - y; // ny = mh - x; // break; // } nx = sx*(nx-ntx); ny = sy*(ny-nty); return ""+df.format(nx)+" "+df.format(ny)+" "; } /** * Converts the Java space coordinates into pdf text space. * @param x coordinate * @param y coordinate * @return String containing the coordinates in PDF text space */ private String txy(float x,float y) { // float nx=x, ny=y; // int mh = (int) page.getPageFormat().getHeight(); Point2D ptSrc = new Point2D.Float(x, y); Point2D ptDst = new Point2D.Float(); transform.transform(ptSrc, ptDst); // // handle any translations // x+=trax; // y+=tray; // // nx = x; // ny = mh - y; // // System.out.println("\ntxy(" + ptSrc.getX() + ", " + ptSrc.getY() + ")"); // System.out.println("Old [" + nx + "," + ny + "]"); // System.out.println("Trn [" + ptDst.getX() + ", " + ptDst.getY() + "]"); // // return ""+df.format(nx)+" "+df.format(ny)+" "; return ""+df.format(ptDst.getX())+" "+df.format(ptDst.getY())+" "; } } // end class PDFGraphics \ No newline at end of file diff --git a/libsrc/gnujpdf/src/gnu/jpdf/PDFImage.java b/libsrc/gnujpdf/src/gnu/jpdf/PDFImage.java new file mode 100644 index 000000000..7eec37f82 --- /dev/null +++ b/libsrc/gnujpdf/src/gnu/jpdf/PDFImage.java @@ -0,0 +1,405 @@ +/* + * $Id: PDFImage.java,v 1.2 2007/08/26 18:56:35 gil1 Exp $ + * + * $Date: 2007/08/26 18:56:35 $ + * + * 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 + */ +package gnu.jpdf; + +import java.awt.Image; +import java.awt.image.*; +import java.io.*; +import java.util.*; +import java.util.zip.*; + +/** + *

This implements the Image XObject. Calling one of the + * drawImage methods of PDFGraphics will + * put all the necessary code into the pdf file, and the image will + * be encoded in ascii base 85, then deflated in zip format.

+ * + * @author Eric Z. Beard (original version by Peter Mount) + * @author Matthew Hreljac, mhreljac@hotmail.com + * @version $Revision: 1.2 $, $Date: 2007/08/26 18:56:35 $ + */ +public class PDFImage extends PDFStream implements ImageObserver, Serializable +{ + /* + * NOTE: The original class is the work of Peter T. Mount, who released it + * in the uk.org.retep.pdf package. It was modified by Eric Z. Beard as + * follows: + * The package name was changed to gnu.jpdf. + * The formatting was changed a little bit. + * Images were not yet implemented, so the core of this + * class was mostly rewritten + * It is still licensed under the LGPL. + * Got some help with base85 methods from Mathew Hreljac + */ + + + + // Dimensions of the image. + private int width; + private int height; + private Image img; + private String name; + + + /** + * Creates a new PDFImage instance. + * + */ + public PDFImage() + { + super("/XObject"); + } + + /** + * Creates a new PDFImage instance. + * + * @param img an Image value + */ + public PDFImage(Image img) { + this(); + setImage(img,img.getWidth(this), img.getHeight(this), this); + } + + /** + * Creates a new PDFImage instance. + * + * @param img an Image value + * @param x an int value + * @param y an int value + * @param w an int value + * @param h an int value + * @param obs an ImageObserver value + */ + public PDFImage(Image img,ImageObserver obs) { + this(); + setImage(img, img.getWidth(this), img.getHeight(this), obs); + } + + + /** + * Get the value of width. + * @return value of width. + */ + public int getWidth() { + return width; + } + + /** + * Set the value of width. + * @param v Value to assign to width. + */ + public void setWidth(int v) { + this.width = v; + } + + /** + * Get the value of height. + * @return value of height. + */ + public int getHeight() { + return height; + } + + /** + * Set the value of height. + * @param v Value to assign to height. + */ + public void setHeight(int v) { + this.height = v; + } + + + /** + * Set the name + * + * @param n a String value + */ + public void setName(String n) { + name = n; + } + + /** + * Get the name + * + * @return a String value + */ + public String getName() { + return name; + } + + /** + * Set the image + * + * @param img an Image value + * @param x an int value + * @param y an int value + * @param w an int value + * @param h an int value + * @param obs an ImageObserver value + */ + public void setImage(Image img,int w,int h,ImageObserver obs) { + this.img = img; + width = w; + height = h; + } + + + + /** + *

Adobe's base 85 does not follow the format used by ipv6 + * addresses. It simply starts with 33 and goes straight up without + * skipping any characters

+ * + *

Parts of this method contributed by Mathew Hreljac

+ * + * @param stringToEncode a String value + * @return a String value + */ + private String base85Encoding(String stringToEncode) + throws NumberFormatException { + if ((stringToEncode == null) || (stringToEncode.length() == 0)) { + //System.out.println("PDFImage.base85Encoding() null or blank String"); + return ""; + } + if ((stringToEncode.length() > 8) || + ((stringToEncode.length() % 2) != 0)) { + System.out.println("PDFImage.base85Encoding, Incorrect tuple length: " + + stringToEncode.length()); + return ""; + } + //System.out.println("str: " + stringToEncode); + // String buffer to use to return the String encoding + StringBuffer sb = new StringBuffer(); + + // Deal with a partial tuple (less than 8 hex digits) + // From Adobe's docs: + // "Given n (1, 2 or 3) bytes of binary data, the encoding first + // appends 4 - n zero bytes to make a complete 4-tuple. This 4-tuple + // is encoded in the usual way, but without applying the special + // z-case. Finally, only the first n+1 characters of the resulting + // 5-tuple are written out. Those characters are immediately followed + // by the EOD marker, ~>" + + int numHexDigits = stringToEncode.length() / 2; + int numAppendBytes = 4 - numHexDigits; + for (int i = 0; i < numAppendBytes; i++) { + stringToEncode += "00"; + } + Vector digitVector = new Vector(); + long number = Long.parseLong(stringToEncode, 16); + int remainder = 0; + + while (number >= 85) { + remainder = (int) (number % 85); + number = number / 85; + digitVector.add( 0, new Integer( remainder ) ); + } + digitVector.add( 0, new Integer( (int)number ) ); + + for ( int i = 0; i < digitVector.size(); i++) { + char c = (char) (((Integer)digitVector.elementAt(i)).intValue() + 33); + sb.append(c); + } + String tuple = sb.toString(); + int len = tuple.length(); + switch (len) { + case 1: tuple = "!!!!" + tuple; break; + case 2: tuple = "!!!" + tuple; break; + case 3: tuple = "!!" + tuple; break; + case 4: tuple = "!" + tuple; break; + default: break; + } // end switch + //System.out.println("enc tuple: " + tuple); + + return (tuple); + } // end base85encoding + + + + /** + * Writes the image to the stream + * + * @param os an OutputStream value + * @exception IOException if an error occurs + */ + public void writeStream(OutputStream os) throws IOException { + // This is a non-deflated stream + /* + os.write("/Length ".getBytes()); + // Accout for stream\n ... >\nendstream + os.write(Integer.toString(buf.size() + 18).getBytes()); + os.write("\n/Filter /ASCII85Decode".getBytes()); + os.write("\n>>\nstream\n".getBytes()); + buf.writeTo(os); + os.write(">\nendstream\nendobj\n\n".getBytes()); + */ + ByteArrayOutputStream b = new ByteArrayOutputStream(); + DeflaterOutputStream dos = new DeflaterOutputStream(b); + buf.writeTo(dos); + dos.finish(); + dos.close(); + + // FlatDecode is compatible with the java.util.zip.Deflater class + //os.write("/Filter [/FlateDecode /ASCIIHexDecode]\n".getBytes()); + os.write("/Filter [/FlateDecode /ASCII85Decode]\n".getBytes()); + os.write("/Length ".getBytes()); + os.write(Integer.toString(b.size()).getBytes()); + os.write("\n>>\nstream\n".getBytes()); + b.writeTo(os); + os.write("\nendstream\nendobj\n".getBytes()); + + } // end writeStream + + + /** + *

Compression needs to be improved here

+ * + * @param os OutputStream to send the object to + * @exception IOException on error + */ + public void write(OutputStream os) throws IOException + { + writeStart(os); + + // write the extra details + os.write("/Subtype /Image\n/Name ".getBytes()); + os.write(name.getBytes()); + os.write("\n/Width ".getBytes()); + os.write(Integer.toString(width).getBytes()); + os.write("\n/Height ".getBytes()); + os.write(Integer.toString(height).getBytes()); + os.write("\n/BitsPerComponent 8\n/ColorSpace /DeviceRGB\n".getBytes()); + + // write the pixels to the stream + //System.err.println("Processing image "+width+"x"+height+" pixels"); + ByteArrayOutputStream bos = getStream(); + + int w = width; + int h = height; + int x = 0; + int y = 0; + int[] pixels = new int[w * h]; + PixelGrabber pg = new PixelGrabber(img, x, y, w, h, pixels, 0, w); + try { + pg.grabPixels(); + } catch (InterruptedException e) { + System.err.println("interrupted waiting for pixels!"); + return; + } + if ((pg.getStatus() & ImageObserver.ABORT) != 0) { + System.err.println("image fetch aborted or errored"); + return; + } + StringBuffer out = new StringBuffer(); + for (int j = 0; j < h; j++) { + for (int i = 0; i < w; i++) { + //System.out.print("p[" + j * w + i+ "]=" + pixels[j * w + i] + "."); + out.append(handlePixel(x+i, y+j, pixels[j * w + i])); + if (out.toString().length() >= 8) { + String tuple = out.substring(0, 8); + out.delete(0, 8); + // Convert !!!!! to 'z' + String encTuple = base85Encoding(tuple); + if (encTuple.equals("!!!!!")) { + encTuple = "z"; + } + bos.write(encTuple.getBytes()); + } + } + } + // This should be the only partial tuple case, + + String lastTuple = base85Encoding(out.toString()); + //System.out.println("lastTuple: " + lastTuple); + bos.write(lastTuple.getBytes()); + bos.write("~".getBytes()); + + + //System.out.println("Processing done"); + + // this will write the actual stream + setDeflate(false); + + writeStream(os); + + // Note: we do not call writeEnd() on streams! + } + + + + /** + *

Converts a pixel to a hex string

+ * + * @param x an int value + * @param y an int value + * @param p an int value + * @return a String value + */ + public static String handlePixel(int x, int y, int p) { + int alpha = (p >> 24) & 0xff; + int red = (p >> 16) & 0xff; + int green = (p >> 8) & 0xff; + int blue = (p ) & 0xff; + String redHex = Integer.toHexString(red); + String greenHex = Integer.toHexString(green); + String blueHex = Integer.toHexString(blue); + if (redHex.length() == 1) { + redHex = "0" + redHex; + } + if (greenHex.length() == 1) { + greenHex = "0" + greenHex; + } + if (blueHex.length() == 1) { + blueHex = "0" + blueHex; + } + return redHex + greenHex + blueHex; + } // end handlePixel + + + + + + /** + * Describe imageUpdate method here. + * + * @param img an Image value + * @param infoflags an int value + * @param x an int value + * @param y an int value + * @param w an int value + * @param h an int value + * @return a boolean value + */ + public boolean imageUpdate(Image img,int infoflags,int x,int y,int w,int h) { + System.err.println("img="+img+"\ninfoflags="+infoflags+ + "\nx="+x+" y="+y+" w="+w+" h="+h); + //if(img == this.img) { + if(infoflags==ImageObserver.WIDTH) + width = w; + if(infoflags==ImageObserver.HEIGHT) + height = h; + + //return true; + //} + return false; + } + + +} // end class PDFImage diff --git a/libsrc/gnujpdf/src/gnu/jpdf/PDFInfo.java b/libsrc/gnujpdf/src/gnu/jpdf/PDFInfo.java new file mode 100644 index 000000000..fb216295b --- /dev/null +++ b/libsrc/gnujpdf/src/gnu/jpdf/PDFInfo.java @@ -0,0 +1,193 @@ +/* + * $Id: PDFInfo.java,v 1.2 2007/08/26 18:56:35 gil1 Exp $ + * + * $Date: 2007/08/26 18:56:35 $ + * + * + * 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 + * + */ +package gnu.jpdf; + +import java.io.*; + +/** + *

This class stores details of the author, the PDF generator etc. + * The values are accessible via the PDFDocument class.

+ * + * @author Peter T. Mount + * @author Eric Z. Beard, ericzbeard@hotmail.com + * @version $Revision: 1.2 $, $Date: 2007/08/26 18:56:35 $ + + */ +public class PDFInfo extends PDFObject +{ + private String author; + private String creator; + private String title; + private String subject; + private String keywords; + + /** + * This constructs a minimal info object + */ + public PDFInfo() { + super(null); + } + + /** + * @param title Title of this document + */ + public PDFInfo(String title) { + this(); + this.title = title; + } + + + + /** + * Get the value of author. + * @return value of author. + */ + public String getAuthor() { + return author; + } + + /** + * Set the value of author. + * @param v Value to assign to author. + */ + public void setAuthor(String v) { + this.author = v; + } + + + + /** + * PDF has two values, a Creator and a Producer. The creator field is + * available for calling code. The producer is fixed by this library. + * Get the value of creator. + * @return value of creator. + */ + public String getCreator() { + return creator; + } + + /** + * Set the value of creator. + * @param v Value to assign to creator. + */ + public void setCreator(String v) { + this.creator = v; + } + + /** + * Get the value of title. + * @return value of title. + */ + public String getTitle() { + return title; + } + + /** + * Set the value of title. + * @param v Value to assign to title. + */ + public void setTitle(String v) { + this.title = v; + } + + /** + * Get the value of subject. + * @return value of subject. + */ + public String getSubject() { + return subject; + } + + /** + * Set the value of subject. + * @param v Value to assign to subject. + */ + public void setSubject(String v) { + this.subject = v; + } + + /** + * Get the value of keywords. + * @return value of keywords. + */ + public String getKeywords() { + return keywords; + } + + /** + * Set the value of keywords. + * @param v Value to assign to keywords. + */ + public void setKeywords(String v) { + this.keywords = v; + } + + /** + * @param os OutputStream to send the object to + * @exception IOException on error + */ + public void write(OutputStream os) throws IOException { + // Write the object header + writeStart(os); + + // now the objects body + + if(author!=null) { + os.write("/Author (".getBytes()); + os.write(PDFStringHelper.makePDFString(author).getBytes()); + os.write(")\n".getBytes()); + } + + if(creator!=null) { + os.write("/Creator (".getBytes()); + os.write(PDFStringHelper.makePDFString(creator).getBytes()); + os.write(")\n".getBytes()); + } + + os.write("/Producer ".getBytes()); + os.write(PDFStringHelper.makePDFString("gnujpdf - gnujpdf.sourceforge.net") + .getBytes()); + os.write("\n".getBytes()); + + if(title!=null) { + os.write("/Title ".getBytes()); + os.write(PDFStringHelper.makePDFString(title).getBytes()); + os.write("\n".getBytes()); + } + + if(subject!=null) { + os.write("/Subject (".getBytes()); + os.write(PDFStringHelper.makePDFString(subject).getBytes()); + os.write(")\n".getBytes()); + } + + if(keywords!=null) { + os.write("/Keywords (".getBytes()); + os.write(PDFStringHelper.makePDFString(keywords).getBytes()); + os.write(")\n".getBytes()); + } + + // finish off with its footer + writeEnd(os); + } // end write + +} // end class PDFInfo diff --git a/libsrc/gnujpdf/src/gnu/jpdf/PDFJob.java b/libsrc/gnujpdf/src/gnu/jpdf/PDFJob.java new file mode 100644 index 000000000..6d296d234 --- /dev/null +++ b/libsrc/gnujpdf/src/gnu/jpdf/PDFJob.java @@ -0,0 +1,405 @@ +/* + * $Id: PDFJob.java,v 1.3 2007/08/26 18:56:35 gil1 Exp $ + * + * $Date: 2007/08/26 18:56:35 $ + * + * 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 + */ +package gnu.jpdf; + +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.PrintGraphics; +import java.awt.PrintJob; +import java.awt.Rectangle; +import java.awt.print.PageFormat; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.io.Serializable; + + +/** + *

This class extends awt's PrintJob, to provide a simple method of writing + * PDF documents.

+ * + *

You can use this with any code that uses Java's printing mechanism. It + * does include a few extra methods to provide access to some of PDF's features + * like annotations, or outlines.

+ * + * + * @author Peter T Mount, http://www.retep.org.uk/pdf/ + * @author Eric Z. Beard, ericzbeard@hotmail.com + * @version $Revision: 1.3 $, $Date: 2007/08/26 18:56:35 $ + */ +public class PDFJob extends PrintJob implements Serializable +{ + /* + * NOTE: The original class is the work of Peter T. Mount, who released it + * in the uk.org.retep.pdf package. It was modified by Eric Z. Beard as + * follows: + * The package name was changed to gnu.jpdf + * The formatting was changed a little bit. + * This used to subclass an abstract class with the same name in + * another package to support jdk1.1. Now it's one concrete class, + * with no jdk1.1 support + * Instances of PDFJob come directly from constructors, not + * static methods in PDFDocument (which used to be PDF) + * It is still licensed under the LGPL. + */ + + + /** + * This is the OutputStream the PDF file will be written to when complete + * Note: This is transient, as it's not valid after being Serialized. + */ + protected transient OutputStream os; + + /** + * This is the PDF file being constructed + */ + protected PDFDocument pdfDocument; + + /** + * This is the current page being constructed by the last getGraphics() + * call + */ + protected PDFPage page; + + /** + * This is the page number of the current page + */ + protected int pagenum; + + + // Constructors + + /** + *

This constructs the job. This method must be used when creating a + * template pdf file, ie one that is Serialised by one application, and + * then restored by another.

+ * + *

ezb 20011115 - Haven't done anything with templates yet, don't know + * how/if they are implemented

+ */ + public PDFJob() { + this(null); + } + + /** + *

This constructs the job. This is the primary constructor that + * will be used for creating pdf documents with this package. The + * specified output stream is a handle to the .pdf file you wish to + * create.

+ * + * @param os - OutputStream to use for the pdf output + */ + public PDFJob(OutputStream os) { + this(os, "PDF Doc"); + } + + /** + *

This constructs the job. This is the primary constructor that + * will be used for creating pdf documents with this package. The + * specified output stream is a handle to the .pdf file you wish to + * create.

+ * + *

Use this constructor if you want to give the pdf document a name + * other than the default of "PDF Doc"

+ * + * @param os - OutputStream to use for the pdf output + * @param title a String value + */ + public PDFJob(OutputStream os, String title) { + this.os = os; + this.pdfDocument = new PDFDocument(); + pagenum = 0; + pdfDocument.getPDFInfo().setTitle(title); + } + + + /** + *

This returns a graphics object that can be used to draw on a page. + * In PDF, this will be a new page within the document.

+ * + * @param orient - the int Orientation of the new page, + * as defined in PDFPage + * @return Graphics object to draw. + * @see PageFormat#PORTRAIT + * @see PageFormat#LANDSCAPE + * @see PageFormat#REVERSE_LANDSCAPE + */ + public Graphics getGraphics(int orient) { + // create a new page + page = new PDFPage(orient); + pdfDocument.add(page); + pagenum++; + + // Now create a Graphics object to draw onto the page + return new graphic(page,this); + } + + + /** + *

This returns a graphics object that can be used to draw on a page. + * In PDF, this will be a new page within the document.

+ * + * @param pageFormat PageFormat describing the page size + * @return Graphics object to draw. + */ + public Graphics getGraphics(PageFormat pageFormat) { + // create a new page + page = new PDFPage(pageFormat); + pdfDocument.add(page); + pagenum++; + + // Now create a Graphics object to draw onto the page + return new graphic(page,this); + } + + + + + /** + *

This writes the PDF document to the OutputStream, finishing the + * document.

+ */ + public void end() { + try { + pdfDocument.write(os); + } catch(IOException ioe) { + // Ideally we should throw this. However, PrintJob doesn't throw + // anything, so we will print the Stack Trace instead. + ioe.printStackTrace(); + } finally { + try { + if (os != null) { + os.close(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + // This should mark us as dead + os = null; + pdfDocument = null; + } + + + + /** + *

This returns a graphics object that can be used to draw on a page. + * In PDF, this will be a new page within the document.

+ * + *

This new page will by default be oriented as a portrait

+ * + * @return a Graphics object to draw to. + */ + public Graphics getGraphics() { + return getGraphics(PageFormat.PORTRAIT); + } + + + + /** + *

Returns the page dimension

+ * + * @return a Dimension instance, the size of the page + */ + public Dimension getPageDimension() { + if (page == null) { + System.err.println("PDFJob.getPageDimension(), page is null"); + } + return page.getDimension(); + } + + + /** + *

How about a setPageDimension(Rectangle media) ??

+ */ + + + /** + * This returns the page resolution. + * + *

This is the PDF (and Postscript) device resolution of 72 dpi + * (equivalent to 1 point).

+ * + * @return an int, the resolution in pixels per inch + */ + public int getPageResolution() { + return 72; + } + + + + + /** + *

In AWT's PrintJob, this would return true if the user requested that the + * file is printed in reverse order. For PDF's this is not applicable, so + * it will always return false.

+ * + * @return false + */ + public boolean lastPageFirst() { + return false; + } + + //======== END OF PrintJob extension ========== + + + + /** + * Returns the PDFDocument object for this document. + * Useful for gaining access to + * the internals of PDFDocument. + * @return the PDF object + */ + public PDFDocument getPDFDocument() { + return pdfDocument; + } + + /** + *

Returns the current PDFPage being worked on. Useful for working on + * Annotations (like links), etc.

+ * + * @return the PDFPage currently being constructed + */ + public PDFPage getCurrentPage() { + return page; + } + + /** + *

Returns the current page number. + * Useful if you need to include one in the document

+ * + * @return the int current page number + */ + public int getCurrentPageNumber() { + return pagenum; + } + + + + /** + *

This method attaches an outline to the current page being generated. + * When selected, the outline displays the top of the page.

+ * + * @param title a String, the title of the Outline + * @return a PDFOutline object that was created, + * for adding sub-outline's if required. + */ + public PDFOutline addOutline(String title) { + return page.addOutline(title); + } + + /** + *

This method attaches an outline to the current page being generated. + * When selected, the outline displays the specified region.

+ * + * @param title Outline title to attach + * @param x Left coordinate of region + * @param y Top coordinate of region + * @param w width of region + * @param h height of region + * @return the PDFOutline object created, + * for adding sub-outline's if required. + */ + public PDFOutline addOutline(String title,int x,int y,int w,int h) { + return page.addOutline(title,x,y,w,h); + } + + /** + * Convenience method: Adds a text note to the document. + * @param note Text of the note + * @param x Coordinate of note + * @param y Coordinate of note + * @param w Width of the note + * @param h Height of the note + * @return Returns the annotation, so other settings can be changed. + */ + public PDFAnnot addNote(String note,int x,int y,int w,int h) { + return page.addNote(note,x,y,w,h); + } + + + /** + *

This inner class extends PDFGraphics for the PrintJob.

+ * + *

Like with java.awt, Graphics instances created with PrintJob implement + * the PrintGraphics interface. Here we implement that method, and overide + * PDFGraphics.create() method, so all instances have this interface.

+ */ + class graphic extends PDFGraphics implements PrintGraphics { + /** + * The PDFJob we are linked with + */ + private PDFJob job; + + /** + * @param page to attach to + * @param job PDFJob containing this graphic + */ + graphic(PDFPage page,PDFJob job) { + super(); + this.init(page); + this.job = job; + } + + /** + * This is used by our version of create() + */ + graphic(PDFPage page,PDFJob job,PrintWriter pw) { + super(); + this.init(page,pw); + this.job = job; + } + + /** + * This returns a child instance of this Graphics object. As with AWT, + * the affects of using the parent instance while the child exists, + * is not determined. + * + *

This method is used to make a new Graphics object without + * going to a new page

+ * + *

Once complete, the child should be released with it's dispose() + * method which will restore the graphics state to it's parent. + * + * @return Graphics object + */ + public Graphics create() { + closeBlock(); + graphic g = new graphic(getPage(),job,getWriter()); + + // The new instance inherits a few items + g.clipRectangle = new Rectangle(clipRectangle); + + return (Graphics) g; + } + + /** + * This is the PrintGraphics interface + * @return PrintJob for this object + */ + public PrintJob getPrintJob() { + return (PrintJob)job; + } + + } // end inner class graphic + +} // end class PDFJob + diff --git a/libsrc/gnujpdf/src/gnu/jpdf/PDFObject.java b/libsrc/gnujpdf/src/gnu/jpdf/PDFObject.java new file mode 100644 index 000000000..e2b0e6a47 --- /dev/null +++ b/libsrc/gnujpdf/src/gnu/jpdf/PDFObject.java @@ -0,0 +1,177 @@ +/* + * + * $Id: PDFObject.java,v 1.3 2007/09/22 12:48:16 gil1 Exp $ + * + * $Date: 2007/09/22 12:48:16 $ + * + * 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 + */ +package gnu.jpdf; + +import java.io.*; +import java.util.*; + +/** + * This is the base class for all Objects that form the PDF document. + * + * @author Peter T Mount, http://www.retep.org.uk/pdf/ + * @author Eric Z. Beard, ericzbeard@hotmail.com + * @version $Revision: 1.3 $, $Date: 2007/09/22 12:48:16 $ + */ +public abstract class PDFObject implements Serializable +{ + + /* + * NOTE: The original class is the work of Peter T. Mount, who released it + * in the uk.org.retep.pdf package. It was modified by Eric Z. Beard as + * follows: + * The package name was changed to gnu.pdf. + * The formatting was changed a little bit. + * It is still licensed under the LGPL. + */ + + /** + * This is the object's PDF Type + */ + private String type; + + /** + * This is the unique serial number for this object. + */ + protected int objser; + + /** + * This allows any PDF object to refer to the document being constructed. + */ + protected PDFDocument pdfDocument; + + + /** + * This is usually called by extensors to this class, and sets the + * PDF Object Type + * @param type the PDF Object Type + */ + public PDFObject(String type) + { + this.type = type; + } + + /** + * Returns the PDF Type of this object + * @return The PDF Type of this object + */ + public String getType() + { + return type; + } + + /** + * Returns the unique serial number of this object. + * @return Unique serial number of this object. + */ + public final int getSerialID() + { + return objser; + } + + /** + * Returns the PDF document this object belongs to. + * @return PDF containing this object + */ + public final PDFDocument getPDFDocument() + { + return pdfDocument; + } + + /** + *

Writes the object to the output stream. + * This method must be overidden.

+ * + *

Note: It should not write any other objects, even if they are + * it's Kids, as they will be written by the calling routine.

+ * + * @param os OutputStream to send the object to + * @exception IOException on error + */ + public abstract void write(OutputStream os) throws IOException; + + /** + * The write method should call this before writing anything to the + * OutputStream. This will send the standard header for each object. + * + *

Note: There are a few rare cases where this method is not called. + * + * @param os OutputStream to write to + * @exception IOException on error + */ + public final void writeStart(OutputStream os) throws IOException + { + os.write(Integer.toString(objser).getBytes()); + os.write(" 0 obj\n<<\n".getBytes()); + if(type!=null) { + os.write("/Type ".getBytes()); + os.write(type.getBytes()); + os.write("\n".getBytes()); + } + } + + + + + /** + * The write method should call this after writing anything to the + * OutputStream. This will send the standard footer for each object. + * + *

Note: There are a few rare cases where this method is not called. + * + * @param os OutputStream to write to + * @exception IOException on error + */ + public final void writeEnd(OutputStream os) throws IOException + { + os.write(">>\nendobj\n".getBytes()); + } + + /** + * Returns the unique serial number in PDF format + * @return the serial number in PDF format + */ + public String toString() + { + return ""+objser+" 0 R"; + } + + /** + * This utility method returns a String containing an array definition + * based on a Vector containing PDFObjects + * @param v Vector containing PDFObjects + * @return String containing a PDF array + */ + public static String toArray(Vector v) + { + if(v.size()==0) + return ""; + + StringBuffer b = new StringBuffer(); + String bs = "["; + for(PDFObject x : v) { + b.append(bs); + b.append(x.toString()); + bs = " "; + } + b.append("]"); + return b.toString(); + } +} diff --git a/libsrc/gnujpdf/src/gnu/jpdf/PDFOutline.java b/libsrc/gnujpdf/src/gnu/jpdf/PDFOutline.java new file mode 100644 index 000000000..e68f558fc --- /dev/null +++ b/libsrc/gnujpdf/src/gnu/jpdf/PDFOutline.java @@ -0,0 +1,340 @@ +/* + * $Id: PDFOutline.java,v 1.3 2007/09/22 12:58:40 gil1 Exp $ + * + * $Date: 2007/09/22 12:58:40 $ + * + * 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 + */ +package gnu.jpdf; + +import java.io.*; +import java.util.*; + +/** + * This class manages the documents outlines (also known as bookmarks). + * + * @author Peter T Mount http://www.retep.org.uk/pdf/ + * @author Eric Z. Beard, ericzbeard@hotmail.com + * @version $Revision: 1.3 $, $Date: 2007/09/22 12:58:40 $ + */ +public class PDFOutline extends PDFObject implements Serializable +{ + + /* + * NOTE: The original class is the work of Peter T. Mount, who released it + * in the uk.org.retep.pdf package. It was modified by Eric Z. Beard as + * follows: + * The package name was changed to gnu.pdf. + * The formatting was changed a little bit. + * It is still licensed under the LGPL. + */ + + /** + * This holds any outlines below us + */ + private Vector outlines; + + /** + * For subentries, this points to it's parent outline + */ + protected PDFOutline parent; + + /** + * This is this outlines Title + */ + private String title; + + /** + * The destination page + */ + PDFPage dest; + + /** + * The region on the destination page + */ + int l,b,r,t; + + /** + * How the destination is handled + */ + boolean destMode; + + /** + * When jumping to the destination, display the whole page + */ + static final boolean FITPAGE = false; + + /** + * When jumping to the destination, display the specified region + */ + static final boolean FITRECT = true; + + /** + * Constructs a PDF Outline object. This method is used internally only. + */ + protected PDFOutline() + { + super("/Outlines"); + outlines = new Vector(); + title = null; + dest = null; + destMode = FITPAGE; + } + + /** + * Constructs a PDF Outline object. When selected, the whole page is + * displayed. + * + * @param title Title of the outline + * @param dest The destination page + */ + public PDFOutline(String title,PDFPage dest) + { + this(); + this.title = title; + this.dest = dest; + } + + /** + * Constructs a PDF Outline object. When selected, the specified region + * is displayed. + * + * @param title Title of the outline + * @param dest The destination page + * @param l left coordinate + * @param b bottom coordinate + * @param r right coordinate + * @param t top coordinate + */ + public PDFOutline(String title,PDFPage dest,int l,int b,int r,int t) + { + this(title,dest); + this.destMode = FITRECT; + this.l = l; + this.b = b; + this.r = r; + this.t = t; + } + + /** + * This method creates an outline, and attaches it to this one. + * When the outline is selected, the entire page is displayed. + * + *

This allows you to have an outline for say a Chapter, + * then under the chapter, one for each section. You are not really + * limited on how deep you go, but it's best not to go below say 6 levels, + * for the reader's sake. + * + * @param title Title of the outline + * @param dest The destination page + * @return PDFOutline object created, for creating sub-outlines + */ + public PDFOutline add(String title,PDFPage dest) { + PDFOutline outline = new PDFOutline(title,dest); + pdfDocument.add(outline); // add to the pdf first! + add(outline); + return outline; + } + + /** + * This method creates an outline, and attaches it to this one. + * When the outline is selected, the supplied region is displayed. + * + *

Note: the coordiates are in Java space. They are converted to User + * space. + * + *

This allows you to have an outline for say a Chapter, + * then under the chapter, one for each section. You are not really + * limited on how deep you go, but it's best not to go below say 6 levels, + * for the reader's sake. + * + * @param title Title of the outline + * @param dest The destination page + * @param x coordinate of region in Java space + * @param y coordinate of region in Java space + * @param w width of region in Java space + * @param h height of region in Java space + * @return PDFOutline object created, for creating sub-outlines + */ + public PDFOutline add(String title,PDFPage dest, + int x,int y,int w,int h) { + int xy1[] = dest.cxy(x,y+h); + int xy2[] = dest.cxy(x+w,y); + PDFOutline outline = new PDFOutline(title,dest, + xy1[0],xy1[1], + xy2[0],xy2[1]); + pdfDocument.add(outline); // add to the pdf first! + add(outline); + return outline; + } + + /** + * This adds an already existing outline to this one. + * + *

Note: the outline must have been added to the PDF document before + * calling this method. Normally the other add methods are used. + * + * @param outline PDFOutline to add + */ + public void add(PDFOutline outline) + { + outlines.addElement(outline); + + // Tell the outline of ourselves + outline.parent = this; + } + + /** + * @param os OutputStream to send the object to + * @exception IOException on error + */ + public void write(OutputStream os) throws IOException + { + // Write the object header + writeStart(os); + + // now the objects body + + // These are for kids only + if(parent!=null) { + os.write("/Title ".getBytes()); + os.write(PDFStringHelper.makePDFString(title).getBytes()); + os.write("\n/Dest [".getBytes()); + os.write(dest.toString().getBytes()); + + if(destMode==FITPAGE) { + //os.write(" null null null]\n/Parent ".getBytes()); + os.write(" /Fit]\n/Parent ".getBytes()); + } else { + os.write(" /FitR ".getBytes()); + os.write(Integer.toString(l).getBytes()); + os.write(" ".getBytes()); + os.write(Integer.toString(b).getBytes()); + os.write(" ".getBytes()); + os.write(Integer.toString(r).getBytes()); + os.write(" ".getBytes()); + os.write(Integer.toString(t).getBytes()); + os.write("]\n/Parent ".getBytes()); + } + os.write(parent.toString().getBytes()); + os.write("\n".getBytes()); + } + + // the number of outlines in this document + if(parent==null) { + // were the top level node, so all are open by default + os.write("/Count ".getBytes()); + os.write(Integer.toString(outlines.size()).getBytes()); + os.write("\n".getBytes()); + } else { + // were a decendent, so by default we are closed. Find out how many + // entries are below us + int c = descendants(); + if(c>0) { + os.write("/Count ".getBytes()); + os.write(Integer.toString(-c).getBytes()); + os.write("\n".getBytes()); + } + } + + // These only valid if we have children + if(outlines.size()>0) { + // the number of the first outline in list + os.write("/First ".getBytes()); + os.write(outlines.elementAt(0).toString().getBytes()); + os.write("\n".getBytes()); + + // the number of the last outline in list + os.write("/Last ".getBytes()); + os.write(outlines.elementAt(outlines.size()-1).toString().getBytes()); + os.write("\n".getBytes()); + } + + if(parent!=null) { + int index = parent.getIndex(this); + if(index>0) { + // Now if were not the first, then we have a /Prev node + os.write("/Prev ".getBytes()); + os.write(parent.getNode(index-1).toString().getBytes()); + os.write("\n".getBytes()); + } + if(index elements() + { + return outlines.elements(); + } + + /** + * Returns the total number of descendants below this one. + * @return the number of descendants below this one + */ + protected int descendants() + { + int c = outlines.size(); // initially the number of kids + + // now call each one for their descendants + for(PDFOutline o : outlines) { + c += o.descendants(); + } + + return c; + } +} // end class PDFOutline diff --git a/libsrc/gnujpdf/src/gnu/jpdf/PDFOutput.java b/libsrc/gnujpdf/src/gnu/jpdf/PDFOutput.java new file mode 100644 index 000000000..e17f19444 --- /dev/null +++ b/libsrc/gnujpdf/src/gnu/jpdf/PDFOutput.java @@ -0,0 +1,218 @@ +/* + * $Id: PDFOutput.java,v 1.3 2007/09/22 12:48:16 gil1 Exp $ + * + * $Date: 2007/09/22 12:48:16 $ + * + * + * 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 + * + */ +package gnu.jpdf; + +import java.io.*; +import java.util.*; + +/** + * This class is used to write a PDF document. It acts as a wrapper + * to a real OutputStream, but is necessary for certain internal PDF + * structures to be built correctly. + * + * @author Peter T. Mount + * @author Eric Z. Beard, ericzbeard@hotmail.com + * @version $Revision: 1.3 $, $Date: 2007/09/22 12:48:16 $ + */ +public class PDFOutput +{ + /** + * This is the actual OutputStream used to write to. + */ + protected OutputStream os; + + /** + * This is the OutputStream used to write each object to. + * + *

We use a separate stream, because we need to keep track of how + * many bytes have been written for each object for the xref table to + * work correctly. + */ + protected ByteArrayOutputStream baos; + + /** + * This is the current position within the stream + */ + protected int offset; + + /** + * This vector contains offsets of each object + */ + protected Vector offsets; + + /** + * This is used to track the /Root object (catalog) + */ + protected PDFObject rootID; + + /** + * This is used to track the /Info object (info) + */ + protected PDFObject infoID; + + /** + * This creates a PDF OutputStream + * + * @param os The output stream to write the PDF file to. + * @throws IOException if there is an I/O error. + */ + public PDFOutput(OutputStream os) throws IOException + { + this.os = os; + offset = 0; + offsets = new Vector(); + baos = new ByteArrayOutputStream(); + + // Now write the PDF header + // + // Note: As the encoding is fixed here, we use getBytes(). + // + baos.write("%PDF-1.2\n".getBytes()); + + // This second comment is advised in the PDF Reference manual + // page 61 + baos.write("%\342\343\317\323\n".getBytes()); + + offset = baos.size(); + baos.writeTo(os); + } + + /** + * This method writes a PDFObject to the stream. + * + * @param ob PDFObject Obeject to write + * @exception IOException on error + */ + protected void write(PDFObject ob) throws IOException + { + // Check the object to see if it's one that is needed in the trailer + // object + if(ob instanceof PDFCatalog) rootID=ob; + if(ob instanceof PDFInfo) infoID=ob; + + offsets.addElement(new PDFXref(ob.getSerialID(),offset)); + baos.reset(); + ob.write(baos); + offset+=baos.size(); + baos.writeTo(os); + } + + /** + * This closes the Stream, writing the xref table + */ + protected void close() throws IOException + { + // Make sure everything is written + os.flush(); + + // we use baos to speed things up a little. + // Also, offset is preserved, and marks the begining of this block. + // This is required by PDF at the end of the PDF file. + baos.reset(); + baos.write("xref\n".getBytes()); + + // Now a single subsection for object 0 + //baos.write("0 1\n0000000000 65535 f \n".getBytes()); + + // Now scan through the offsets list. The should be in sequence, + // but just in case: + int firstid = 0; // First id in block + int lastid = -1; // The last id used + Vector block = new Vector(); // xrefs in this block + + // We need block 0 to exist + block.addElement(new PDFXref(0,0,65535)); + + for(PDFXref x : offsets) { + + if(firstid==-1) firstid=x.id; + + // check to see if block is in range (-1 means empty) + if(lastid>-1 && x.id != (lastid+1)) { + // no, so write this block, and reset + writeblock(firstid,block); + block.removeAllElements(); + firstid=-1; + } + + // now add to block + block.addElement(x); + lastid = x.id; + } + + // now write the last block + if(firstid>-1) + writeblock(firstid,block); + + // now the trailer object + baos.write("trailer\n<<\n".getBytes()); + + // the number of entries (REQUIRED) + baos.write("/Size ".getBytes()); + baos.write(Integer.toString(offsets.size()+1).getBytes()); + baos.write("\n".getBytes()); + + // the /Root catalog indirect reference (REQUIRED) + if(rootID != null) { + baos.write("/Root ".getBytes()); + baos.write(rootID.toString().getBytes()); + baos.write("\n".getBytes()); + } else + throw new IOException("Root object is not present in document"); + + // the /Info reference (OPTIONAL) + if(infoID != null) { + baos.write("/Info ".getBytes()); + baos.write(infoID.toString().getBytes()); + baos.write("\n".getBytes()); + } + + // end the trailer object + baos.write(">>\nstartxref\n".getBytes()); + baos.write(Integer.toString(offset).getBytes()); + baos.write("\n%%EOF\n".getBytes()); + + // now flush the stream + baos.writeTo(os); + os.flush(); + } + + /** + * Writes a block of references to the PDF file + * @param firstid ID of the first reference in this block + * @param block Vector containing the references in this block + * @exception IOException on write error + */ + protected void writeblock(int firstid,Vector block) throws IOException + { + baos.write(Integer.toString(firstid).getBytes()); + baos.write(" ".getBytes()); + baos.write(Integer.toString(block.size()).getBytes()); + baos.write("\n".getBytes()); + //baos.write("\n0000000000 65535 f\n".getBytes()); + + for(PDFXref x : block) { + baos.write(x.toString().getBytes()); + baos.write("\n".getBytes()); + } + } +} // end class PDFOutput diff --git a/libsrc/gnujpdf/src/gnu/jpdf/PDFPage.java b/libsrc/gnujpdf/src/gnu/jpdf/PDFPage.java new file mode 100644 index 000000000..fa8fad764 --- /dev/null +++ b/libsrc/gnujpdf/src/gnu/jpdf/PDFPage.java @@ -0,0 +1,648 @@ +/* + * $Id: PDFPage.java,v 1.5 2007/09/22 12:58:40 gil1 Exp $ + * + * $Date: 2007/09/22 12:58:40 $ + * + * 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 + */ +package gnu.jpdf; + +import java.awt.Dimension; +import java.awt.Rectangle; +import java.awt.print.PageFormat; +import java.io.IOException; +import java.io.OutputStream; +import java.io.Serializable; +import java.util.Vector; + +/** + *

This class defines a single page within a document. It is linked to a + * single PDFGraphics object

+ * + * @author Peter T Mount + * @author Eric Z. Beard, ericzbeard@hotmail.com + * @author Gilbert DeLeeuw, gil1@users.sourceforge.net + * @version $Revision: 1.5 $, $Date: 2007/09/22 12:58:40 $ + * + * + */ +public class PDFPage extends PDFObject implements Serializable +{ + /* + * NOTE: The original class is the work of Peter T. Mount, who released it + * in the uk.org.retep.pdf package. It was modified by Eric Z. Beard as + * follows: + * The package name was changed to gnu.pdf. + * The formatting was changed a little bit. + * It is still licensed under the LGPL. + */ + + /** + * Default page format (Letter size with 1 inch margins and + * Portrait orientation) + */ + private static final PageFormat DEF_FORMAT = new PageFormat(); + + /** + * This is this page format, ie the size of the page, margins, and rotation + */ + protected PageFormat pageFormat; + + /** + * This is the pages object id that this page belongs to. + * It is set by the pages object when it is added to it. + */ + protected PDFObject pdfPageList; + + /** + * This holds the contents of the page. + */ + protected Vector contents; + + /** + * Object ID that contains a thumbnail sketch of the page. + * -1 indicates no thumbnail. + */ + protected PDFObject thumbnail; + + /** + * This holds any Annotations contained within this page. + */ + protected Vector annotations; + + /** + * This holds any resources for this page + */ + protected Vector resources; + + // JM + protected Vector imageResources; + + /** + * The fonts associated with this page + */ + protected Vector fonts; + + /** + * The xobjects or other images in the pdf + */ +// protected Vector xobjects; + + /** + * These handle the procset for this page. + * Refer to page 140 of the PDF Reference manual + * NB: Text is handled when the fonts Vector is null, and a font is created + * refer to getFont() to see where it's defined + */ + protected boolean hasImageB,hasImageC,hasImageI; + protected procset procset; + + /** + * This constructs a Page object, which will hold any contents for this + * page. + * + *

Once created, it is added to the document via the PDF.add() method. + * (For Advanced use, via the PDFPages.add() method). + * + *

This defaults to a4 media. + */ + public PDFPage() + { + super("/Page"); + pageFormat = DEF_FORMAT; + contents = new Vector(); + thumbnail = null; + annotations = new Vector(); + resources = new Vector(); + // JM + imageResources = new Vector(); + fonts = new Vector(); + procset = null; + } + + /** + * Constructs a page using A4 media, but using the supplied orientation. + * @param orientation Orientation: 0, 90 or 270 + * @see PageFormat#PORTRAIT + * @see PageFormat#LANDSCAPE + * @see PageFormat#REVERSE_LANDSCAPE + */ + public PDFPage(int orientation) + { + this(); + setOrientation(orientation); + } + + /** + * Constructs a page using the supplied media size and orientation. + * @param pageFormat PageFormat describing the page size + */ + public PDFPage(PageFormat pageFormat) + { + this(); + this.pageFormat = pageFormat; + } + + /** + * Adds to procset. + * @param proc the String to be added. + */ + public void addToProcset(String proc) { + if (procset == null) { + addProcset(); + } + procset.add(proc); + } + + /** + * This returns a PDFGraphics object, which can then be used to render + * on to this page. If a previous PDFGraphics object was used, this object + * is appended to the page, and will be drawn over the top of any previous + * objects. + * + * @return a new PDFGraphics object to be used to draw this page. + */ + public PDFGraphics getGraphics() { + try { + PDFGraphics g = new PDFGraphics(); + g.init(this); + return g; + } catch(Exception ex) { + ex.printStackTrace(); + } + + return null; + } + + /** + * Returns a PDFFont, creating it if not yet used. + * @param type Font type, usually /Type1 + * @param font Font name + * @param style java.awt.Font style, ie Font.NORMAL + * @return a PDFFont object. + */ + public PDFFont getFont(String type,String font,int style) { + // Search the fonts on this page, and return one that matches this + // font. + // This keeps the number of font definitions down to one per font/style + for(PDFFont ft : fonts) { + if(ft.equals(type,font,style)) + return ft; + } + + // Ok, the font isn't in the page, so create one. + + // We need a procset if we are using fonts, so create it (if not + // already created, and add to our resources + if(fonts.size()==0) { + addProcset(); + procset.add("/Text"); + } + + // finally create and return the font + PDFFont f = pdfDocument.getFont(type,font,style); + fonts.addElement(f); + return f; + } + + /** + * Returns the page's PageFormat. + * @return PageFormat describing the page size in device units (72dpi) + */ + public PageFormat getPageFormat() { + return pageFormat; + } + + /** + * Gets the dimensions of the page. + * @return a Dimension object containing the width and height of the page. + */ + public Dimension getDimension() { + return new Dimension((int) pageFormat.getWidth(), (int) pageFormat + .getHeight()); + } + + /** + * Gets the imageable area of the page. + * @return a Rectangle containing the bounds of the imageable area. + */ + public Rectangle getImageableArea() { + return new Rectangle((int) pageFormat.getImageableX(), (int) pageFormat + .getImageableY(), + (int) (pageFormat.getImageableX() + pageFormat + .getImageableWidth()), (int) (pageFormat + .getImageableY() + pageFormat.getImageableHeight())); + } + + /** + * Sets the page's orientation. + * + *

Normally, this should be done when the page is created, to avoid + * problems. + * + * @param orientation a PageFormat orientation constant: + * PageFormat.PORTRAIT, PageFormat.LANDSACPE or PageFromat.REVERSE_LANDSACPE + */ + public void setOrientation(int orientation) { + pageFormat.setOrientation(orientation); + } + + /** + * Returns the pages orientation: + * PageFormat.PORTRAIT, PageFormat.LANDSACPE or PageFromat.REVERSE_LANDSACPE + * + * @see java.awt.print.PageFormat + * @return current orientation of the page + */ + public int getOrientation() { + return pageFormat.getOrientation(); + } + + /** + * This adds an object that describes some content to this page. + * + *

Note: Objects that describe contents must be added using this + * method _AFTER_ the PDF.add() method has been called. + * + * @param ob PDFObject describing some contents + */ + public void add(PDFObject ob) { + contents.addElement(ob); + } + + /** + * This adds an Annotation to the page. + * + *

As with other objects, the annotation must be added to the pdf + * document using PDF.add() before adding to the page. + * + * @param ob Annotation to add. + */ + public void addAnnotation(PDFObject ob) { + annotations.addElement(ob); + } + + /** + * This method adds a text note to the document. + * @param note Text of the note + * @param x Coordinate of note + * @param y Coordinate of note + * @param w Width of the note + * @param h Height of the note + * @return Returns the annotation, so other settings can be changed. + */ + public PDFAnnot addNote(String note,int x,int y,int w,int h) { + int xy1[] = cxy(x,y+h); + int xy2[] = cxy(x+w,y); + PDFAnnot ob = new PDFAnnot(xy1[0],xy1[1], + xy2[0],xy2[1], + note); + pdfDocument.add(ob); + annotations.addElement(ob); + return ob; + } + + /** + * Adds a hyperlink to the document. + * @param x Coordinate of active area + * @param y Coordinate of active area + * @param w Width of the active area + * @param h Height of the active area + * @param dest Page that will be displayed when the link is activated. When + * displayed, the zoom factor will be changed to fit the display. + * @return Returns the annotation, so other settings can be changed. + */ + public PDFAnnot addLink(int x,int y,int w,int h,PDFObject dest) { + int xy1[] = cxy(x,y+h); + int xy2[] = cxy(x+w,y); + PDFAnnot ob = new PDFAnnot(xy1[0],xy1[1], + xy2[0],xy2[1], + dest + ); + pdfDocument.add(ob); + annotations.addElement(ob); + return ob; + } + + /** + * Adds a hyperlink to the document. + * @param x Coordinate of active area + * @param y Coordinate of active area + * @param w Width of the active area + * @param h Height of the active area + * @param dest Page that will be displayed when the link is activated + * @param vx Coordinate of view area + * @param vy Coordinate of view area + * @param vw Width of the view area + * @param vh Height of the view area + * @return Returns the annotation, so other settings can be changed. + */ + public PDFAnnot addLink(int x,int y,int w,int h, + PDFObject dest, + int vx,int vy,int vw,int vh) { + int xy1[] = cxy(x,y+h); + int xy2[] = cxy(x+w,y); + int xy3[] = cxy(vx,vy+vh); + int xy4[] = cxy(vx+vw,vy); + PDFAnnot ob = new PDFAnnot(xy1[0],xy1[1], + xy2[0],xy2[1], + dest, + xy3[0],xy3[1], + xy4[0],xy4[1] + ); + pdfDocument.add(ob); + annotations.addElement(ob); + return ob; + } + + /** Contains the text strings for the xobjects. */ + private Vector xobjects = new Vector(); + + /** + * This adds an XObject resource to the page. + * The string should be of the format + * /Name ObjectNumber RevisionNumber R as in /Image1 13 0 R . + * @param inxobject the XObject resource to be added. + */ + public void addXObject(String inxobject){ + xobjects.addElement(inxobject); + } + + /** + * This adds a resource to the page. + * @param resource String defining the resource + */ + public void addResource(String resource) { + resources.addElement(resource); + } + + // JM + /** + * This adds an image resource to the page. + * @param resource the XObject resource to be added. + */ + public void addImageResource(String resource) { + imageResources.addElement(resource); + } + + /** + * This adds an object that describes a thumbnail for this page. + *

Note: The object must already exist in the PDF, as only the + * object ID is stored. + * @param thumbnail PDFObject containing the thumbnail + */ + public void setThumbnail(PDFObject thumbnail) + { + this.thumbnail = thumbnail; + } + + /** + * This method attaches an outline to the current page being generated. When + * selected, the outline displays the top of the page. + * @param title Outline title to attach + * @return PDFOutline object created, for addSubOutline if required. + */ + public PDFOutline addOutline(String title) { + PDFOutline outline = new PDFOutline(title,this); + pdfDocument.add(outline); + pdfDocument.getOutline().add(outline); + return outline; + } + + /** + * This method attaches an outline to the current page being generated. + * When selected, the outline displays the top of the page. + * + *

Note: If the outline is not in the top level (ie below another + * outline) then it must not be passed to this method. + * + * @param title Outline title to attach + * @param x Left coordinate of region + * @param y Bottom coordinate of region + * @param w Width of region + * @param h Height coordinate of region + * @return PDFOutline object created, for addSubOutline if required. + */ + public PDFOutline addOutline(String title,int x,int y,int w,int h) { + int xy1[] = cxy(x,y+h); + int xy2[] = cxy(x+w,y); + PDFOutline outline = new PDFOutline(title,this, + xy1[0],xy1[1], + xy2[0],xy2[1]); + pdfDocument.add(outline); + pdfDocument.getOutline().add(outline); + return outline; + } + + /** + * @param os OutputStream to send the object to + * @exception IOException on error + */ + public void write(OutputStream os) throws IOException + { + // Write the object header + writeStart(os); + + // now the objects body + + // the /Parent pages object + os.write("/Parent ".getBytes()); + os.write(pdfPageList.toString().getBytes()); + os.write("\n".getBytes()); + + // the /MediaBox for the page size + os.write("/MediaBox [".getBytes()); + os.write(Integer.toString(0).getBytes()); + os.write(" ".getBytes()); + os.write(Integer.toString(0).getBytes()); + os.write(" ".getBytes()); + os.write(Integer.toString((int)pageFormat.getWidth()).getBytes()); + os.write(" ".getBytes()); + os.write(Integer.toString((int)pageFormat.getHeight()).getBytes()); + os.write("]\n".getBytes()); + + // Rotation (if not zero) +// if(rotate!=0) { +// os.write("/Rotate ".getBytes()); +// os.write(Integer.toString(rotate).getBytes()); +// os.write("\n".getBytes()); +// } + + // Now the resources + os.write("/Resources << ".getBytes()); + // fonts + if(fonts.size()>0) { + //os.write("/Font << ".getBytes()); + os.write("\n/Font << ".getBytes()); + for(PDFFont font : fonts) { + os.write(font.getName().getBytes()); + os.write(" ".getBytes()); + os.write(font.toString().getBytes()); + os.write(" ".getBytes()); + } + os.write(">> ".getBytes()); + } + // Now the XObjects + if (xobjects.size() > 0){ + os.write("\n/XObject << ".getBytes()); + for(String str : xobjects) { + os.write(str.getBytes()); + os.write(" ".getBytes()); + } + os.write(">> ".getBytes()); + } + // Any other resources + for(String str : resources) { + os.write(str.getBytes()); + os.write(" ".getBytes()); + } + // JM + if(imageResources.size() > 0) { + os.write("/XObject << ".getBytes()); + for(String str : imageResources) { + os.write(str.getBytes()); + os.write(" ".getBytes()); + } + os.write(" >> ".getBytes()); + } + os.write(">>\n".getBytes()); + + // The thumbnail + if(thumbnail!=null) { + os.write("/Thumb ".getBytes()); + os.write(thumbnail.toString().getBytes()); + os.write("\n".getBytes()); + } + + // the /Contents pages object + if(contents.size()>0) { + if(contents.size()==1) { + PDFObject ob = (PDFObject)contents.elementAt(0); + os.write("/Contents ".getBytes()); + os.write(ob.toString().getBytes()); + os.write("\n".getBytes()); + } else { + os.write("/Contents [".getBytes()); + os.write(PDFObject.toArray(contents).getBytes()); + os.write("\n".getBytes()); + } + } + + // The /Annots object + if(annotations.size()>0) { + os.write("/Annots ".getBytes()); + os.write(PDFObject.toArray(annotations).getBytes()); + os.write("\n".getBytes()); + } + + // finish off with its footer + writeEnd(os); + } + + /** + * This creates a procset and sets up the page to reference it + */ + private void addProcset() { + if(procset==null) { + pdfDocument.add(procset = new procset()); + resources.addElement("/ProcSet "+procset); + } + } + + /** + * This defines a procset + */ + public class procset extends PDFObject { + private Vector set; + + /** + * Creates a new procset object. + */ + public procset() { + super(null); + set = new Vector(); + + // Our default procset (use addElement not add, as we dont want a + // leading space) + set.addElement("/PDF"); + } + + /** + * @param proc Entry to add to the procset + */ + public void add(String proc) { + set.addElement(" "+proc); + } + + /** + * @param os OutputStream to send the object to + * @exception IOException on error + */ + public void write(OutputStream os) throws IOException { + // Write the object header + //writeStart(os); + + os.write(Integer.toString(objser).getBytes()); + os.write(" 0 obj\n".getBytes()); + + // now the objects body + os.write("[".getBytes()); + for(String str : set) + os.write(str.getBytes()); + os.write("]\n".getBytes()); + + // finish off with its footer + //writeEnd(os); + os.write("endobj\n".getBytes()); + } + } + + /** + * This utility method converts the y coordinate from Java to User space + * within the page. + * @param x Coordinate in Java space + * @param y Coordinate in Java space + * @return y Coordinate in User space + */ + public int cy(int x,int y) { + return cxy(x,y)[1]; + } + + /** + * This utility method converts the y coordinate from Java to User space + * within the page. + * @param x Coordinate in Java space + * @param y Coordinate in Java space + * @return x Coordinate in User space + */ + public int cx(int x, int y) { + return cxy(x,y)[0]; + } + + /** + * This utility method converts the Java coordinates to User space + * within the page. + * @param x Coordinate in Java space + * @param y Coordinate in Java space + * @return array containing the x & y Coordinate in User space + */ + public int[] cxy(int x,int y) { + int r[] = new int[2]; + r[0] = x; + r[1] = (int) pageFormat.getHeight() - y; + return r; + } + +} diff --git a/libsrc/gnujpdf/src/gnu/jpdf/PDFPageList.java b/libsrc/gnujpdf/src/gnu/jpdf/PDFPageList.java new file mode 100644 index 000000000..961c5906f --- /dev/null +++ b/libsrc/gnujpdf/src/gnu/jpdf/PDFPageList.java @@ -0,0 +1,95 @@ +/* + * $Id: PDFPageList.java,v 1.2 2007/08/26 18:56:35 gil1 Exp $ + * + * $Date: 2007/08/26 18:56:35 $ + * + * + * 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 + * + */ +package gnu.jpdf; + +import java.io.*; +import java.util.*; + +/** + * This object contains the document's pages. + * + * @author Peter T. Mount + * @author Eric Z. Beard, ericzbeard@hotmail.com + * @version $Revision: 1.2 $, $Date: 2007/08/26 18:56:35 $ + */ +public class PDFPageList extends PDFObject +{ + + /** + * This holds the pages + */ + private Vector pages; + + /** + * This constructs a PDF Pages object. + */ + public PDFPageList() { + super("/Pages"); + pages = new Vector(); + } + + /** + * This adds a page to the document. + * + * @param page PDFPage to add + */ + public void add(PDFPage page) { + pages.addElement(page); + + // Tell the page of ourselves + page.pdfPageList = this; + } + + /** + * This returns a specific page. Used by the PDF class. + * @param page page number to return + * @return PDFPage at that position + */ + public PDFPage getPage(int page) { + return (PDFPage)(pages.elementAt(page)); + } + + /** + * @param os OutputStream to send the object to + * @exception IOException on error + */ + public void write(OutputStream os) throws IOException { + // Write the object header + writeStart(os); + + // now the objects body + + // the Kids array + os.write("/Kids ".getBytes()); + os.write(PDFObject.toArray(pages).getBytes()); + os.write("\n".getBytes()); + + // the number of Kids in this document + os.write("/Count ".getBytes()); + os.write(Integer.toString(pages.size()).getBytes()); + os.write("\n".getBytes()); + + // finish off with its footer + writeEnd(os); + } + +} // end class PDFPageList diff --git a/libsrc/gnujpdf/src/gnu/jpdf/PDFPrinterJob.java b/libsrc/gnujpdf/src/gnu/jpdf/PDFPrinterJob.java new file mode 100644 index 000000000..72625a816 --- /dev/null +++ b/libsrc/gnujpdf/src/gnu/jpdf/PDFPrinterJob.java @@ -0,0 +1,274 @@ +package gnu.jpdf; + +import java.awt.HeadlessException; +import java.awt.print.Book; +import java.awt.print.PageFormat; +import java.awt.print.Pageable; +import java.awt.print.Printable; +import java.awt.print.PrinterException; +import java.awt.print.PrinterJob; +import java.io.File; +import java.io.FileOutputStream; +import java.util.Locale; + +import javax.print.attribute.HashPrintRequestAttributeSet; +import javax.print.attribute.IntegerSyntax; +import javax.print.attribute.PrintRequestAttributeSet; +import javax.print.attribute.TextSyntax; +import javax.print.attribute.standard.Copies; +import javax.print.attribute.standard.JobName; +import javax.print.attribute.standard.RequestingUserName; +import javax.swing.JFileChooser; + +/** + *

+ * This class extends awt's PrinterJob, to provide a simple method of writing + * PDF documents. + *

+ * + *

+ * You can use this with any code that uses Java's printing mechanism. It does + * include a few extra methods to provide access to some of PDF's features like + * annotations, or outlines. + *

+ * + * @author Gilbert DeLeeuw, gil1@users.sourceforge.net + */ +public class PDFPrinterJob extends PrinterJob { + + /** + * The file chooser + */ + private static JFileChooser fileChooser; + + /** + * Printing options; + */ + private PrintRequestAttributeSet attributes; + + /** + * PDF document properties + */ + private PDFInfo info; + + /** + * A pageable, or null + */ + private Pageable pageable = null; + + /** + * Page format. + */ + private PageFormat pageFormat; + + /** + * The Printable object to print. + */ + private Printable printable; + + /** + * The actual print job. + */ + private PDFJob printJob; + + /** + * Initializes a new instance of PDFPrinterJob. + */ + public PDFPrinterJob() { + attributes = new HashPrintRequestAttributeSet(); + info = new PDFInfo(); + pageFormat = new PageFormat(); // default page format. + setJobName("Java Printing"); + } + + @Override + public void cancel() { + // Cancel is not an option + } + + @Override + public PageFormat defaultPage(PageFormat page) { + return validatePage(page); + } + + @Override + public int getCopies() { + return ((IntegerSyntax) attributes.get(Copies.class)).getValue(); + } + + @Override + public String getJobName() { + return ((TextSyntax) attributes.get(JobName.class)).getValue(); + } + + public static PrinterJob getPrinterJob() { + return new PDFPrinterJob(); + } + + @Override + public String getUserName() { + return ((TextSyntax) attributes.get(RequestingUserName.class)) + .getValue(); + } + + @Override + public boolean isCancelled() { + return false; + } + + @Override + public PageFormat pageDialog(PageFormat page) throws HeadlessException { + // No page dialog is supported. + return (PageFormat) page.clone(); + } + + /** + * Prints a set of pages. + * + * @param pathname + * the full path for the output PDF file. + * @exception PrinterException + * an error in the print system caused the job to be aborted. + * @see Book + * @see Pageable + * @see Printable + */ + public void print(String pathname) throws PrinterException { + int pageCount; + File file = null; + FileOutputStream fileOutputStream = null; + + try { + file = new File(pathname); + fileOutputStream = new FileOutputStream(file); + } catch (Exception e) { + System.err.println("Error!! - Invalid output file path: " + + pathname); + } + + PDFGraphics pdfGraphics = null; + printJob = new PDFJob(fileOutputStream); + + if (info != null) { + printJob.getPDFDocument().setPDFInfo(info); + } + + pageCount = pageable.getNumberOfPages(); + for (int pageIndex = 0; pageIndex < pageCount; pageIndex++) { + pageFormat = pageable.getPageFormat(pageIndex); + + pdfGraphics = (PDFGraphics) printJob.getGraphics(pageFormat); + printable = pageable.getPrintable(pageIndex); + printable.print(pdfGraphics, pageFormat, pageIndex); + pdfGraphics.dispose(); + } + + printJob.end(); + + } + + @Override + public void print() throws PrinterException { + File file; + File path; + String jobName = getJobName(); + + if (fileChooser == null) { + fileChooser = new JFileChooser(); + fileChooser.setMultiSelectionEnabled(false); + fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + } + + // Make sure job name is not blank + if (jobName.equals("")) { + jobName = "Java Printing"; + } + // Eliminate invalid characters from job name. + jobName = jobName.replaceAll("\\\\", "-"); + jobName = jobName.replaceAll("/", "-"); + + path = fileChooser.getCurrentDirectory(); + file = new File(path, jobName + ".pdf"); + + // Dialog to get file name... + fileChooser.setSelectedFile(file); + + // Print + if (fileChooser.showSaveDialog(null) == JFileChooser.APPROVE_OPTION) { + file = fileChooser.getSelectedFile(); + print(file.getAbsolutePath()); + } + } + + @Override + public boolean printDialog() throws HeadlessException { + return true; + } + + /** + * Sets the author for this document. + * + * @param author the author's name. + */ + public void setAuthor(String author) { + info.setAuthor(author); + } + + @Override + public void setCopies(int copies) { + // Will be ignored, but add attribute anyway + attributes.add(new Copies(copies)); + } + + /** + * Sets the creator for this document. + * + * @param creator the application name. + */ + public void setCreator(String creator) { + info.setCreator(creator); + } + + @Override + public void setJobName(String jobName) { + attributes.add(new JobName(jobName, Locale.getDefault())); + + if (info.getTitle() == null) { + info.setTitle(jobName); + } + } + + @Override + public void setPageable(Pageable document) throws NullPointerException { + if (document == null) { + throw new NullPointerException("Pageable cannot be null."); + } + this.pageable = document; + } + + @Override + public void setPrintable(Printable painter) { + this.printable = painter; + } + + @Override + public void setPrintable(Printable painter, PageFormat format) { + this.printable = painter; + this.pageFormat = format; + } + + /** + * Sets the title for this document. + * + * @param title the document title. + */ + public void setTitle(String title) { + info.setTitle(title); + } + + @Override + public PageFormat validatePage(PageFormat page) { + return (PageFormat) page.clone(); + } + +} diff --git a/libsrc/gnujpdf/src/gnu/jpdf/PDFStream.java b/libsrc/gnujpdf/src/gnu/jpdf/PDFStream.java new file mode 100644 index 000000000..7702d5574 --- /dev/null +++ b/libsrc/gnujpdf/src/gnu/jpdf/PDFStream.java @@ -0,0 +1,218 @@ +/* + * $Id: PDFStream.java,v 1.2 2007/08/26 18:56:35 gil1 Exp $ + * + * $Date: 2007/08/26 18:56:35 $ + * + * 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 + */ +package gnu.jpdf; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.io.Serializable; +import java.util.zip.DeflaterOutputStream; + +/** + * This class implements a PDF stream object. In PDF, streams contain data + * like the graphic operators that render a page, or the pixels of an image. + * + *

In PDF, a stream can be compressed using several different methods, or + * left uncompressed. Here we support both uncompressed, and FlateDecode as + * it's supported by the java core. + * + * @author Peter T Mount http://www.retep.org.uk/pdf/ + * @author Eric Z. Beard, ericzbeard@hotmail.com + * @version $Revision: 1.2 $, $Date: 2007/08/26 18:56:35 $ + * + */ +public class PDFStream extends PDFObject implements Serializable +{ + + + /* + * NOTE: The original class is the work of Peter T. Mount, who released it + * in the uk.org.retep.pdf package. It was modified by Eric Z. Beard as + * follows: + * The package name was changed to gnu.jpdf. + * The formatting was changed a little bit. + * It is still licensed under the LGPL. + */ + + /** + * This holds the stream's content. + */ + transient ByteArrayOutputStream buf; + + /** + * True if we will compress the stream in the pdf file + */ + boolean deflate; + + /** + * Constructs a plain stream. + *

By default, the stream will be compressed. + */ + public PDFStream() { + this(null); + } + + /** + * Constructs a stream. The supplied type is stored in the stream's header + * and is used by other objects that extend the PDFStream class (like + * PDFImage). + *

By default, the stream will be compressed. + * @param type type for the stream + * @see PDFImage + */ + public PDFStream(String type) { + super(type); + buf = new ByteArrayOutputStream(); + + // default deflate mode + deflate = false; + } + + /** + * @param mode true will FlatDecode the stream + */ + public void setDeflate(boolean mode) { + // TODO: Restore the next line of code to allow deflate to occur. + deflate = mode; + } + + /** + * Returs true if the stream will be compressed. + * @return true if compression is enabled + */ + public boolean getDeflate() { + return deflate; + } + + /** + * Returns the OutputStream that will append to this stream. + * @return The stream for this object + */ + public OutputStream getOutputStream() { + return (OutputStream)buf; + } + + /** + * Creates a PrintWriter that will append to this stream. + * @return a PrintWriter to write to the stream + */ + public PrintWriter getWriter() { + return new PrintWriter(buf,true); + } + + /** + * This is for extenders, and provides access to the stream. + * @return ByteArrayOutputStream containing the contents. + */ + public ByteArrayOutputStream getStream() { + return buf; + } + + /** + * @param os OutputStream to send the object to + * @exception IOException on error + */ + public void write(OutputStream os) throws IOException { + writeStart(os); + writeStream(os); + // Unlike most PDF objects, we dont call writeEnd(os) because we + // contain a stream + } + + /** + * This inserts the Streams length, then the actual stream, finally + * the end of stream/object markers. + * + *

This is intended for anyone extending PDFStream, as objects + * containing streams do no use writeEnd(), and they must be able + * to write the actual stream. + * + * @param os OutputStream to send the object to + * @exception IOException on error + */ + public void writeStream(OutputStream os) throws IOException { + if(deflate) { + ByteArrayOutputStream b = new ByteArrayOutputStream(); + DeflaterOutputStream dos = new DeflaterOutputStream(b); + //,new Deflater(Deflater.BEST_COMPRESSION,true)); + buf.writeTo(dos); + dos.finish(); + dos.close(); + + // FlatDecode is compatible with the java.util.zip.Deflater class + os.write("/Filter /FlateDecode\n".getBytes()); + os.write("/Length ".getBytes()); + os.write(Integer.toString(b.size()+1).getBytes()); + os.write("\n>>\nstream\n".getBytes()); + b.writeTo(os); + os.write("\n".getBytes()); + } else { + // This is a non-deflated stream + os.write("/Length ".getBytes()); + os.write(Integer.toString(buf.size()).getBytes()); + os.write("\n>>\nstream\n".getBytes()); + buf.writeTo(os); + } + + os.write("endstream\nendobj\n".getBytes()); + + // Unlike most PDF objects, we dont call writeEnd(os) because we + // contain a stream + } + + + + // Why is this here? Did it have a specific purpose? + + + /** + * This implements our own special Serialization for this object. + * + *

Here we write the length of the stream's contents, then a byte + * array of the contents. We have to do this, as ByteArrayOutputStream + * is not serializable (hence the transient tag). + * + */ + private void writeObject(java.io.ObjectOutputStream out) throws IOException + { + out.writeInt(buf.size()); + out.write(buf.toByteArray()); + } + + /** + * This implements our own special Serialization for this object + * + *

Here we read the length of the stream's contents, then a byte + * array of the contents. Then we recreate a new ByteArrayOutputStream. + * We have to do this, as ByteArrayOutputStream is not serializable + * (hence the transient tag). + * + */ + private void readObject(java.io.ObjectInputStream in) throws IOException + { + int l = in.readInt(); + byte b[] = new byte[l]; + in.read(b,0,l); + buf=new ByteArrayOutputStream(l); + buf.write(b); + } + +} diff --git a/libsrc/gnujpdf/src/gnu/jpdf/PDFStringHelper.java b/libsrc/gnujpdf/src/gnu/jpdf/PDFStringHelper.java new file mode 100644 index 000000000..4c33df9cd --- /dev/null +++ b/libsrc/gnujpdf/src/gnu/jpdf/PDFStringHelper.java @@ -0,0 +1,79 @@ +/* + * $Id: PDFStringHelper.java,v 1.2 2007/08/26 18:56:35 gil1 Exp $ + * + * $Date: 2007/08/26 18:56:35 $ + * + * Copyright (C) 2001 Eric Z. Beard, ericzbeard@hotmail.com + * + * + * 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 + * + */ + +package gnu.jpdf; + +/** + * String manipulation methods + * + * @author Eric Z. Beard, ericzbeard@hotmail.com + * @version $Revision: 1.2 $, $Date: 2007/08/26 18:56:35 $ + * + */ +public class PDFStringHelper +{ + /** + * This converts a string into PDF. It prefixes ( or ) with \ + * and wraps the string in a ( ) pair. + * @param s String to convert + * @return String that can be placed in a PDF (or Postscript) stream + */ + public static String makePDFString(String s) { + if(s.indexOf("(")>-1) + s = replace(s,"(","\\("); + + if(s.indexOf(")")>-1) + s = replace(s,")","\\)"); + + return "("+s+")"; + } + + /** + * Helper method for toString() + * @param s source string + * @param f string to remove + * @param t string to replace f + * @return string with f replaced by t + */ + private static String replace(String source, + String removeThis, + String replaceWith) { + StringBuffer b = new StringBuffer(); + int p = 0, c=0; + + while(c>-1) { + if((c = source.indexOf(removeThis,p)) > -1) { + b.append(source.substring(p,c)); + b.append(replaceWith); + p=c+1; + } + } + + // include any remaining text + if(pThis class is used to hold the xref information in the PDF + * Trailer block.

+ * + *

Basically, each object has an id, and an offset in the end file.

+ * + *

See the Adobe PDF Manual for more information. This class will + * normally not be used directly by a developer

+ * + * @author Peter T. Mount + * @author Eric Z. Beard, ericzbeard@hotmail.com + * @version $Revision: 1.2 $, $Date: 2007/08/26 18:56:35 $ + * + */ +public class PDFXref +{ + + /* + * NOTE: Originally an inner class in PDF.java (now PDFDocument) written + * by Peter Mount for uk.org.retep.pdf + */ + + /** + * The id of a PDF Object + */ + public int id; + + /** + * The offset within the PDF file + */ + public int offset; + + /** + * The generation of the object, usually 0 + */ + public int generation; + + /** + * Creates a crossreference for a PDF Object + * @param id The object's ID + * @param offset The object's position in the file + */ + public PDFXref(int id,int offset) + { + this(id,offset,0); + } + + /** + * Creates a crossreference for a PDF Object + * + * @param id The object's ID + * @param offset The object's position in the file + * @param generation The object's generation, usually 0 + */ + public PDFXref(int id,int offset,int generation) + { + this.id = id; + this.offset = offset; + this.generation = generation; + } + + /** + * @return The xref in the format of the xref section in the PDF file + */ + public String toString() + { + String of = Integer.toString(offset); + String ge = Integer.toString(generation); + String rs = "0000000000".substring(0, 10-of.length()) + + of + + " " + + "00000".substring(0,5-ge.length())+ge; + if(generation==65535) + return rs+" f "; + return rs+" n "; + } + +} // end class PDFXref + diff --git a/libsrc/gnujpdf/src/gnu/jpdf/StringTooLongException.java b/libsrc/gnujpdf/src/gnu/jpdf/StringTooLongException.java new file mode 100644 index 000000000..7beae66f3 --- /dev/null +++ b/libsrc/gnujpdf/src/gnu/jpdf/StringTooLongException.java @@ -0,0 +1,53 @@ +/* + * $Id: StringTooLongException.java,v 1.1 2007/08/22 01:02:32 gil1 Exp $ + * + * $Date: 2007/08/22 01:02:32 $ + * + * + * 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 + */ +package gnu.jpdf; + + +/** + *

This exception is thrown from {@link gnu.jpdf.BoundingBox} if the + * string won't fit into the box

+ * + * @author Eric Z. Beard, erizbeard@hotmail.com + * @version $Revision: 1.1 $ $Date: 2007/08/22 01:02:32 $ + */ +public class StringTooLongException extends Exception { + private String msg; + + + /** + *

Normally this exception is constructed with a message containing + * information about the sizes of the parent and child box, and maybe the + * string that caused the overflow.

+ * + * @param msg a String, some informative message for the logs + */ + public StringTooLongException(String msg) { + this.msg = msg; + } + + public String toString() { + return msg; + } + + public String getMessage() { + return msg; + } +} // end class StringTooLongException diff --git a/libsrc/gnujpdf/src/gnu/jpdf/Test.java b/libsrc/gnujpdf/src/gnu/jpdf/Test.java new file mode 100644 index 000000000..ce45fd9fe --- /dev/null +++ b/libsrc/gnujpdf/src/gnu/jpdf/Test.java @@ -0,0 +1,40 @@ +package gnu.jpdf; + + +import gnu.jpdf.PDFJob; +import java.awt.Graphics; +import java.awt.image.BufferedImage; +import java.awt.print.PageFormat; +import java.awt.print.Paper; +import java.io.File; +import java.io.FileOutputStream; +import javax.imageio.ImageIO; + + + +/** + * + * @author JPEXS + */ +public class Test { + public static void main(String[] args) throws Exception { + PDFJob job=new PDFJob(new FileOutputStream("test.pdf")); + PageFormat pf=new PageFormat(); + pf.setOrientation(PageFormat.PORTRAIT); + Paper p = new Paper(); + p.setSize(210,297); //A4 + pf.setPaper(p); + + BufferedImage img = ImageIO.read(new File("earth.jpg")); + + int w = 200; + + for(int i=0;i<10;i++){ + Graphics g = job.getGraphics(); + g.drawImage(img, 0, 0,w,w, null); + g.dispose(); + } + + job.end(); + } +} diff --git a/nbproject/project.xml b/nbproject/project.xml index b6652643b..c0b1e84ff 100644 --- a/nbproject/project.xml +++ b/nbproject/project.xml @@ -156,7 +156,7 @@ src - lib/LZMA.jar;lib/jna-3.5.1.jar;lib/jpproxy.jar;lib/jsyntaxpane-0.9.5.jar;lib/trident-6.2.jar;lib/substance-flamingo-6.2.jar;lib/flamingo-6.2.jar;lib/substance-6.2.jar;lib/jl1.0.1.jar;lib/nellymoser.jar;lib/gif.jar;lib/avi.jar;lib/ttf.jar;lib/jpacker.jar;lib/sfntly.jar + lib/LZMA.jar;lib/jna-3.5.1.jar;lib/jpproxy.jar;lib/jsyntaxpane-0.9.5.jar;lib/trident-6.2.jar;lib/substance-flamingo-6.2.jar;lib/flamingo-6.2.jar;lib/substance-6.2.jar;lib/jl1.0.1.jar;lib/nellymoser.jar;lib/gif.jar;lib/avi.jar;lib/ttf.jar;lib/jpacker.jar;lib/sfntly.jar;lib/gnujpdf.jar build javadoc reports diff --git a/src/com/jpexs/decompiler/flash/SWF.java b/src/com/jpexs/decompiler/flash/SWF.java index e0c0b5cd7..50be87d18 100644 --- a/src/com/jpexs/decompiler/flash/SWF.java +++ b/src/com/jpexs/decompiler/flash/SWF.java @@ -149,14 +149,18 @@ import com.jpexs.helpers.Helper; import com.jpexs.helpers.ProgressListener; import com.jpexs.helpers.SerializableImage; import com.jpexs.helpers.utf8.Utf8Helper; +import gnu.jpdf.PDFJob; import java.awt.AlphaComposite; import java.awt.Color; +import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; +import java.awt.print.PageFormat; +import java.awt.print.Paper; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.EOFException; @@ -1290,6 +1294,7 @@ public final class SWF implements TreeItem, Timelined { } } } + public void exportMovies(AbortRetryIgnoreHandler handler, String outdir, MovieExportSettings settings) throws IOException { new MovieExporter().exportMovies(handler, outdir, tags, settings); @@ -1298,7 +1303,7 @@ public final class SWF implements TreeItem, Timelined { public void exportSounds(AbortRetryIgnoreHandler handler, String outdir, SoundExportSettings settings) throws IOException { new SoundExporter().exportSounds(handler, outdir, tags, settings); } - + public void exportFonts(AbortRetryIgnoreHandler handler, String outdir, FontExportSettings settings) throws IOException { new FontExporter().exportFonts(handler, outdir, tags, settings); } @@ -1501,7 +1506,7 @@ public final class SWF implements TreeItem, Timelined { } return ret; } - + if (settings.mode == FramesExportMode.CANVAS) { final Timeline ftim = tim; final Color fbackgroundColor = backgroundColor; @@ -1633,6 +1638,31 @@ public final class SWF implements TreeItem, Timelined { }, handler).run(); } break; + case PDF: + new RetryTask(new RunnableIOEx() { + @Override + public void run() throws IOException { + File f = new File(foutdir + File.separator + "frames.pdf"); + PDFJob job=new PDFJob(new FileOutputStream(f)); + PageFormat pf=new PageFormat(); + pf.setOrientation(PageFormat.PORTRAIT); + Paper p = new Paper(); + + p.setSize(frameImages.get(0).getWidth()+10,frameImages.get(0).getHeight()+10); + pf.setPaper(p); + + for(int i=0;i()); @@ -2074,6 +2107,7 @@ public final class SWF implements TreeItem, Timelined { } } } + t.setModified(true); } } @@ -2609,7 +2643,7 @@ public final class SWF implements TreeItem, Timelined { g.setColor(backGroundColor); g.fill(new Rectangle(image.getWidth(), image.getHeight())); } - Matrix m = new Matrix(); + Matrix m = transformation.clone(); m.translate(-rect.Xmin, -rect.Ymin); frameToImage(timeline, frame, time, stateUnderCursor, mouseButton, image, m, colorTransform); putToCache(key, image); diff --git a/src/com/jpexs/decompiler/flash/abc/ABC.java b/src/com/jpexs/decompiler/flash/abc/ABC.java index 4eca016e3..87e100566 100644 --- a/src/com/jpexs/decompiler/flash/abc/ABC.java +++ b/src/com/jpexs/decompiler/flash/abc/ABC.java @@ -332,7 +332,7 @@ public class ABC { } } } - } + } } public ABC(InputStream is, SWF swf, ABCContainerTag tag) throws IOException { diff --git a/src/com/jpexs/decompiler/flash/action/ActionDeobfuscation.java b/src/com/jpexs/decompiler/flash/action/ActionDeobfuscation.java index 0772814f5..166736ffc 100644 --- a/src/com/jpexs/decompiler/flash/action/ActionDeobfuscation.java +++ b/src/com/jpexs/decompiler/flash/action/ActionDeobfuscation.java @@ -95,6 +95,7 @@ public class ActionDeobfuscation { String changedName = deobfuscateName(name, false, "instance", namesMap, renameType, selected); if (changedName != null) { po.setInstanceName(changedName); + ((Tag)po).setModified(true); } } String className = po.getClassName(); @@ -102,6 +103,7 @@ public class ActionDeobfuscation { String changedClassName = deobfuscateNameWithPackage(className, namesMap, renameType, selected); if (changedClassName != null) { po.setClassName(changedClassName); + ((Tag)po).setModified(true); } } } diff --git a/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java b/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java index a92def373..4d2b18f99 100644 --- a/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java +++ b/src/com/jpexs/decompiler/flash/console/CommandLineArgumentParser.java @@ -20,6 +20,7 @@ import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler; import com.jpexs.decompiler.flash.ApplicationInfo; import com.jpexs.decompiler.flash.EventListener; import com.jpexs.decompiler.flash.SWF; +import static com.jpexs.decompiler.flash.SWF.frameToImageGet; import com.jpexs.decompiler.flash.SWFBundle; import com.jpexs.decompiler.flash.SWFSourceInfo; import com.jpexs.decompiler.flash.SearchMode; @@ -27,6 +28,15 @@ import com.jpexs.decompiler.flash.abc.RenameType; import com.jpexs.decompiler.flash.abc.avm2.parser.script.ActionScriptParser; import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.configuration.ConfigurationItem; +import com.jpexs.decompiler.flash.exporters.BinaryDataExporter; +import com.jpexs.decompiler.flash.exporters.FontExporter; +import com.jpexs.decompiler.flash.exporters.ImageExporter; +import com.jpexs.decompiler.flash.exporters.MorphShapeExporter; +import com.jpexs.decompiler.flash.exporters.MovieExporter; +import com.jpexs.decompiler.flash.exporters.ShapeExporter; +import com.jpexs.decompiler.flash.exporters.SoundExporter; +import com.jpexs.decompiler.flash.exporters.TextExporter; +import com.jpexs.decompiler.flash.exporters.commonshape.Matrix; import com.jpexs.decompiler.flash.exporters.modes.BinaryDataExportMode; import com.jpexs.decompiler.flash.exporters.modes.FontExportMode; import com.jpexs.decompiler.flash.exporters.modes.FramesExportMode; @@ -47,12 +57,23 @@ import com.jpexs.decompiler.flash.exporters.settings.ShapeExportSettings; import com.jpexs.decompiler.flash.exporters.settings.SoundExportSettings; import com.jpexs.decompiler.flash.exporters.settings.TextExportSettings; import com.jpexs.decompiler.flash.gui.Main; +import com.jpexs.decompiler.flash.tags.DefineSpriteTag; +import com.jpexs.decompiler.flash.tags.Tag; +import com.jpexs.decompiler.flash.tags.base.CharacterIdTag; +import com.jpexs.decompiler.flash.types.ColorTransform; +import com.jpexs.decompiler.flash.types.RECT; import com.jpexs.decompiler.flash.xfl.FLAVersion; import com.jpexs.helpers.Helper; import com.jpexs.helpers.Path; import com.jpexs.helpers.streams.SeekableInputStream; import com.sun.jna.Platform; import com.sun.jna.platform.win32.Kernel32; +import gnu.jpdf.PDFJob; +import java.awt.Color; +import java.awt.Graphics; +import java.awt.image.BufferedImage; +import java.awt.print.PageFormat; +import java.awt.print.Paper; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; @@ -95,20 +116,22 @@ public class CommandLineArgumentParser { //(as|pcode|pcodehex|hex|image|shape|movie|sound|binaryData|text|textplain|all|all_as|all_pcode|all_pcodehex|all_hex) public static void printCmdLineUsage() { + int cnt=1; System.out.println("Commandline arguments:"); - System.out.println(" 1) -help | --help | /?"); + System.out.println(" "+(cnt++)+") -help | --help | /?"); System.out.println(" ...shows commandline arguments (this help)"); - System.out.println(" 2) "); + System.out.println(" "+(cnt++)+") "); System.out.println(" ...opens SWF file with the decompiler GUI"); - System.out.println(" 3) -proxy [-P]"); + System.out.println(" "+(cnt++)+") -proxy [-P]"); System.out.println(" ...auto start proxy in the tray. Optional parameter -P specifies port for proxy. Defaults to 55555. "); - System.out.println(" 4) -export [-selectas3class ...]"); + System.out.println(" "+(cnt++)+") -export [-selectas3class ...]"); System.out.println(" ...export sources to "); System.out.println(" Values for parameter:"); System.out.println(" script - Scripts (Default format: ActionScript source)"); System.out.println(" - For this type, optional \"-selectas3class\" parameter can be passed to export only selected classes (ActionScript 3 only)"); System.out.println(" image - Images (Default format: PNG/JPEG)"); System.out.println(" shape - Shapes (Default format: SVG)"); + System.out.println(" morphshape - MorphShapes (Default format: SVG)"); System.out.println(" movie - Movies (Default format: FLV without sound)"); System.out.println(" frame - Frames (Default format: PNG)"); System.out.println(" sound - Sounds (Default format: MP3/WAV/FLV only sound)"); @@ -123,7 +146,7 @@ public class CommandLineArgumentParser { System.out.println(" Old DEPRECATED aliases include: (please use basic itemtypes and -format parameter instead)"); System.out.println(" as, pcode, pcodehex, hex, all_as, all_pcode, all_pcodehex, textplain"); System.out.println(); - System.out.println(" 5) -format "); + System.out.println(" "+(cnt++)+") -format "); System.out.println(" ...sets output formats for export"); System.out.println(" Values for parameter:"); System.out.println(" script:as - ActionScript source"); @@ -132,9 +155,14 @@ public class CommandLineArgumentParser { System.out.println(" script:hex - ActionScript Hex only"); System.out.println(" shape:svg - SVG format for Shapes"); System.out.println(" shape:png - PNG format for Shapes"); + System.out.println(" shape:canvas - HTML5 Canvas format for Shapes"); + System.out.println(" morphshape:svg - SVG format for MorphShapes"); + System.out.println(" morphshape:canvas - HTML5 Canvas format for MorphShapes"); System.out.println(" frame:png - PNG format for Frames"); System.out.println(" frame:gif - GIF format for Frames"); System.out.println(" frame:avi - AVI format for Frames"); + System.out.println(" frame:canvas - HTML5 Canvas format for Frames"); + System.out.println(" frame:pdf - PDF format for Frames"); System.out.println(" image:png_jpeg - PNG/JPEG format for Images"); System.out.println(" image:png - PNG format for Images"); System.out.println(" image:jpeg - JPEG format for Images"); @@ -150,19 +178,29 @@ public class CommandLineArgumentParser { System.out.println(" You can set multiple formats at once using comma (,)"); System.out.println(" DO NOT PUT space between comma (,) and next value."); System.out.println(" The prefix with colon (:) is neccessary."); - System.out.println(" 6) -dumpSWF "); + System.out.println(" "+(cnt++)+") -select "); + System.out.println(" ...selects frames/pages for export"); + System.out.println(" Example formats:"); + System.out.println(" 1-5"); + System.out.println(" 2,3"); + System.out.println(" 2-5,7,9-"); + System.out.println(" DO NOT PUT space between comma (,) and next ramge."); + System.out.println(" "+(cnt++)+") -selectid "); + System.out.println(" ...selects characters for export by character id"); + System.out.println(" format is same as in -select"); + System.out.println(" "+(cnt++)+") -dumpSWF "); System.out.println(" ...dumps list of SWF tags to console"); - System.out.println(" 7) -compress "); + System.out.println(" "+(cnt++)+") -compress "); System.out.println(" ...Compress SWF and save it to "); - System.out.println(" 8) -decompress "); + System.out.println(" "+(cnt++)+") -decompress "); System.out.println(" ...Decompress and save it to "); - System.out.println(" 9) -extract [-o |] [nocheck] [(all|biggest|smallest|first|last)]"); + System.out.println(" "+(cnt++)+") -extract [-o |] [nocheck] [(all|biggest|smallest|first|last)]"); System.out.println(" ...Extracts SWF files from ZIP or other binary files"); System.out.println(" ...-o parameter should contain a file path when \"biggest\" or \"first\" parameter is specified"); System.out.println(" ...-o parameter should contain a folder path when no exctaction mode or \"all\" parameter is specified"); - System.out.println(" 10) -renameInvalidIdentifiers (typeNumber|randomWord) e"); + System.out.println(" "+(cnt++)+") -renameInvalidIdentifiers (typeNumber|randomWord) e"); System.out.println(" ...Renames the invalid identifiers in and save it to "); - System.out.println(" 11) -config key=value[,key2=value2][,key3=value3...] [other parameters]"); + System.out.println(" "+(cnt++)+") -config key=value[,key2=value2][,key3=value3...] [other parameters]"); System.out.print(" ...Sets configuration values. Available keys[current setting]:"); for (ConfigurationItem item : commandlineConfigBoolean) { System.out.print(" " + item + "[" + item.get() + "]"); @@ -171,14 +209,18 @@ public class CommandLineArgumentParser { System.out.println(" Values are boolean, you can use 0/1, true/false, on/off or yes/no."); System.out.println(" If no other parameters passed, configuration is saved. Otherwise it is used only once."); System.out.println(" DO NOT PUT space between comma (,) and next value."); - System.out.println(" 12) -onerror (abort|retryN|ignore)"); + System.out.println(" "+(cnt++)+") -onerror (abort|retryN|ignore)"); System.out.println(" ...error handling mode. \"abort\" stops the exporting, \"retry\" tries the exporting N times, \"ignore\" ignores the current file"); - System.out.println(" 13) -timeout "); + System.out.println(" "+(cnt++)+") -timeout "); System.out.println(" ...decompilation timeout for a single method in AS3 or single action in AS1/2 in seconds"); - System.out.println(" 14) -exportTimeout "); + System.out.println(" "+(cnt++)+") -exportTimeout "); System.out.println(" ...total export timeout in seconds"); - System.out.println(" 15) -exportFileTimeout "); + System.out.println(" "+(cnt++)+") -exportFileTimeout "); System.out.println(" ...export timeout for a single AS3 class in seconds"); + System.out.println(" "+(cnt++)+") -flashpaper2pdf "); + System.out.println(" ...converts FlashPaper SWF file to PDF . Use -zoom parameter to specify image quality."); + System.out.println(" "+(cnt++)+") -zoom "); + System.out.println(" ...apply zoom during export (currently for FlashPaper conversion only)"); System.out.println(); System.out.println("Examples:"); System.out.println("java -jar ffdec.jar myfile.swf"); @@ -216,7 +258,9 @@ public class CommandLineArgumentParser { } AbortRetryIgnoreHandler handler = null; Map format = new HashMap<>(); - + double zoom = 1; + Selection selection=new Selection(); + Selection selectionIds=new Selection(); String nextParam; OUTER: while (true) { @@ -225,6 +269,15 @@ public class CommandLineArgumentParser { nextParam = nextParam.toLowerCase(); } switch (nextParam) { + case "-selectid": + selectionIds = parseSelect(args); + break; + case "-select": + selection = parseSelect(args); + break; + case "-zoom": + zoom = parseZoom(args); + break; case "-format": format = parseFormat(args); break; @@ -276,7 +329,7 @@ public class CommandLineArgumentParser { } else if (nextParam.equals("-proxy")) { parseProxy(args); } else if (nextParam.equals("-export")) { - parseExport(args, handler, traceLevel, format); + parseExport(selection,selectionIds,args, handler, traceLevel, format); } else if (nextParam.equals("-compress")) { parseCompress(args); } else if (nextParam.equals("-decompress")) { @@ -287,6 +340,8 @@ public class CommandLineArgumentParser { parseRenameInvalidIdentifiers(args); } else if (nextParam.equals("-dumpswf")) { parseDumpSwf(args); + } else if (nextParam.equals("-flashpaper2pdf")) { + parseFlashPaperToPdf(selection,zoom, args); } else if (nextParam.equals("-as3compiler")) { ActionScriptParser.compile(null /*?*/,args.remove(), args.remove()); } else if (nextParam.equals("-help") || nextParam.equals("--help") || nextParam.equals("/?")) { @@ -385,6 +440,110 @@ public class CommandLineArgumentParser { setConfigurations(args.remove()); } + + private static class Range { + public Integer min; + public Integer max; + + public Range(Integer min, Integer max) { + this.min = min; + this.max = max; + } + + public boolean contains(int index){ + Integer minimum = min == null? Integer.MIN_VALUE:min; + Integer maximum = max == null? Integer.MAX_VALUE:max; + + return index>=minimum && index<=maximum; + } + + } + + private static class Selection { + public List ranges; + + public Selection(){ + this.ranges=new ArrayList<>(); + this.ranges.add(new Range(null, null)); + } + + public Selection(List ranges) { + this.ranges = ranges; + } + + public boolean contains(int index){ + for(Range r:ranges){ + if(r.contains(index)){ + return true; + } + } + return false; + } + + } + + + private static Selection parseSelect(Queue args) { + List ret=new ArrayList<>(); + if (args.isEmpty()) { + System.err.println("range parameter expected"); + badArguments(); + } + String range=args.remove(); + String[] ranges; + if(range.contains(",")){ + ranges = range.split(","); + }else{ + ranges=new String[]{range}; + } + for(String r:ranges){ + Integer min = null; + Integer max = null; + if(r.contains("-")){ + String ps[]=r.split("\\-"); + if(ps.length!=2){ + System.err.println("invalid range"); + badArguments(); + } + try{ + if(!"".equals(ps[0])){ + min = Integer.parseInt(ps[0]); + } + if(!"".equals(ps[1])){ + max = Integer.parseInt(ps[1]); + } + }catch(NumberFormatException nfe){ + System.err.println("invalid range"); + badArguments(); + } + }else{ + try{ + min = Integer.parseInt(r); + max = min; + }catch(NumberFormatException nfe){ + System.err.println("invalid range"); + badArguments(); + } + } + ret.add(new Range(min, max)); + } + return new Selection(ret); + } + + private static double parseZoom(Queue args) { + if (args.isEmpty()) { + System.err.println("zoom parameter expected"); + badArguments(); + } + try{ + return Double.parseDouble(args.remove()); + }catch(NumberFormatException nfe){ + System.err.println("invalid zoom"); + badArguments(); + } + return 1; + } + private static AbortRetryIgnoreHandler parseOnError(Queue args) { int errorMode = AbortRetryIgnoreHandler.UNDEFINED; int retryCount = 0; @@ -540,7 +699,7 @@ public class CommandLineArgumentParser { } - private static void parseExport(Queue args, AbortRetryIgnoreHandler handler, Level traceLevel, Map formats) { + private static void parseExport(Selection selection, Selection selectionIds, Queue args, AbortRetryIgnoreHandler handler, Level traceLevel, Map formats) { if (args.size() < 3) { badArguments(); } @@ -595,6 +754,21 @@ public class CommandLineArgumentParser { try { SWF exfile = new SWF(new FileInputStream(inFile), Configuration.parallelSpeedUp.get()); + + List extags=new ArrayList<>(); + for(Tag t:exfile.tags){ + if(t instanceof CharacterIdTag){ + CharacterIdTag c=(CharacterIdTag)t; + if(selectionIds.contains(c.getCharacterId())){ + extags.add(t); + } + }else{ + if(selectionIds.contains(0)){ + extags.add(t); + } + } + } + final Level level = traceLevel; exfile.addEventListener(new EventListener() { @Override @@ -632,17 +806,19 @@ public class CommandLineArgumentParser { allExportMode = strToExportFormat(formats.get("script")); } System.out.println("Exporting images..."); - exfile.exportImages(handler, outDir.getAbsolutePath() + File.separator + "images", new ImageExportSettings(ImageExportMode.PNG_JPEG)); + new ImageExporter().exportImages(handler, outDir.getAbsolutePath() + File.separator + "images", extags, new ImageExportSettings(ImageExportMode.PNG_JPEG)); System.out.println("Exporting shapes..."); - exfile.exportShapes(handler, outDir.getAbsolutePath() + File.separator + "shapes", new ShapeExportSettings(ShapeExportMode.SVG)); + new ShapeExporter().exportShapes(handler, outDir.getAbsolutePath() + File.separator + "shapes", extags, new ShapeExportSettings(ShapeExportMode.SVG)); + System.out.println("Exporting morphshapes..."); + new MorphShapeExporter().exportMorphShapes(handler, outDir.getAbsolutePath() + File.separator + "morphshapes", extags, new MorphShapeExportSettings(MorphShapeExportMode.SVG)); System.out.println("Exporting scripts..."); exfile.exportActionScript(handler, outDir.getAbsolutePath() + File.separator + "scripts", allExportMode, Configuration.parallelSpeedUp.get()); System.out.println("Exporting movies..."); - exfile.exportMovies(handler, outDir.getAbsolutePath() + File.separator + "movies", new MovieExportSettings(MovieExportMode.FLV)); + new MovieExporter().exportMovies(handler, outDir.getAbsolutePath() + File.separator + "movies", extags, new MovieExportSettings(MovieExportMode.FLV)); System.out.println("Exporting sounds..."); - exfile.exportSounds(handler, outDir.getAbsolutePath() + File.separator + "sounds", new SoundExportSettings(SoundExportMode.MP3_WAV_FLV)); + new SoundExporter().exportSounds(handler, outDir.getAbsolutePath() + File.separator + "sounds", extags, new SoundExportSettings(SoundExportMode.MP3_WAV_FLV)); System.out.println("Exporting binaryData..."); - exfile.exportBinaryData(handler, outDir.getAbsolutePath() + File.separator + "binaryData", new BinaryDataExportSettings(BinaryDataExportMode.RAW)); + new BinaryDataExporter().exportBinaryData(handler, outDir.getAbsolutePath() + File.separator + "binaryData", extags, new BinaryDataExportSettings(BinaryDataExportMode.RAW)); System.out.println("Exporting texts..."); String allTextFormat = formats.get("text"); @@ -653,25 +829,22 @@ public class CommandLineArgumentParser { if (singleTextFile == null) { singleTextFile = Configuration.textExportSingleFile.get(); } - exfile.exportTexts(handler, outDir.getAbsolutePath() + File.separator + "texts", - new TextExportSettings(allTextFormat.equals("formatted") ? TextExportMode.FORMATTED : TextExportMode.PLAIN, singleTextFile)); + new TextExporter().exportTexts(handler, outDir.getAbsolutePath() + File.separator + "texts", extags, new TextExportSettings(allTextFormat.equals("formatted") ? TextExportMode.FORMATTED : TextExportMode.PLAIN, singleTextFile)); } break; case "image": { System.out.println("Exporting images..."); - exfile.exportImages(handler, outDir.getAbsolutePath() + (exportFormats.length > 1 ? File.separator + "images" : ""), - new ImageExportSettings(enumFromStr(formats.get("image"), ImageExportMode.class))); + new ImageExporter().exportImages(handler, outDir.getAbsolutePath() + (exportFormats.length > 1 ? File.separator + "images" : ""), extags, new ImageExportSettings(enumFromStr(formats.get("image"), ImageExportMode.class))); } break; case "shape": { System.out.println("Exporting shapes..."); - exfile.exportShapes(handler, outDir.getAbsolutePath() + (exportFormats.length > 1 ? File.separator + "shapes" : ""), - new ShapeExportSettings(enumFromStr(formats.get("shape"), ShapeExportMode.class))); + new ShapeExporter().exportShapes(handler, outDir.getAbsolutePath() + (exportFormats.length > 1 ? File.separator + "shapes" : ""), extags, new ShapeExportSettings(enumFromStr(formats.get("shape"), ShapeExportMode.class))); } + break; case "morphshape": { System.out.println("Exporting morphshapes..."); - exfile.exportMorphShapes(handler, outDir.getAbsolutePath() + (exportFormats.length > 1 ? File.separator + "morphshapes" : ""), - new MorphShapeExportSettings(enumFromStr(formats.get("morphshape"), MorphShapeExportMode.class))); + new MorphShapeExporter().exportMorphShapes(handler, outDir.getAbsolutePath() + (exportFormats.length > 1 ? File.separator + "morphshapes" : ""), extags, new MorphShapeExportSettings(enumFromStr(formats.get("morphshape"), MorphShapeExportMode.class))); } break; case "script": @@ -695,31 +868,37 @@ public class CommandLineArgumentParser { break; case "movie": { System.out.println("Exporting movies..."); - exfile.exportMovies(handler, outDir.getAbsolutePath() + (exportFormats.length > 1 ? File.separator + "movies" : ""), + new MovieExporter().exportMovies(handler, outDir.getAbsolutePath() + (exportFormats.length > 1 ? File.separator + "movies" : ""),extags, new MovieExportSettings(enumFromStr(formats.get("movie"), MovieExportMode.class))); } break; case "font": { System.out.println("Exporting fonts..."); - exfile.exportFonts(handler, outDir.getAbsolutePath() + (exportFormats.length > 1 ? File.separator + "fonts" : ""), + new FontExporter().exportFonts(handler, outDir.getAbsolutePath() + (exportFormats.length > 1 ? File.separator + "fonts" : ""),extags, new FontExportSettings(enumFromStr(formats.get("font"), FontExportMode.class))); } break; case "frame": { System.out.println("Exporting frames..."); - exfile.exportFrames(handler, outDir.getAbsolutePath() + (exportFormats.length > 1 ? File.separator + "frames" : ""), 0, null, + List frames=new ArrayList<>(); + for(int i=0;i 1 ? File.separator + "frames" : ""), 0, frames, new FramesExportSettings(enumFromStr(formats.get("frame"), FramesExportMode.class))); } break; case "sound": { System.out.println("Exporting sounds..."); - exfile.exportSounds(handler, outDir.getAbsolutePath() + (exportFormats.length > 1 ? File.separator + "sounds" : ""), + new SoundExporter().exportSounds(handler, outDir.getAbsolutePath() + (exportFormats.length > 1 ? File.separator + "sounds" : ""),extags, new SoundExportSettings(enumFromStr(formats.get("sound"), SoundExportMode.class))); } break; case "binarydata": { System.out.println("Exporting binaryData..."); - exfile.exportBinaryData(handler, outDir.getAbsolutePath() + (exportFormats.length > 1 ? File.separator + "binaryData" : ""), + new BinaryDataExporter().exportBinaryData(handler, outDir.getAbsolutePath() + (exportFormats.length > 1 ? File.separator + "binaryData" : ""),extags, new BinaryDataExportSettings(enumFromStr(formats.get("binarydata"), BinaryDataExportMode.class))); } break; @@ -729,7 +908,7 @@ public class CommandLineArgumentParser { if (singleTextFile == null) { singleTextFile = Configuration.textExportSingleFile.get(); } - exfile.exportTexts(handler, outDir.getAbsolutePath() + (exportFormats.length > 1 ? File.separator + "texts" : ""), + new TextExporter().exportTexts(handler, outDir.getAbsolutePath() + (exportFormats.length > 1 ? File.separator + "texts" : ""),extags, new TextExportSettings(enumFromStr(formats.get("text"), TextExportMode.class), singleTextFile)); } break; @@ -739,7 +918,7 @@ public class CommandLineArgumentParser { if (singleTextFile == null) { singleTextFile = Configuration.textExportSingleFile.get(); } - exfile.exportTexts(handler, outDir.getAbsolutePath() + (exportFormats.length > 1 ? File.separator + "texts" : ""), + new TextExporter().exportTexts(handler, outDir.getAbsolutePath() + (exportFormats.length > 1 ? File.separator + "texts" : ""),extags, new TextExportSettings(TextExportMode.PLAIN, singleTextFile)); } break; @@ -976,6 +1155,90 @@ public class CommandLineArgumentParser { } return ret; } + + private static void parseFlashPaperToPdf(Selection selection,double zoom,Queue args){ + if (args.size()<2) { + badArguments(); + } + File inFile = new File(args.remove()); + File outFile=new File(args.remove()); + printHeader(); + + try(FileInputStream is = new FileInputStream(inFile)) { + + PDFJob job = null; + + SWF swf = new SWF(is,Configuration.parallelSpeedUp.get()); + int totalPages = 0; + + for(Tag t:swf.tags){ + if(t instanceof DefineSpriteTag){ + DefineSpriteTag ds=(DefineSpriteTag)t; + if("page1".equals(ds.getExportName())){ + totalPages = 1; + }else{ + if(totalPages>0){ + totalPages++; + } + } + } + } + + int page = 0; + + for(Tag t:swf.tags){ + if(t instanceof DefineSpriteTag){ + DefineSpriteTag ds=(DefineSpriteTag)t; + if("page1".equals(ds.getExportName())){ + page = 1; + job=new PDFJob(new FileOutputStream(outFile)); + }else{ + if(page>0){ + page++; + } + } + if(("page"+page).equals(ds.getExportName())){ + if(!selection.contains(page)){ + continue; + } + System.out.print("Page "+page+"/"+totalPages+"..."); + RECT displayRect = new RECT(ds.getTimeline().displayRect); + displayRect.Xmax*=zoom; + displayRect.Ymax*=zoom; + Matrix m=new Matrix(); + m.scale(zoom); + BufferedImage img = swf.frameToImageGet(ds.getTimeline(), 0, 0, null, 0, displayRect,m , new ColorTransform(), Color.white).getBufferedImage(); + PageFormat pf=new PageFormat(); + pf.setOrientation(PageFormat.PORTRAIT); + Paper p = new Paper(); + p.setSize(img.getWidth(),img.getHeight()); + pf.setPaper(p); + Graphics g = job.getGraphics(pf); + g.drawImage(img, 0, 0,img.getWidth(),img.getHeight(), null); + g.dispose(); + System.out.println("OK"); + + + } + } + } + + if(job == null){ + System.err.println("No pages found. Maybe it is not a FlashPaper file"); + System.exit(2); + } + job.end(); + + } catch (FileNotFoundException ex) { + System.err.println("File not found"); + System.exit(1); + } catch (IOException | InterruptedException ex) { + System.err.println("I/O error during reading"); + System.exit(2); + } + System.exit(0); + } + private static void parseDumpSwf(Queue args) { if (args.isEmpty()) { diff --git a/src/com/jpexs/decompiler/flash/exporters/modes/FramesExportMode.java b/src/com/jpexs/decompiler/flash/exporters/modes/FramesExportMode.java index 8f458768e..e87770bf2 100644 --- a/src/com/jpexs/decompiler/flash/exporters/modes/FramesExportMode.java +++ b/src/com/jpexs/decompiler/flash/exporters/modes/FramesExportMode.java @@ -21,10 +21,10 @@ package com.jpexs.decompiler.flash.exporters.modes; * @author JPEXS */ public enum FramesExportMode { - - PNG, + PNG, GIF, AVI, SVG, - CANVAS + CANVAS, + PDF } diff --git a/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog.properties b/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog.properties index 4f635c3b4..94e0b6b37 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog.properties @@ -61,6 +61,7 @@ frames.gif = GIF frames.avi = AVI frames.svg = SVG frames.canvas = HTML5 Canvas +frames.pdf = PDF fonts = Fonts fonts.ttf = TTF diff --git a/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java b/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java index 07726dc60..62df54f56 100644 --- a/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java +++ b/src/com/jpexs/decompiler/flash/tags/DefineButtonTag.java @@ -375,4 +375,9 @@ public class DefineButtonTag extends ButtonTag implements ASMSource { public void resetTimeline() { timeline = null; } + + @Override + public Tag getSourceTag() { + return this; + } } diff --git a/src/com/jpexs/decompiler/flash/tags/DoActionTag.java b/src/com/jpexs/decompiler/flash/tags/DoActionTag.java index 8ef4a2fcb..450180af0 100644 --- a/src/com/jpexs/decompiler/flash/tags/DoActionTag.java +++ b/src/com/jpexs/decompiler/flash/tags/DoActionTag.java @@ -187,4 +187,11 @@ public class DoActionTag extends Tag implements ASMSource { public String removePrefixAndSuffix(String source) { return source; } + + @Override + public Tag getSourceTag() { + return this; + } + + } diff --git a/src/com/jpexs/decompiler/flash/tags/DoInitActionTag.java b/src/com/jpexs/decompiler/flash/tags/DoInitActionTag.java index f99358411..99e42e77c 100644 --- a/src/com/jpexs/decompiler/flash/tags/DoInitActionTag.java +++ b/src/com/jpexs/decompiler/flash/tags/DoInitActionTag.java @@ -235,4 +235,11 @@ public class DoInitActionTag extends CharacterIdTag implements ASMSource { public String removePrefixAndSuffix(String source) { return source; } + + @Override + public Tag getSourceTag() { + return this; + } + + } diff --git a/src/com/jpexs/decompiler/flash/tags/base/ASMSource.java b/src/com/jpexs/decompiler/flash/tags/base/ASMSource.java index 62944fd53..edfde1567 100644 --- a/src/com/jpexs/decompiler/flash/tags/base/ASMSource.java +++ b/src/com/jpexs/decompiler/flash/tags/base/ASMSource.java @@ -20,6 +20,7 @@ import com.jpexs.decompiler.flash.DisassemblyListener; import com.jpexs.decompiler.flash.action.Action; import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; import com.jpexs.decompiler.flash.helpers.GraphTextWriter; +import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.flash.treeitems.TreeItem; import java.util.List; @@ -84,4 +85,6 @@ public interface ASMSource extends TreeItem { public int getPrefixLineCount(); public String removePrefixAndSuffix(String source); + + public Tag getSourceTag(); } diff --git a/src/com/jpexs/decompiler/flash/types/BUTTONCONDACTION.java b/src/com/jpexs/decompiler/flash/types/BUTTONCONDACTION.java index 6d6fbf257..c2d9be522 100644 --- a/src/com/jpexs/decompiler/flash/types/BUTTONCONDACTION.java +++ b/src/com/jpexs/decompiler/flash/types/BUTTONCONDACTION.java @@ -311,4 +311,11 @@ public class BUTTONCONDACTION implements ASMSource, Exportable, ContainerItem, S public String getExportFileName() { return getHeader(true); } + + @Override + public Tag getSourceTag() { + return tag; + } + + } diff --git a/src/com/jpexs/decompiler/flash/types/CLIPACTIONRECORD.java b/src/com/jpexs/decompiler/flash/types/CLIPACTIONRECORD.java index 5689881c7..56e464fe2 100644 --- a/src/com/jpexs/decompiler/flash/types/CLIPACTIONRECORD.java +++ b/src/com/jpexs/decompiler/flash/types/CLIPACTIONRECORD.java @@ -280,4 +280,11 @@ public class CLIPACTIONRECORD implements ASMSource, Exportable, ContainerItem, S public String getExportFileName() { return eventFlags.getHeader(keyCode, true); } + + @Override + public Tag getSourceTag() { + return tag; + } + + }