From 5a5dc0374e3486e79ad713e187e7d04a1fe0651b Mon Sep 17 00:00:00 2001 From: Jeremias Maerki Date: Thu, 27 Mar 2003 10:24:55 +0000 Subject: [PATCH] All factory code (make*) has been moved to this new factory class. PDFDocument was too long. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@196154 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/pdf/PDFFactory.java | 1297 +++++++++++++++++++ 1 file changed, 1297 insertions(+) create mode 100644 src/java/org/apache/fop/pdf/PDFFactory.java diff --git a/src/java/org/apache/fop/pdf/PDFFactory.java b/src/java/org/apache/fop/pdf/PDFFactory.java new file mode 100644 index 000000000..cee35861b --- /dev/null +++ b/src/java/org/apache/fop/pdf/PDFFactory.java @@ -0,0 +1,1297 @@ +/* + * $Id$ + * ============================================================================ + * The Apache Software License, Version 1.1 + * ============================================================================ + * + * Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modifica- + * tion, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: "This product includes software + * developed by the Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "FOP" and "Apache Software Foundation" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache", nor may + * "Apache" appear in their name, without prior written permission of the + * Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- + * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ============================================================================ + * + * This software consists of voluntary contributions made by many individuals + * on behalf of the Apache Software Foundation and was originally created by + * James Tauber . For more information on the Apache + * Software Foundation, please see . + */ +package org.apache.fop.pdf; + +import java.awt.geom.Rectangle2D; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.Map; + +import org.apache.fop.util.StreamUtilities; +import org.apache.avalon.framework.container.ContainerUtil; +import org.apache.fop.fonts.CIDFont; +import org.apache.fop.fonts.CustomFont; +import org.apache.fop.fonts.Font; +import org.apache.fop.fonts.FontDescriptor; +import org.apache.fop.fonts.FontMetrics; +import org.apache.fop.fonts.FontType; +import org.apache.fop.fonts.LazyFont; +import org.apache.fop.fonts.MultiByteFont; +import org.apache.fop.fonts.truetype.FontFileReader; +import org.apache.fop.fonts.truetype.TTFSubSetFile; +import org.apache.fop.fonts.type1.PFBData; +import org.apache.fop.fonts.type1.PFBParser; + +/** + * This class provides method to create and register PDF objects. + */ +public class PDFFactory { + + private PDFDocument document; + + /** + * Creates a new PDFFactory. + * @param document the parent PDFDocument needed to register the generated + * objects + */ + public PDFFactory(PDFDocument document) { + this.document = document; + } + + /** + * Returns the parent PDFDocument associated with this factory. + * @return PDFDocument the parent PDFDocument + */ + public final PDFDocument getDocument() { + return this.document; + } + + /* ========================= structure objects ========================= */ + + /** + * Make a /Catalog (Root) object. This object is written in + * the trailer. + * + * @param pages the pages pdf object that the root points to + * @return the new pdf root object for this document + */ + public PDFRoot makeRoot(PDFPages pages) { + //Make a /Pages object. This object is written in the trailer. + PDFRoot pdfRoot = new PDFRoot(++this.document.objectcount, pages); + getDocument().addTrailerObject(pdfRoot); + return pdfRoot; + } + + /** + * Make a /Pages object. This object is written in the trailer. + * + * @return a new PDF Pages object for adding pages to + */ + public PDFPages makePages() { + PDFPages pdfPages = new PDFPages(++this.document.objectcount); + getDocument().addTrailerObject(pdfPages); + return pdfPages; + } + + /** + * Make a /Resources object. This object is written in the trailer. + * + * @return a new PDF resources object + */ + public PDFResources makeResources() { + PDFResources pdfResources = new PDFResources(++this.document.objectcount); + getDocument().addTrailerObject(pdfResources); + return pdfResources; + } + + /** + * make an /Info object + * + * @param prod string indicating application producing the PDF + * @return the created /Info object + */ + protected PDFInfo makeInfo(String prod) { + + /* + * create a PDFInfo with the next object number and add to + * list of objects + */ + PDFInfo pdfInfo = new PDFInfo(); + // set the default producer + pdfInfo.setProducer(prod); + getDocument().registerObject(pdfInfo); + return pdfInfo; + } + + /** + * Make a /Page object. The page is assigned an object number immediately + * so references can already be made. The page must be added to the + * PDFDocument later using addObject(). + * + * @param resources resources object to use + * @param pagewidth width of the page in points + * @param pageheight height of the page in points + * + * @return the created /Page object + */ + public PDFPage makePage(PDFResources resources, + int pagewidth, int pageheight) { + + /* + * create a PDFPage with the next object number, the given + * resources, contents and dimensions + */ + PDFPage page = new PDFPage(resources, + pagewidth, pageheight); + + getDocument().assignObjectNumber(page); + getDocument().getPages().addPage(page); + return page; + } + + /* ========================= functions ================================= */ + + /** + * Make a Type 0 sampled function + * + * @param theDomain List objects of Double objects. + * This is the domain of the function. + * See page 264 of the PDF 1.3 Spec. + * @param theRange List objects of Double objects. + * This is the Range of the function. + * See page 264 of the PDF 1.3 Spec. + * @param theSize A List object of Integer objects. + * This is the number of samples in each input dimension. + * I can't imagine there being more or less than two input dimensions, + * so maybe this should be an array of length 2. + * + * See page 265 of the PDF 1.3 Spec. + * @param theBitsPerSample An int specifying the number of bits user + * to represent each sample value. + * Limited to 1,2,4,8,12,16,24 or 32. + * See page 265 of the 1.3 PDF Spec. + * @param theOrder The order of interpolation between samples. + * Default is 1 (one). Limited + * to 1 (one) or 3, which means linear or cubic-spline interpolation. + * + * This attribute is optional. + * + * See page 265 in the PDF 1.3 spec. + * @param theEncode List objects of Double objects. + * This is the linear mapping of input values intop the domain + * of the function's sample table. Default is hard to represent in + * ascii, but basically [0 (Size0 1) 0 (Size1 1)...]. + * This attribute is optional. + * + * See page 265 in the PDF 1.3 spec. + * @param theDecode List objects of Double objects. + * This is a linear mapping of sample values into the range. + * The default is just the range. + * + * This attribute is optional. + * Read about it on page 265 of the PDF 1.3 spec. + * @param theFunctionDataStream The sample values that specify + * the function are provided in a stream. + * + * This is optional, but is almost always used. + * + * Page 265 of the PDF 1.3 spec has more. + * @param theFilter This is a vector of String objects which + * are the various filters that have are to be + * applied to the stream to make sense of it. + * Order matters, so watch out. + * + * This is not documented in the Function section of the PDF 1.3 spec, + * it was deduced from samples that this is sometimes used, even if we may never + * use it in FOP. It is added for completeness sake. + * @param theFunctionType This is the type of function (0,2,3, or 4). + * It should be 0 as this is the constructor for sampled functions. + * @return the PDF function that was created + */ + public PDFFunction makeFunction(int theFunctionType, List theDomain, + List theRange, List theSize, + int theBitsPerSample, int theOrder, + List theEncode, List theDecode, + StringBuffer theFunctionDataStream, + List theFilter) { + // Type 0 function + PDFFunction function = new PDFFunction(theFunctionType, theDomain, + theRange, theSize, + theBitsPerSample, theOrder, + theEncode, theDecode, + theFunctionDataStream, + theFilter); + + PDFFunction oldfunc = getDocument().findFunction(function); + if (oldfunc == null) { + getDocument().registerObject(function); + } else { + function = oldfunc; + } + return (function); + } + + /** + * make a type Exponential interpolation function + * (for shading usually) + * + * @param theDomain List objects of Double objects. + * This is the domain of the function. + * See page 264 of the PDF 1.3 Spec. + * @param theRange List of Doubles that is the Range of the function. + * See page 264 of the PDF 1.3 Spec. + * @param theCZero This is a vector of Double objects which defines the function result + * when x=0. + * + * This attribute is optional. + * It's described on page 268 of the PDF 1.3 spec. + * @param theCOne This is a vector of Double objects which defines the function result + * when x=1. + * + * This attribute is optional. + * It's described on page 268 of the PDF 1.3 spec. + * @param theInterpolationExponentN This is the inerpolation exponent. + * + * This attribute is required. + * PDF Spec page 268 + * @param theFunctionType The type of the function, which should be 2. + * @return the PDF function that was created + */ + public PDFFunction makeFunction(int theFunctionType, List theDomain, + List theRange, List theCZero, + List theCOne, + double theInterpolationExponentN) { // type 2 + PDFFunction function = new PDFFunction(theFunctionType, theDomain, + theRange, theCZero, theCOne, + theInterpolationExponentN); + PDFFunction oldfunc = getDocument().findFunction(function); + if (oldfunc == null) { + getDocument().registerObject(function); + } else { + function = oldfunc; + } + return (function); + } + + /** + * Make a Type 3 Stitching function + * + * @param theDomain List objects of Double objects. + * This is the domain of the function. + * See page 264 of the PDF 1.3 Spec. + * @param theRange List objects of Double objects. + * This is the Range of the function. + * See page 264 of the PDF 1.3 Spec. + * @param theFunctions An List of the PDFFunction objects + * that the stitching function stitches. + * + * This attributed is required. + * It is described on page 269 of the PDF spec. + * @param theBounds This is a vector of Doubles representing + * the numbers that, in conjunction with Domain + * define the intervals to which each function from + * the 'functions' object applies. It must be in + * order of increasing magnitude, and each must be + * within Domain. + * + * It basically sets how much of the gradient each function handles. + * + * This attributed is required. + * It's described on page 269 of the PDF 1.3 spec. + * @param theEncode List objects of Double objects. + * This is the linear mapping of input values intop the domain + * of the function's sample table. Default is hard to represent in + * ascii, but basically [0 (Size0 1) 0 (Size1 1)...]. + * This attribute is required. + * + * See page 270 in the PDF 1.3 spec. + * @param theFunctionType This is the function type. It should be 3, + * for a stitching function. + * @return the PDF function that was created + */ + public PDFFunction makeFunction(int theFunctionType, List theDomain, + List theRange, List theFunctions, + List theBounds, + List theEncode) { + // Type 3 + + PDFFunction function = new PDFFunction(theFunctionType, theDomain, + theRange, theFunctions, + theBounds, theEncode); + + PDFFunction oldfunc = getDocument().findFunction(function); + if (oldfunc == null) { + getDocument().registerObject(function); + } else { + function = oldfunc; + } + return (function); + } + + /** + * make a postscript calculator function + * + * @param theNumber the PDF object number + * @param theFunctionType the type of function to make + * @param theDomain the domain values + * @param theRange the range values of the function + * @param theFunctionDataStream a string containing the pdf drawing + * @return the PDF function that was created + */ + public PDFFunction makeFunction(int theNumber, int theFunctionType, + List theDomain, List theRange, + StringBuffer theFunctionDataStream) { + // Type 4 + PDFFunction function = new PDFFunction(theFunctionType, theDomain, + theRange, + theFunctionDataStream); + + PDFFunction oldfunc = getDocument().findFunction(function); + if (oldfunc == null) { + getDocument().registerObject(function); + } else { + function = oldfunc; + } + return (function); + + } + + /* ========================= shadings ================================== */ + + /** + * make a function based shading object + * + * @param res the PDF resource context to add the shading, may be null + * @param theShadingType The type of shading object, which should be 1 for function + * based shading. + * @param theColorSpace The colorspace is 'DeviceRGB' or something similar. + * @param theBackground An array of color components appropriate to the + * colorspace key specifying a single color value. + * This key is used by the f operator buy ignored by the sh operator. + * @param theBBox List of double's representing a rectangle + * in the coordinate space that is current at the + * time of shading is imaged. Temporary clipping + * boundary. + * @param theAntiAlias Whether or not to anti-alias. + * @param theDomain Optional vector of Doubles specifying the domain. + * @param theMatrix List of Doubles specifying the matrix. + * If it's a pattern, then the matrix maps it to pattern space. + * If it's a shading, then it maps it to current user space. + * It's optional, the default is the identity matrix + * @param theFunction The PDF Function that maps an (x,y) location to a color + * @return the PDF shading that was created + */ + public PDFShading makeShading(PDFResourceContext res, int theShadingType, + PDFColorSpace theColorSpace, + List theBackground, List theBBox, + boolean theAntiAlias, List theDomain, + List theMatrix, + PDFFunction theFunction) { + // make Shading of Type 1 + PDFShading shading = new PDFShading(theShadingType, + theColorSpace, theBackground, + theBBox, theAntiAlias, theDomain, + theMatrix, theFunction); + + PDFShading oldshad = getDocument().findShading(shading); + if (oldshad == null) { + getDocument().registerObject(shading); + } else { + shading = oldshad; + } + + // add this shading to resources + if (res != null) { + res.getPDFResources().addShading(shading); + } else { + getDocument().getResources().addShading(shading); + } + + return (shading); + } + + /** + * Make an axial or radial shading object. + * + * @param res the PDF resource context to add the shading, may be null + * @param theShadingType 2 or 3 for axial or radial shading + * @param theColorSpace "DeviceRGB" or similar. + * @param theBackground theBackground An array of color components appropriate to the + * colorspace key specifying a single color value. + * This key is used by the f operator buy ignored by the sh operator. + * @param theBBox List of double's representing a rectangle + * in the coordinate space that is current at the + * time of shading is imaged. Temporary clipping + * boundary. + * @param theAntiAlias Default is false + * @param theCoords List of four (type 2) or 6 (type 3) Double + * @param theDomain List of Doubles specifying the domain + * @param theFunction the Stitching (PDFfunction type 3) function, + * even if it's stitching a single function + * @param theExtend List of Booleans of whether to extend the + * start and end colors past the start and end points + * The default is [false, false] + * @return the PDF shading that was created + */ + public PDFShading makeShading(PDFResourceContext res, int theShadingType, + PDFColorSpace theColorSpace, + List theBackground, List theBBox, + boolean theAntiAlias, List theCoords, + List theDomain, PDFFunction theFunction, + List theExtend) { + // make Shading of Type 2 or 3 + PDFShading shading = new PDFShading(theShadingType, + theColorSpace, theBackground, + theBBox, theAntiAlias, theCoords, + theDomain, theFunction, + theExtend); + + PDFShading oldshad = getDocument().findShading(shading); + if (oldshad == null) { + getDocument().registerObject(shading); + } else { + shading = oldshad; + } + + if (res != null) { + res.getPDFResources().addShading(shading); + } else { + getDocument().getResources().addShading(shading); + } + + return (shading); + } + + /** + * Make a free-form gouraud shaded triangle mesh, coons patch mesh, or tensor patch mesh + * shading object + * + * @param res the PDF resource context to add the shading, may be null + * @param theShadingType 4, 6, or 7 depending on whether it's + * Free-form gouraud-shaded triangle meshes, coons patch meshes, + * or tensor product patch meshes, respectively. + * @param theColorSpace "DeviceRGB" or similar. + * @param theBackground theBackground An array of color components appropriate to the + * colorspace key specifying a single color value. + * This key is used by the f operator buy ignored by the sh operator. + * @param theBBox List of double's representing a rectangle + * in the coordinate space that is current at the + * time of shading is imaged. Temporary clipping + * boundary. + * @param theAntiAlias Default is false + * @param theBitsPerCoordinate 1,2,4,8,12,16,24 or 32. + * @param theBitsPerComponent 1,2,4,8,12, and 16 + * @param theBitsPerFlag 2,4,8. + * @param theDecode List of Doubles see PDF 1.3 spec pages 303 to 312. + * @param theFunction the PDFFunction + * @return the PDF shading that was created + */ + public PDFShading makeShading(PDFResourceContext res, int theShadingType, + PDFColorSpace theColorSpace, + List theBackground, List theBBox, + boolean theAntiAlias, + int theBitsPerCoordinate, + int theBitsPerComponent, + int theBitsPerFlag, List theDecode, + PDFFunction theFunction) { + // make Shading of type 4,6 or 7 + PDFShading shading = new PDFShading(theShadingType, + theColorSpace, theBackground, + theBBox, theAntiAlias, + theBitsPerCoordinate, + theBitsPerComponent, + theBitsPerFlag, theDecode, + theFunction); + + PDFShading oldshad = getDocument().findShading(shading); + if (oldshad == null) { + getDocument().registerObject(shading); + } else { + shading = oldshad; + } + + if (res != null) { + res.getPDFResources().addShading(shading); + } else { + getDocument().getResources().addShading(shading); + } + + return (shading); + } + + /** + * make a Lattice-Form Gouraud mesh shading object + * + * @param res the PDF resource context to add the shading, may be null + * @param theShadingType 5 for lattice-Form Gouraud shaded-triangle mesh + * without spaces. "Shading1" or "Sh1" are good examples. + * @param theColorSpace "DeviceRGB" or similar. + * @param theBackground theBackground An array of color components appropriate to the + * colorspace key specifying a single color value. + * This key is used by the f operator buy ignored by the sh operator. + * @param theBBox List of double's representing a rectangle + * in the coordinate space that is current at the + * time of shading is imaged. Temporary clipping + * boundary. + * @param theAntiAlias Default is false + * @param theBitsPerCoordinate 1,2,4,8,12,16, 24, or 32 + * @param theBitsPerComponent 1,2,4,8,12,24,32 + * @param theDecode List of Doubles. See page 305 in PDF 1.3 spec. + * @param theVerticesPerRow number of vertices in each "row" of the lattice. + * @param theFunction The PDFFunction that's mapped on to this shape + * @return the PDF shading that was created + */ + public PDFShading makeShading(PDFResourceContext res, int theShadingType, + PDFColorSpace theColorSpace, + List theBackground, List theBBox, + boolean theAntiAlias, + int theBitsPerCoordinate, + int theBitsPerComponent, List theDecode, + int theVerticesPerRow, + PDFFunction theFunction) { + // make shading of Type 5 + PDFShading shading = new PDFShading(theShadingType, + theColorSpace, theBackground, + theBBox, theAntiAlias, + theBitsPerCoordinate, + theBitsPerComponent, theDecode, + theVerticesPerRow, theFunction); + + PDFShading oldshad = getDocument().findShading(shading); + if (oldshad == null) { + getDocument().registerObject(shading); + } else { + shading = oldshad; + } + + if (res != null) { + res.getPDFResources().addShading(shading); + } else { + getDocument().getResources().addShading(shading); + } + + return (shading); + } + + /* ========================= patterns ================================== */ + + /** + * Make a tiling pattern + * + * @param res the PDF resource context to add the shading, may be null + * @param thePatternType the type of pattern, which is 1 for tiling. + * @param theResources the resources associated with this pattern + * @param thePaintType 1 or 2, colored or uncolored. + * @param theTilingType 1, 2, or 3, constant spacing, no distortion, or faster tiling + * @param theBBox List of Doubles: The pattern cell bounding box + * @param theXStep horizontal spacing + * @param theYStep vertical spacing + * @param theMatrix Optional List of Doubles transformation matrix + * @param theXUID Optional vector of Integers that uniquely identify the pattern + * @param thePatternDataStream The stream of pattern data to be tiled. + * @return the PDF pattern that was created + */ + public PDFPattern makePattern(PDFResourceContext res, int thePatternType, // 1 + PDFResources theResources, int thePaintType, int theTilingType, + List theBBox, double theXStep, + double theYStep, List theMatrix, + List theXUID, StringBuffer thePatternDataStream) { + // PDFResources theResources + PDFPattern pattern = new PDFPattern(theResources, 1, + thePaintType, theTilingType, + theBBox, theXStep, theYStep, + theMatrix, theXUID, + thePatternDataStream); + + PDFPattern oldpatt = getDocument().findPattern(pattern); + if (oldpatt == null) { + getDocument().registerObject(pattern); + } else { + pattern = oldpatt; + } + + if (res != null) { + res.getPDFResources().addPattern(pattern); + } else { + getDocument().getResources().addPattern(pattern); + } + + return (pattern); + } + + /** + * Make a smooth shading pattern + * + * @param res the PDF resource context to add the shading, may be null + * @param thePatternType the type of the pattern, which is 2, smooth shading + * @param theShading the PDF Shading object that comprises this pattern + * @param theXUID optional:the extended unique Identifier if used. + * @param theExtGState optional: the extended graphics state, if used. + * @param theMatrix Optional:List of Doubles that specify the matrix. + * @return the PDF pattern that was created + */ + public PDFPattern makePattern(PDFResourceContext res, + int thePatternType, PDFShading theShading, + List theXUID, StringBuffer theExtGState, + List theMatrix) { + PDFPattern pattern = new PDFPattern(2, theShading, + theXUID, theExtGState, theMatrix); + + PDFPattern oldpatt = getDocument().findPattern(pattern); + if (oldpatt == null) { + getDocument().registerObject(pattern); + } else { + pattern = oldpatt; + } + + if (res != null) { + res.getPDFResources().addPattern(pattern); + } else { + getDocument().getResources().addPattern(pattern); + } + + return (pattern); + } + + /** + * Make a gradient + * + * @param res the PDF resource context to add the shading, may be null + * @param radial if true a radial gradient will be created + * @param theColorspace the colorspace of the gradient + * @param theColors the list of colors for the gradient + * @param theBounds the list of bounds associated with the colors + * @param theCoords the coordinates for the gradient + * @return the PDF pattern that was created + */ + public PDFPattern makeGradient(PDFResourceContext res, boolean radial, + PDFColorSpace theColorspace, + List theColors, List theBounds, + List theCoords) { + PDFShading myShad; + PDFFunction myfunky; + PDFFunction myfunc; + List theCzero; + List theCone; + PDFPattern myPattern; + //PDFColorSpace theColorSpace; + double interpolation = (double)1.000; + List theFunctions = new java.util.ArrayList(); + + int currentPosition; + int lastPosition = theColors.size() - 1; + + + // if 5 elements, the penultimate element is 3. + // do not go beyond that, because you always need + // to have a next color when creating the function. + + for (currentPosition = 0; currentPosition < lastPosition; + currentPosition++) { // for every consecutive color pair + PDFColor currentColor = + (PDFColor)theColors.get(currentPosition); + PDFColor nextColor = (PDFColor)theColors.get(currentPosition + + 1); + // colorspace must be consistant + if (getDocument().getColorSpace() + != currentColor.getColorSpace()) { + currentColor.setColorSpace( + getDocument().getColorSpace()); + } + + if (getDocument().getColorSpace() + != nextColor.getColorSpace()) { + nextColor.setColorSpace( + getDocument().getColorSpace()); + } + + theCzero = currentColor.getVector(); + theCone = nextColor.getVector(); + + myfunc = makeFunction(2, null, null, theCzero, theCone, + interpolation); + + theFunctions.add(myfunc); + + } // end of for every consecutive color pair + + myfunky = makeFunction(3, null, null, theFunctions, theBounds, + null); + + if (radial) { + if (theCoords.size() == 6) { + myShad = makeShading(res, 3, getDocument().getPDFColorSpace(), + null, null, + false, theCoords, null, myfunky, + null); + } else { // if the center x, center y, and radius specifiy + // the gradient, then assume the same center x, center y, + // and radius of zero for the other necessary component + List newCoords = new java.util.ArrayList(); + newCoords.add(theCoords.get(0)); + newCoords.add(theCoords.get(1)); + newCoords.add(theCoords.get(2)); + newCoords.add(theCoords.get(0)); + newCoords.add(theCoords.get(1)); + newCoords.add(new Double(0.0)); + + myShad = makeShading(res, 3, getDocument().getPDFColorSpace(), + null, null, + false, newCoords, null, myfunky, + null); + + } + } else { + myShad = makeShading(res, 2, getDocument().getPDFColorSpace(), + null, null, + false, theCoords, null, myfunky, + null); + + } + + myPattern = makePattern(res, 2, myShad, null, null, null); + + return (myPattern); + } + + /* ========================= links ===================================== */ + + /** + * Make an internal link. + * + * @param rect the hotspot position in absolute coordinates + * @param page the target page reference value + * @param dest the position destination + * @return the new PDF link object + */ + public PDFLink makeLink(Rectangle2D rect, String page, String dest) { + PDFLink link = new PDFLink(rect); + getDocument().registerObject(link); + + PDFGoTo gt = new PDFGoTo(page); + gt.setDestination(dest); + getDocument().addTrailerObject(gt); + PDFInternalLink internalLink = new PDFInternalLink(gt.referencePDF()); + link.setAction(internalLink); + + return link; + } + + /** + * make a link object + * + * @param rect the clickable rectangle + * @param destination the destination file + * @param linkType the link type + * @param yoffset the yoffset on the page for an internal link + * @return the PDFLink object created + */ + public PDFLink makeLink(Rectangle2D rect, String destination, + int linkType, float yoffset) { + + //PDFLink linkObject; + int index; + + PDFLink link = new PDFLink(rect); + + if (linkType == PDFLink.EXTERNAL) { + // check destination + if (destination.startsWith("http://")) { + PDFUri uri = new PDFUri(destination); + link.setAction(uri); + } else if (destination.endsWith(".pdf")) { // FileSpec + PDFGoToRemote remote = getGoToPDFAction(destination, null, -1); + link.setAction(remote); + } else if ((index = destination.indexOf(".pdf#page=")) > 0) { + //String file = destination.substring(0, index + 4); + int page = Integer.parseInt(destination.substring(index + 10)); + PDFGoToRemote remote = getGoToPDFAction(destination, null, page); + link.setAction(remote); + } else if ((index = destination.indexOf(".pdf#dest=")) > 0) { + //String file = destination.substring(0, index + 4); + String dest = destination.substring(index + 10); + PDFGoToRemote remote = getGoToPDFAction(destination, dest, -1); + link.setAction(remote); + } else { // URI + PDFUri uri = new PDFUri(destination); + link.setAction(uri); + } + } else { + // linkType is internal + String goToReference = getGoToReference(destination, yoffset); + PDFInternalLink internalLink = new PDFInternalLink(goToReference); + link.setAction(internalLink); + } + + PDFLink oldlink = getDocument().findLink(link); + if (oldlink == null) { + getDocument().registerObject(link); + } else { + link = oldlink; + } + + return link; + } + + private String getGoToReference(String destination, float yoffset) { + String goToReference = null; + PDFGoTo gt = new PDFGoTo(destination); + gt.setYPosition(yoffset); + PDFGoTo oldgt = getDocument().findGoTo(gt); + if (oldgt == null) { + getDocument().assignObjectNumber(gt); + getDocument().addTrailerObject(gt); + } else { + gt = oldgt; + } + + goToReference = gt.referencePDF(); + return goToReference; + } + + /** + * Create and return a goto pdf document action. + * This creates a pdf files spec and pdf goto remote action. + * It also checks available pdf objects so it will not create an + * object if it already exists. + * + * @param file the pdf file name + * @param dest the remote name destination, may be null + * @param page the remote page number, -1 means not specified + * @return the pdf goto remote object + */ + private PDFGoToRemote getGoToPDFAction(String file, String dest, int page) { + PDFFileSpec fileSpec = new PDFFileSpec(file); + PDFFileSpec oldspec = getDocument().findFileSpec(fileSpec); + if (oldspec == null) { + getDocument().registerObject(fileSpec); + } else { + fileSpec = oldspec; + } + PDFGoToRemote remote; + + if (dest == null && page == -1) { + remote = new PDFGoToRemote(fileSpec); + } else if (dest != null) { + remote = new PDFGoToRemote(fileSpec, dest); + } else { + remote = new PDFGoToRemote(fileSpec, page); + } + PDFGoToRemote oldremote = getDocument().findGoToRemote(remote); + if (oldremote == null) { + getDocument().registerObject(remote); + } else { + remote = oldremote; + } + return remote; + } + + /** + * Make an outline object and add it to the given outline + * + * @param parent parent PDFOutline object which may be null + * @param label the title for the new outline object + * @param destination the reference string for the action to go to + * @param yoffset the yoffset on the destination page + * @return the new PDF outline object + */ + public PDFOutline makeOutline(PDFOutline parent, String label, + String destination, float yoffset) { + String goToRef = getGoToReference(destination, yoffset); + + PDFOutline obj = new PDFOutline(label, goToRef); + + if (parent != null) { + parent.addOutline(obj); + } + getDocument().registerObject(obj); + return obj; + } + + + + /* ========================= fonts ===================================== */ + + /** + * make a /Encoding object + * + * @param encodingName character encoding scheme name + * @return the created /Encoding object + */ + public PDFEncoding makeEncoding(String encodingName) { + PDFEncoding encoding = new PDFEncoding(encodingName); + + getDocument().registerObject(encoding); + return encoding; + } + + /** + * make a Type1 /Font object + * + * @param fontname internal name to use for this font (eg "F1") + * @param basefont name of the base font (eg "Helvetica") + * @param encoding character encoding scheme used by the font + * @param metrics additional information about the font + * @param descriptor additional information about the font + * @return the created /Font object + */ + public PDFFont makeFont(String fontname, String basefont, + String encoding, FontMetrics metrics, + FontDescriptor descriptor) { + PDFFont preRegisteredfont = getDocument().findFont(fontname); + if (preRegisteredfont != null) { + return preRegisteredfont; + } + + if (descriptor == null) { + PDFFont font = new PDFFont(fontname, FontType.TYPE1, basefont, encoding); + getDocument().registerObject(font); + return font; + } else { + FontType fonttype = metrics.getFontType(); + + PDFFontDescriptor pdfdesc = makeFontDescriptor(descriptor); + + PDFFontNonBase14 font = null; + if (fonttype == FontType.TYPE0) { + /* + * Temporary commented out - customized CMaps + * isn't needed until /ToUnicode support is added + * PDFCMap cmap = new PDFCMap(++this.objectcount, + * "fop-ucs-H", + * new PDFCIDSystemInfo("Adobe", + * "Identity", + * 0)); + * cmap.addContents(); + * this.objects.add(cmap); + */ + font = + (PDFFontNonBase14)PDFFont.createFont(fontname, fonttype, + basefont, + "Identity-H"); + } else { + + font = + (PDFFontNonBase14)PDFFont.createFont(fontname, fonttype, + basefont, encoding); + } + getDocument().registerObject(font); + + font.setDescriptor(pdfdesc); + + if (fonttype == FontType.TYPE0) { + CIDFont cidMetrics; + if (metrics instanceof LazyFont) { + cidMetrics = (CIDFont)((LazyFont) metrics).getRealFont(); + } else { + cidMetrics = (CIDFont)metrics; + } + PDFCIDSystemInfo sysInfo = + new PDFCIDSystemInfo(cidMetrics.getRegistry(), + cidMetrics.getOrdering(), + cidMetrics.getSupplement()); + PDFCIDFont cidFont = + new PDFCIDFont(basefont, + cidMetrics.getCIDType(), + cidMetrics.getDefaultWidth(), + cidMetrics.getWidths(), sysInfo, + (PDFCIDFontDescriptor)pdfdesc); + getDocument().registerObject(cidFont); + + ((PDFFontType0)font).setDescendantFonts(cidFont); + } else { + int firstChar = 0; + int lastChar = 255; + if (metrics instanceof CustomFont) { + CustomFont cf = (CustomFont)metrics; + firstChar = cf.getFirstChar(); + lastChar = cf.getLastChar(); + } + font.setWidthMetrics(firstChar, + lastChar, + makeArray(metrics.getWidths())); + } + + return font; + } + } + + + /** + * make a /FontDescriptor object + * + * @param desc the font descriptor + * @return the new PDF font descriptor + */ + public PDFFontDescriptor makeFontDescriptor(FontDescriptor desc) { + PDFFontDescriptor descriptor = null; + + if (desc.getFontType() == FontType.TYPE0) { + // CID Font + descriptor = new PDFCIDFontDescriptor(desc.getFontName(), + desc.getFontBBox(), + desc.getCapHeight(), + desc.getFlags(), + desc.getItalicAngle(), + desc.getStemV(), null); + } else { + // Create normal FontDescriptor + descriptor = new PDFFontDescriptor(desc.getFontName(), + desc.getAscender(), + desc.getDescender(), + desc.getCapHeight(), + desc.getFlags(), + new PDFRectangle(desc.getFontBBox()), + desc.getStemV(), + desc.getItalicAngle()); + } + getDocument().registerObject(descriptor); + + // Check if the font is embeddable + if (desc.isEmbeddable()) { + PDFStream stream = makeFontFile(desc); + if (stream != null) { + descriptor.setFontFile(desc.getFontType(), stream); + getDocument().registerObject(stream); + } + } + return descriptor; + } + + /** + * Embeds a font. + * @param desc FontDescriptor of the font. + * @return PDFStream The embedded font file + */ + public PDFStream makeFontFile(FontDescriptor desc) { + if (desc.getFontType() == FontType.OTHER) { + throw new IllegalArgumentException("Trying to embed unsupported font type: " + + desc.getFontType()); + } + + Font tempFont; + if (desc instanceof LazyFont) { + tempFont = ((LazyFont)desc).getRealFont(); + } else { + tempFont = (Font)desc; + } + if (!(tempFont instanceof CustomFont)) { + throw new IllegalArgumentException( + "FontDescriptor must be instance of CustomFont, but is a " + + desc.getClass().getName()); + } + CustomFont font = (CustomFont)tempFont; + + InputStream in = null; + try { + // Get file first + if (font.getEmbedFileName() != null) { + try { + in = getDocument().resolveURI(font.getEmbedFileName()); + } catch (Exception e) { + getDocument().getLogger().error("Failed to embed fontfile: " + + font.getEmbedFileName() + + "(" + e.getMessage() + ")"); + } + } + + // Get resource + if (in == null && font.getEmbedResourceName() != null) { + try { + in = new java.io.BufferedInputStream( + this.getClass().getResourceAsStream( + font.getEmbedResourceName())); + } catch (Exception e) { + getDocument().getLogger().error( + "Failed to embed fontresource: " + + font.getEmbedResourceName() + + "(" + e.getMessage() + ")"); + } + } + + if (in == null) { + return null; + } else { + try { + PDFStream embeddedFont; + if (desc.getFontType() == FontType.TYPE0) { + MultiByteFont mbfont = (MultiByteFont)font; + FontFileReader reader = new FontFileReader(in); + + TTFSubSetFile subset = new TTFSubSetFile(); + ContainerUtil.enableLogging(subset, + getDocument().getLogger().getChildLogger("fonts")); + + byte[] subsetFont = subset.readFont(reader, + mbfont.getTTCName(), mbfont.getUsedGlyphs()); + // Only TrueType CID fonts are supported now + + embeddedFont = new PDFTTFStream(subsetFont.length); + ((PDFTTFStream)embeddedFont).setData(subsetFont, subsetFont.length); + } else if (desc.getFontType() == FontType.TYPE1) { + PFBParser parser = new PFBParser(); + PFBData pfb = parser.parsePFB(in); + embeddedFont = new PDFT1Stream(); + ((PDFT1Stream)embeddedFont).setData(pfb); + } else { + byte[] file = StreamUtilities.toByteArray(in, 128000); + embeddedFont = new PDFTTFStream(file.length); + ((PDFTTFStream)embeddedFont).setData(file, file.length); + } + + /* + embeddedFont.getFilterList().addFilter("flate"); + if (getDocument().isEncryptionActive()) { + getDocument().applyEncryption(embeddedFont); + } else { + embeddedFont.getFilterList().addFilter("ascii-85"); + }*/ + + return embeddedFont; + } finally { + in.close(); + } + } + } catch (IOException ioe) { + getDocument().getLogger().error( + "Failed to embed font [" + desc + "] " + + desc.getFontName(), ioe); + return (PDFStream) null; + } + } + + + /* ========================= streams =================================== */ + + /** + * Make a stream object + * + * @param type the type of stream to be created + * @param add if true then the stream will be added immediately + * @return the stream object created + */ + public PDFStream makeStream(String type, boolean add) { + + // create a PDFStream with the next object number + // and add it to the list of objects + PDFStream obj = new PDFStream(); + obj.setDocument(getDocument()); + obj.getFilterList().addDefaultFilters( + getDocument().getFilterMap(), + type); + + if (add) { + getDocument().registerObject(obj); + } + //getDocument().applyEncryption(obj); + return obj; + } + + /** + * Create a PDFICCStream + * @see PDFXObject + * @see org.apache.fop.image.JpegImage + * @see org.apache.fop.pdf.PDFColorSpace + * @return the new PDF ICC stream object + */ + public PDFICCStream makePDFICCStream() { + PDFICCStream iccStream = new PDFICCStream(); + iccStream.getFilterList().addDefaultFilters( + getDocument().getFilterMap(), + PDFFilterList.CONTENT_FILTER); + + getDocument().registerObject(iccStream); + //getDocument().applyEncryption(iccStream); + return iccStream; + } + + /* ========================= misc. objects ============================= */ + + /** + * make an Array object (ex. Widths array for a font) + * + * @param values the int array values + * @return the PDF Array with the int values + */ + public PDFArray makeArray(int[] values) { + PDFArray array = new PDFArray(values); + + getDocument().registerObject(array); + return array; + } + + /** + * make an ExtGState for extra graphics options + * This tries to find a GState that will setup the correct values + * for the current context. If there is no suitable GState it will + * create a new one. + * + * @param settings the settings required by the caller + * @param current the current GState of the current PDF context + * @return a PDF GState, either an existing GState or a new one + */ + public PDFGState makeGState(Map settings, PDFGState current) { + + // try to locate a gstate that has all the settings + // or will inherit from the current gstate + // compare "DEFAULT + settings" with "current + each gstate" + + PDFGState wanted = new PDFGState(); + wanted.addValues(PDFGState.DEFAULT); + wanted.addValues(settings); + + + PDFGState existing = getDocument().findGState(wanted, current); + if (existing != null) { + return existing; + } + + PDFGState gstate = new PDFGState(); + gstate.addValues(settings); + getDocument().registerObject(gstate); + return gstate; + } + + /** + * Make an annotation list object + * + * @return the annotation list object created + */ + public PDFAnnotList makeAnnotList() { + PDFAnnotList obj = new PDFAnnotList(); + getDocument().assignObjectNumber(obj); + return obj; + } + + + +} -- 2.39.5