0001: /*
0002: * $Id: PdfContentByte.java 2752 2007-05-15 14:58:33Z blowagie $
0003: * $Name$
0004: *
0005: * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
0006: *
0007: * The contents of this file are subject to the Mozilla Public License Version 1.1
0008: * (the "License"); you may not use this file except in compliance with the License.
0009: * You may obtain a copy of the License at http://www.mozilla.org/MPL/
0010: *
0011: * Software distributed under the License is distributed on an "AS IS" basis,
0012: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
0013: * for the specific language governing rights and limitations under the License.
0014: *
0015: * The Original Code is 'iText, a free JAVA-PDF library'.
0016: *
0017: * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
0018: * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
0019: * All Rights Reserved.
0020: * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
0021: * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
0022: *
0023: * Contributor(s): all the names of the contributors are added in the source code
0024: * where applicable.
0025: *
0026: * Alternatively, the contents of this file may be used under the terms of the
0027: * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
0028: * provisions of LGPL are applicable instead of those above. If you wish to
0029: * allow use of your version of this file only under the terms of the LGPL
0030: * License and not to allow others to use your version of this file under
0031: * the MPL, indicate your decision by deleting the provisions above and
0032: * replace them with the notice and other provisions required by the LGPL.
0033: * If you do not delete the provisions above, a recipient may use your version
0034: * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
0035: *
0036: * This library is free software; you can redistribute it and/or modify it
0037: * under the terms of the MPL as stated above or under the terms of the GNU
0038: * Library General Public License as published by the Free Software Foundation;
0039: * either version 2 of the License, or any later version.
0040: *
0041: * This library is distributed in the hope that it will be useful, but WITHOUT
0042: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
0043: * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
0044: * details.
0045: *
0046: * If you didn't download this code from the following link, you should check if
0047: * you aren't using an obsolete version:
0048: * http://www.lowagie.com/iText/
0049: */
0050:
0051: package com.lowagie.text.pdf;
0052:
0053: import java.awt.Color;
0054: import java.awt.geom.AffineTransform;
0055: import java.awt.print.PrinterJob;
0056: import java.util.ArrayList;
0057: import java.util.HashMap;
0058: import java.util.Iterator;
0059:
0060: import com.lowagie.text.Annotation;
0061: import com.lowagie.text.DocumentException;
0062: import com.lowagie.text.Element;
0063: import com.lowagie.text.ExceptionConverter;
0064: import com.lowagie.text.Image;
0065: import com.lowagie.text.Rectangle;
0066: import com.lowagie.text.pdf.internal.PdfAnnotationsImp;
0067: import com.lowagie.text.pdf.internal.PdfXConformanceImp;
0068:
0069: /**
0070: * <CODE>PdfContentByte</CODE> is an object containing the user positioned
0071: * text and graphic contents of a page. It knows how to apply the proper
0072: * font encoding.
0073: */
0074:
0075: public class PdfContentByte {
0076:
0077: /**
0078: * This class keeps the graphic state of the current page
0079: */
0080:
0081: static class GraphicState {
0082:
0083: /** This is the font in use */
0084: FontDetails fontDetails;
0085:
0086: /** This is the color in use */
0087: ColorDetails colorDetails;
0088:
0089: /** This is the font size in use */
0090: float size;
0091:
0092: /** The x position of the text line matrix. */
0093: protected float xTLM = 0;
0094: /** The y position of the text line matrix. */
0095: protected float yTLM = 0;
0096:
0097: /** The current text leading. */
0098: protected float leading = 0;
0099:
0100: /** The current horizontal scaling */
0101: protected float scale = 100;
0102:
0103: /** The current character spacing */
0104: protected float charSpace = 0;
0105:
0106: /** The current word spacing */
0107: protected float wordSpace = 0;
0108:
0109: GraphicState() {
0110: }
0111:
0112: GraphicState(GraphicState cp) {
0113: fontDetails = cp.fontDetails;
0114: colorDetails = cp.colorDetails;
0115: size = cp.size;
0116: xTLM = cp.xTLM;
0117: yTLM = cp.yTLM;
0118: leading = cp.leading;
0119: scale = cp.scale;
0120: charSpace = cp.charSpace;
0121: wordSpace = cp.wordSpace;
0122: }
0123: }
0124:
0125: /** The alignement is center */
0126: public static final int ALIGN_CENTER = Element.ALIGN_CENTER;
0127:
0128: /** The alignement is left */
0129: public static final int ALIGN_LEFT = Element.ALIGN_LEFT;
0130:
0131: /** The alignement is right */
0132: public static final int ALIGN_RIGHT = Element.ALIGN_RIGHT;
0133:
0134: /** A possible line cap value */
0135: public static final int LINE_CAP_BUTT = 0;
0136: /** A possible line cap value */
0137: public static final int LINE_CAP_ROUND = 1;
0138: /** A possible line cap value */
0139: public static final int LINE_CAP_PROJECTING_SQUARE = 2;
0140:
0141: /** A possible line join value */
0142: public static final int LINE_JOIN_MITER = 0;
0143: /** A possible line join value */
0144: public static final int LINE_JOIN_ROUND = 1;
0145: /** A possible line join value */
0146: public static final int LINE_JOIN_BEVEL = 2;
0147:
0148: /** A possible text rendering value */
0149: public static final int TEXT_RENDER_MODE_FILL = 0;
0150: /** A possible text rendering value */
0151: public static final int TEXT_RENDER_MODE_STROKE = 1;
0152: /** A possible text rendering value */
0153: public static final int TEXT_RENDER_MODE_FILL_STROKE = 2;
0154: /** A possible text rendering value */
0155: public static final int TEXT_RENDER_MODE_INVISIBLE = 3;
0156: /** A possible text rendering value */
0157: public static final int TEXT_RENDER_MODE_FILL_CLIP = 4;
0158: /** A possible text rendering value */
0159: public static final int TEXT_RENDER_MODE_STROKE_CLIP = 5;
0160: /** A possible text rendering value */
0161: public static final int TEXT_RENDER_MODE_FILL_STROKE_CLIP = 6;
0162: /** A possible text rendering value */
0163: public static final int TEXT_RENDER_MODE_CLIP = 7;
0164:
0165: private static final float[] unitRect = { 0, 0, 0, 1, 1, 0, 1, 1 };
0166: // membervariables
0167:
0168: /** This is the actual content */
0169: protected ByteBuffer content = new ByteBuffer();
0170:
0171: /** This is the writer */
0172: protected PdfWriter writer;
0173:
0174: /** This is the PdfDocument */
0175: protected PdfDocument pdf;
0176:
0177: /** This is the GraphicState in use */
0178: protected GraphicState state = new GraphicState();
0179:
0180: /** The list were we save/restore the state */
0181: protected ArrayList stateList = new ArrayList();
0182:
0183: /** The list were we save/restore the layer depth */
0184: protected ArrayList layerDepth;
0185:
0186: /** The separator between commands.
0187: */
0188: protected int separator = '\n';
0189:
0190: private static HashMap abrev = new HashMap();
0191:
0192: static {
0193: abrev.put(PdfName.BITSPERCOMPONENT, "/BPC ");
0194: abrev.put(PdfName.COLORSPACE, "/CS ");
0195: abrev.put(PdfName.DECODE, "/D ");
0196: abrev.put(PdfName.DECODEPARMS, "/DP ");
0197: abrev.put(PdfName.FILTER, "/F ");
0198: abrev.put(PdfName.HEIGHT, "/H ");
0199: abrev.put(PdfName.IMAGEMASK, "/IM ");
0200: abrev.put(PdfName.INTENT, "/Intent ");
0201: abrev.put(PdfName.INTERPOLATE, "/I ");
0202: abrev.put(PdfName.WIDTH, "/W ");
0203: }
0204:
0205: // constructors
0206:
0207: /**
0208: * Constructs a new <CODE>PdfContentByte</CODE>-object.
0209: *
0210: * @param wr the writer associated to this content
0211: */
0212:
0213: public PdfContentByte(PdfWriter wr) {
0214: if (wr != null) {
0215: writer = wr;
0216: pdf = writer.getPdfDocument();
0217: }
0218: }
0219:
0220: // methods to get the content of this object
0221:
0222: /**
0223: * Returns the <CODE>String</CODE> representation of this <CODE>PdfContentByte</CODE>-object.
0224: *
0225: * @return a <CODE>String</CODE>
0226: */
0227:
0228: public String toString() {
0229: return content.toString();
0230: }
0231:
0232: /**
0233: * Gets the internal buffer.
0234: * @return the internal buffer
0235: */
0236: public ByteBuffer getInternalBuffer() {
0237: return content;
0238: }
0239:
0240: /** Returns the PDF representation of this <CODE>PdfContentByte</CODE>-object.
0241: *
0242: * @param writer the <CODE>PdfWriter</CODE>
0243: * @return a <CODE>byte</CODE> array with the representation
0244: */
0245:
0246: public byte[] toPdf(PdfWriter writer) {
0247: return content.toByteArray();
0248: }
0249:
0250: // methods to add graphical content
0251:
0252: /**
0253: * Adds the content of another <CODE>PdfContent</CODE>-object to this object.
0254: *
0255: * @param other another <CODE>PdfByteContent</CODE>-object
0256: */
0257:
0258: public void add(PdfContentByte other) {
0259: if (other.writer != null && writer != other.writer)
0260: throw new RuntimeException(
0261: "Inconsistent writers. Are you mixing two documents?");
0262: content.append(other.content);
0263: }
0264:
0265: /**
0266: * Gets the x position of the text line matrix.
0267: *
0268: * @return the x position of the text line matrix
0269: */
0270: public float getXTLM() {
0271: return state.xTLM;
0272: }
0273:
0274: /**
0275: * Gets the y position of the text line matrix.
0276: *
0277: * @return the y position of the text line matrix
0278: */
0279: public float getYTLM() {
0280: return state.yTLM;
0281: }
0282:
0283: /**
0284: * Gets the current text leading.
0285: *
0286: * @return the current text leading
0287: */
0288: public float getLeading() {
0289: return state.leading;
0290: }
0291:
0292: /**
0293: * Gets the current character spacing.
0294: *
0295: * @return the current character spacing
0296: */
0297: public float getCharacterSpacing() {
0298: return state.charSpace;
0299: }
0300:
0301: /**
0302: * Gets the current word spacing.
0303: *
0304: * @return the current word spacing
0305: */
0306: public float getWordSpacing() {
0307: return state.wordSpace;
0308: }
0309:
0310: /**
0311: * Gets the current character spacing.
0312: *
0313: * @return the current character spacing
0314: */
0315: public float getHorizontalScaling() {
0316: return state.scale;
0317: }
0318:
0319: /**
0320: * Changes the <VAR>Flatness</VAR>.
0321: * <P>
0322: * <VAR>Flatness</VAR> sets the maximum permitted distance in device pixels between the
0323: * mathematically correct path and an approximation constructed from straight line segments.<BR>
0324: *
0325: * @param flatness a value
0326: */
0327:
0328: public void setFlatness(float flatness) {
0329: if (flatness >= 0 && flatness <= 100) {
0330: content.append(flatness).append(" i").append_i(separator);
0331: }
0332: }
0333:
0334: /**
0335: * Changes the <VAR>Line cap style</VAR>.
0336: * <P>
0337: * The <VAR>line cap style</VAR> specifies the shape to be used at the end of open subpaths
0338: * when they are stroked.<BR>
0339: * Allowed values are LINE_CAP_BUTT, LINE_CAP_ROUND and LINE_CAP_PROJECTING_SQUARE.<BR>
0340: *
0341: * @param style a value
0342: */
0343:
0344: public void setLineCap(int style) {
0345: if (style >= 0 && style <= 2) {
0346: content.append(style).append(" J").append_i(separator);
0347: }
0348: }
0349:
0350: /**
0351: * Changes the value of the <VAR>line dash pattern</VAR>.
0352: * <P>
0353: * The line dash pattern controls the pattern of dashes and gaps used to stroke paths.
0354: * It is specified by an <I>array</I> and a <I>phase</I>. The array specifies the length
0355: * of the alternating dashes and gaps. The phase specifies the distance into the dash
0356: * pattern to start the dash.<BR>
0357: *
0358: * @param phase the value of the phase
0359: */
0360:
0361: public void setLineDash(float phase) {
0362: content.append("[] ").append(phase).append(" d").append_i(
0363: separator);
0364: }
0365:
0366: /**
0367: * Changes the value of the <VAR>line dash pattern</VAR>.
0368: * <P>
0369: * The line dash pattern controls the pattern of dashes and gaps used to stroke paths.
0370: * It is specified by an <I>array</I> and a <I>phase</I>. The array specifies the length
0371: * of the alternating dashes and gaps. The phase specifies the distance into the dash
0372: * pattern to start the dash.<BR>
0373: *
0374: * @param phase the value of the phase
0375: * @param unitsOn the number of units that must be 'on' (equals the number of units that must be 'off').
0376: */
0377:
0378: public void setLineDash(float unitsOn, float phase) {
0379: content.append("[").append(unitsOn).append("] ").append(phase)
0380: .append(" d").append_i(separator);
0381: }
0382:
0383: /**
0384: * Changes the value of the <VAR>line dash pattern</VAR>.
0385: * <P>
0386: * The line dash pattern controls the pattern of dashes and gaps used to stroke paths.
0387: * It is specified by an <I>array</I> and a <I>phase</I>. The array specifies the length
0388: * of the alternating dashes and gaps. The phase specifies the distance into the dash
0389: * pattern to start the dash.<BR>
0390: *
0391: * @param phase the value of the phase
0392: * @param unitsOn the number of units that must be 'on'
0393: * @param unitsOff the number of units that must be 'off'
0394: */
0395:
0396: public void setLineDash(float unitsOn, float unitsOff, float phase) {
0397: content.append("[").append(unitsOn).append(' ')
0398: .append(unitsOff).append("] ").append(phase).append(
0399: " d").append_i(separator);
0400: }
0401:
0402: /**
0403: * Changes the value of the <VAR>line dash pattern</VAR>.
0404: * <P>
0405: * The line dash pattern controls the pattern of dashes and gaps used to stroke paths.
0406: * It is specified by an <I>array</I> and a <I>phase</I>. The array specifies the length
0407: * of the alternating dashes and gaps. The phase specifies the distance into the dash
0408: * pattern to start the dash.<BR>
0409: *
0410: * @param array length of the alternating dashes and gaps
0411: * @param phase the value of the phase
0412: */
0413:
0414: public final void setLineDash(float[] array, float phase) {
0415: content.append("[");
0416: for (int i = 0; i < array.length; i++) {
0417: content.append(array[i]);
0418: if (i < array.length - 1)
0419: content.append(' ');
0420: }
0421: content.append("] ").append(phase).append(" d").append_i(
0422: separator);
0423: }
0424:
0425: /**
0426: * Changes the <VAR>Line join style</VAR>.
0427: * <P>
0428: * The <VAR>line join style</VAR> specifies the shape to be used at the corners of paths
0429: * that are stroked.<BR>
0430: * Allowed values are LINE_JOIN_MITER (Miter joins), LINE_JOIN_ROUND (Round joins) and LINE_JOIN_BEVEL (Bevel joins).<BR>
0431: *
0432: * @param style a value
0433: */
0434:
0435: public void setLineJoin(int style) {
0436: if (style >= 0 && style <= 2) {
0437: content.append(style).append(" j").append_i(separator);
0438: }
0439: }
0440:
0441: /**
0442: * Changes the <VAR>line width</VAR>.
0443: * <P>
0444: * The line width specifies the thickness of the line used to stroke a path and is measured
0445: * in user space units.<BR>
0446: *
0447: * @param w a width
0448: */
0449:
0450: public void setLineWidth(float w) {
0451: content.append(w).append(" w").append_i(separator);
0452: }
0453:
0454: /**
0455: * Changes the <VAR>Miter limit</VAR>.
0456: * <P>
0457: * When two line segments meet at a sharp angle and mitered joins have been specified as the
0458: * line join style, it is possible for the miter to extend far beyond the thickness of the line
0459: * stroking path. The miter limit imposes a maximum on the ratio of the miter length to the line
0460: * witdh. When the limit is exceeded, the join is converted from a miter to a bevel.<BR>
0461: *
0462: * @param miterLimit a miter limit
0463: */
0464:
0465: public void setMiterLimit(float miterLimit) {
0466: if (miterLimit > 1) {
0467: content.append(miterLimit).append(" M").append_i(separator);
0468: }
0469: }
0470:
0471: /**
0472: * Modify the current clipping path by intersecting it with the current path, using the
0473: * nonzero winding number rule to determine which regions lie inside the clipping
0474: * path.
0475: */
0476:
0477: public void clip() {
0478: content.append("W").append_i(separator);
0479: }
0480:
0481: /**
0482: * Modify the current clipping path by intersecting it with the current path, using the
0483: * even-odd rule to determine which regions lie inside the clipping path.
0484: */
0485:
0486: public void eoClip() {
0487: content.append("W*").append_i(separator);
0488: }
0489:
0490: /**
0491: * Changes the currentgray tint for filling paths (device dependent colors!).
0492: * <P>
0493: * Sets the color space to <B>DeviceGray</B> (or the <B>DefaultGray</B> color space),
0494: * and sets the gray tint to use for filling paths.</P>
0495: *
0496: * @param gray a value between 0 (black) and 1 (white)
0497: */
0498:
0499: public void setGrayFill(float gray) {
0500: content.append(gray).append(" g").append_i(separator);
0501: }
0502:
0503: /**
0504: * Changes the current gray tint for filling paths to black.
0505: */
0506:
0507: public void resetGrayFill() {
0508: content.append("0 g").append_i(separator);
0509: }
0510:
0511: /**
0512: * Changes the currentgray tint for stroking paths (device dependent colors!).
0513: * <P>
0514: * Sets the color space to <B>DeviceGray</B> (or the <B>DefaultGray</B> color space),
0515: * and sets the gray tint to use for stroking paths.</P>
0516: *
0517: * @param gray a value between 0 (black) and 1 (white)
0518: */
0519:
0520: public void setGrayStroke(float gray) {
0521: content.append(gray).append(" G").append_i(separator);
0522: }
0523:
0524: /**
0525: * Changes the current gray tint for stroking paths to black.
0526: */
0527:
0528: public void resetGrayStroke() {
0529: content.append("0 G").append_i(separator);
0530: }
0531:
0532: /**
0533: * Helper to validate and write the RGB color components
0534: * @param red the intensity of red. A value between 0 and 1
0535: * @param green the intensity of green. A value between 0 and 1
0536: * @param blue the intensity of blue. A value between 0 and 1
0537: */
0538: private void HelperRGB(float red, float green, float blue) {
0539: PdfXConformanceImp.checkPDFXConformance(writer,
0540: PdfXConformanceImp.PDFXKEY_RGB, null);
0541: if (red < 0)
0542: red = 0.0f;
0543: else if (red > 1.0f)
0544: red = 1.0f;
0545: if (green < 0)
0546: green = 0.0f;
0547: else if (green > 1.0f)
0548: green = 1.0f;
0549: if (blue < 0)
0550: blue = 0.0f;
0551: else if (blue > 1.0f)
0552: blue = 1.0f;
0553: content.append(red).append(' ').append(green).append(' ')
0554: .append(blue);
0555: }
0556:
0557: /**
0558: * Changes the current color for filling paths (device dependent colors!).
0559: * <P>
0560: * Sets the color space to <B>DeviceRGB</B> (or the <B>DefaultRGB</B> color space),
0561: * and sets the color to use for filling paths.</P>
0562: * <P>
0563: * Following the PDF manual, each operand must be a number between 0 (minimum intensity) and
0564: * 1 (maximum intensity).</P>
0565: *
0566: * @param red the intensity of red. A value between 0 and 1
0567: * @param green the intensity of green. A value between 0 and 1
0568: * @param blue the intensity of blue. A value between 0 and 1
0569: */
0570:
0571: public void setRGBColorFillF(float red, float green, float blue) {
0572: HelperRGB(red, green, blue);
0573: content.append(" rg").append_i(separator);
0574: }
0575:
0576: /**
0577: * Changes the current color for filling paths to black.
0578: */
0579:
0580: public void resetRGBColorFill() {
0581: content.append("0 g").append_i(separator);
0582: }
0583:
0584: /**
0585: * Changes the current color for stroking paths (device dependent colors!).
0586: * <P>
0587: * Sets the color space to <B>DeviceRGB</B> (or the <B>DefaultRGB</B> color space),
0588: * and sets the color to use for stroking paths.</P>
0589: * <P>
0590: * Following the PDF manual, each operand must be a number between 0 (miniumum intensity) and
0591: * 1 (maximum intensity).
0592: *
0593: * @param red the intensity of red. A value between 0 and 1
0594: * @param green the intensity of green. A value between 0 and 1
0595: * @param blue the intensity of blue. A value between 0 and 1
0596: */
0597:
0598: public void setRGBColorStrokeF(float red, float green, float blue) {
0599: HelperRGB(red, green, blue);
0600: content.append(" RG").append_i(separator);
0601: }
0602:
0603: /**
0604: * Changes the current color for stroking paths to black.
0605: *
0606: */
0607:
0608: public void resetRGBColorStroke() {
0609: content.append("0 G").append_i(separator);
0610: }
0611:
0612: /**
0613: * Helper to validate and write the CMYK color components.
0614: *
0615: * @param cyan the intensity of cyan. A value between 0 and 1
0616: * @param magenta the intensity of magenta. A value between 0 and 1
0617: * @param yellow the intensity of yellow. A value between 0 and 1
0618: * @param black the intensity of black. A value between 0 and 1
0619: */
0620: private void HelperCMYK(float cyan, float magenta, float yellow,
0621: float black) {
0622: if (cyan < 0)
0623: cyan = 0.0f;
0624: else if (cyan > 1.0f)
0625: cyan = 1.0f;
0626: if (magenta < 0)
0627: magenta = 0.0f;
0628: else if (magenta > 1.0f)
0629: magenta = 1.0f;
0630: if (yellow < 0)
0631: yellow = 0.0f;
0632: else if (yellow > 1.0f)
0633: yellow = 1.0f;
0634: if (black < 0)
0635: black = 0.0f;
0636: else if (black > 1.0f)
0637: black = 1.0f;
0638: content.append(cyan).append(' ').append(magenta).append(' ')
0639: .append(yellow).append(' ').append(black);
0640: }
0641:
0642: /**
0643: * Changes the current color for filling paths (device dependent colors!).
0644: * <P>
0645: * Sets the color space to <B>DeviceCMYK</B> (or the <B>DefaultCMYK</B> color space),
0646: * and sets the color to use for filling paths.</P>
0647: * <P>
0648: * Following the PDF manual, each operand must be a number between 0 (no ink) and
0649: * 1 (maximum ink).</P>
0650: *
0651: * @param cyan the intensity of cyan. A value between 0 and 1
0652: * @param magenta the intensity of magenta. A value between 0 and 1
0653: * @param yellow the intensity of yellow. A value between 0 and 1
0654: * @param black the intensity of black. A value between 0 and 1
0655: */
0656:
0657: public void setCMYKColorFillF(float cyan, float magenta,
0658: float yellow, float black) {
0659: HelperCMYK(cyan, magenta, yellow, black);
0660: content.append(" k").append_i(separator);
0661: }
0662:
0663: /**
0664: * Changes the current color for filling paths to black.
0665: *
0666: */
0667:
0668: public void resetCMYKColorFill() {
0669: content.append("0 0 0 1 k").append_i(separator);
0670: }
0671:
0672: /**
0673: * Changes the current color for stroking paths (device dependent colors!).
0674: * <P>
0675: * Sets the color space to <B>DeviceCMYK</B> (or the <B>DefaultCMYK</B> color space),
0676: * and sets the color to use for stroking paths.</P>
0677: * <P>
0678: * Following the PDF manual, each operand must be a number between 0 (miniumum intensity) and
0679: * 1 (maximum intensity).
0680: *
0681: * @param cyan the intensity of cyan. A value between 0 and 1
0682: * @param magenta the intensity of magenta. A value between 0 and 1
0683: * @param yellow the intensity of yellow. A value between 0 and 1
0684: * @param black the intensity of black. A value between 0 and 1
0685: */
0686:
0687: public void setCMYKColorStrokeF(float cyan, float magenta,
0688: float yellow, float black) {
0689: HelperCMYK(cyan, magenta, yellow, black);
0690: content.append(" K").append_i(separator);
0691: }
0692:
0693: /**
0694: * Changes the current color for stroking paths to black.
0695: *
0696: */
0697:
0698: public void resetCMYKColorStroke() {
0699: content.append("0 0 0 1 K").append_i(separator);
0700: }
0701:
0702: /**
0703: * Move the current point <I>(x, y)</I>, omitting any connecting line segment.
0704: *
0705: * @param x new x-coordinate
0706: * @param y new y-coordinate
0707: */
0708:
0709: public void moveTo(float x, float y) {
0710: content.append(x).append(' ').append(y).append(" m").append_i(
0711: separator);
0712: }
0713:
0714: /**
0715: * Appends a straight line segment from the current point <I>(x, y)</I>. The new current
0716: * point is <I>(x, y)</I>.
0717: *
0718: * @param x new x-coordinate
0719: * @param y new y-coordinate
0720: */
0721:
0722: public void lineTo(float x, float y) {
0723: content.append(x).append(' ').append(y).append(" l").append_i(
0724: separator);
0725: }
0726:
0727: /**
0728: * Appends a Bêzier curve to the path, starting from the current point.
0729: *
0730: * @param x1 x-coordinate of the first control point
0731: * @param y1 y-coordinate of the first control point
0732: * @param x2 x-coordinate of the second control point
0733: * @param y2 y-coordinate of the second control point
0734: * @param x3 x-coordinaat of the ending point (= new current point)
0735: * @param y3 y-coordinaat of the ending point (= new current point)
0736: */
0737:
0738: public void curveTo(float x1, float y1, float x2, float y2,
0739: float x3, float y3) {
0740: content.append(x1).append(' ').append(y1).append(' ')
0741: .append(x2).append(' ').append(y2).append(' ').append(
0742: x3).append(' ').append(y3).append(" c")
0743: .append_i(separator);
0744: }
0745:
0746: /**
0747: * Appends a Bêzier curve to the path, starting from the current point.
0748: *
0749: * @param x2 x-coordinate of the second control point
0750: * @param y2 y-coordinate of the second control point
0751: * @param x3 x-coordinaat of the ending point (= new current point)
0752: * @param y3 y-coordinaat of the ending point (= new current point)
0753: */
0754:
0755: public void curveTo(float x2, float y2, float x3, float y3) {
0756: content.append(x2).append(' ').append(y2).append(' ')
0757: .append(x3).append(' ').append(y3).append(" v")
0758: .append_i(separator);
0759: }
0760:
0761: /**
0762: * Appends a Bêzier curve to the path, starting from the current point.
0763: *
0764: * @param x1 x-coordinate of the first control point
0765: * @param y1 y-coordinate of the first control point
0766: * @param x3 x-coordinaat of the ending point (= new current point)
0767: * @param y3 y-coordinaat of the ending point (= new current point)
0768: */
0769:
0770: public void curveFromTo(float x1, float y1, float x3, float y3) {
0771: content.append(x1).append(' ').append(y1).append(' ')
0772: .append(x3).append(' ').append(y3).append(" y")
0773: .append_i(separator);
0774: }
0775:
0776: /** Draws a circle. The endpoint will (x+r, y).
0777: *
0778: * @param x x center of circle
0779: * @param y y center of circle
0780: * @param r radius of circle
0781: */
0782: public void circle(float x, float y, float r) {
0783: float b = 0.5523f;
0784: moveTo(x + r, y);
0785: curveTo(x + r, y + r * b, x + r * b, y + r, x, y + r);
0786: curveTo(x - r * b, y + r, x - r, y + r * b, x - r, y);
0787: curveTo(x - r, y - r * b, x - r * b, y - r, x, y - r);
0788: curveTo(x + r * b, y - r, x + r, y - r * b, x + r, y);
0789: }
0790:
0791: /**
0792: * Adds a rectangle to the current path.
0793: *
0794: * @param x x-coordinate of the starting point
0795: * @param y y-coordinate of the starting point
0796: * @param w width
0797: * @param h height
0798: */
0799:
0800: public void rectangle(float x, float y, float w, float h) {
0801: content.append(x).append(' ').append(y).append(' ').append(w)
0802: .append(' ').append(h).append(" re")
0803: .append_i(separator);
0804: }
0805:
0806: private boolean compareColors(Color c1, Color c2) {
0807: if (c1 == null && c2 == null)
0808: return true;
0809: if (c1 == null || c2 == null)
0810: return false;
0811: if (c1 instanceof ExtendedColor)
0812: return c1.equals(c2);
0813: return c2.equals(c1);
0814: }
0815:
0816: /**
0817: * Adds a variable width border to the current path.
0818: * Only use if {@link com.lowagie.text.Rectangle#isUseVariableBorders() Rectangle.isUseVariableBorders}
0819: * = true.
0820: * @param rect a <CODE>Rectangle</CODE>
0821: */
0822: public void variableRectangle(Rectangle rect) {
0823: float t = rect.getTop();
0824: float b = rect.getBottom();
0825: float r = rect.getRight();
0826: float l = rect.getLeft();
0827: float wt = rect.getBorderWidthTop();
0828: float wb = rect.getBorderWidthBottom();
0829: float wr = rect.getBorderWidthRight();
0830: float wl = rect.getBorderWidthLeft();
0831: Color ct = rect.getBorderColorTop();
0832: Color cb = rect.getBorderColorBottom();
0833: Color cr = rect.getBorderColorRight();
0834: Color cl = rect.getBorderColorLeft();
0835: saveState();
0836: setLineCap(PdfContentByte.LINE_CAP_BUTT);
0837: setLineJoin(PdfContentByte.LINE_JOIN_MITER);
0838: float clw = 0;
0839: boolean cdef = false;
0840: Color ccol = null;
0841: boolean cdefi = false;
0842: Color cfil = null;
0843: // draw top
0844: if (wt > 0) {
0845: setLineWidth(clw = wt);
0846: cdef = true;
0847: if (ct == null)
0848: resetRGBColorStroke();
0849: else
0850: setColorStroke(ct);
0851: ccol = ct;
0852: moveTo(l, t - wt / 2f);
0853: lineTo(r, t - wt / 2f);
0854: stroke();
0855: }
0856:
0857: // Draw bottom
0858: if (wb > 0) {
0859: if (wb != clw)
0860: setLineWidth(clw = wb);
0861: if (!cdef || !compareColors(ccol, cb)) {
0862: cdef = true;
0863: if (cb == null)
0864: resetRGBColorStroke();
0865: else
0866: setColorStroke(cb);
0867: ccol = cb;
0868: }
0869: moveTo(r, b + wb / 2f);
0870: lineTo(l, b + wb / 2f);
0871: stroke();
0872: }
0873:
0874: // Draw right
0875: if (wr > 0) {
0876: if (wr != clw)
0877: setLineWidth(clw = wr);
0878: if (!cdef || !compareColors(ccol, cr)) {
0879: cdef = true;
0880: if (cr == null)
0881: resetRGBColorStroke();
0882: else
0883: setColorStroke(cr);
0884: ccol = cr;
0885: }
0886: boolean bt = compareColors(ct, cr);
0887: boolean bb = compareColors(cb, cr);
0888: moveTo(r - wr / 2f, bt ? t : t - wt);
0889: lineTo(r - wr / 2f, bb ? b : b + wb);
0890: stroke();
0891: if (!bt || !bb) {
0892: cdefi = true;
0893: if (cr == null)
0894: resetRGBColorFill();
0895: else
0896: setColorFill(cr);
0897: cfil = cr;
0898: if (!bt) {
0899: moveTo(r, t);
0900: lineTo(r, t - wt);
0901: lineTo(r - wr, t - wt);
0902: fill();
0903: }
0904: if (!bb) {
0905: moveTo(r, b);
0906: lineTo(r, b + wb);
0907: lineTo(r - wr, b + wb);
0908: fill();
0909: }
0910: }
0911: }
0912:
0913: // Draw Left
0914: if (wl > 0) {
0915: if (wl != clw)
0916: setLineWidth(wl);
0917: if (!cdef || !compareColors(ccol, cl)) {
0918: if (cl == null)
0919: resetRGBColorStroke();
0920: else
0921: setColorStroke(cl);
0922: }
0923: boolean bt = compareColors(ct, cl);
0924: boolean bb = compareColors(cb, cl);
0925: moveTo(l + wl / 2f, bt ? t : t - wt);
0926: lineTo(l + wl / 2f, bb ? b : b + wb);
0927: stroke();
0928: if (!bt || !bb) {
0929: if (!cdefi || !compareColors(cfil, cl)) {
0930: if (cl == null)
0931: resetRGBColorFill();
0932: else
0933: setColorFill(cl);
0934: }
0935: if (!bt) {
0936: moveTo(l, t);
0937: lineTo(l, t - wt);
0938: lineTo(l + wl, t - wt);
0939: fill();
0940: }
0941: if (!bb) {
0942: moveTo(l, b);
0943: lineTo(l, b + wb);
0944: lineTo(l + wl, b + wb);
0945: fill();
0946: }
0947: }
0948: }
0949: restoreState();
0950: }
0951:
0952: /**
0953: * Adds a border (complete or partially) to the current path..
0954: *
0955: * @param rectangle a <CODE>Rectangle</CODE>
0956: */
0957:
0958: public void rectangle(Rectangle rectangle) {
0959: // the coordinates of the border are retrieved
0960: float x1 = rectangle.getLeft();
0961: float y1 = rectangle.getBottom();
0962: float x2 = rectangle.getRight();
0963: float y2 = rectangle.getTop();
0964:
0965: // the backgroundcolor is set
0966: Color background = rectangle.getBackgroundColor();
0967: if (background != null) {
0968: setColorFill(background);
0969: rectangle(x1, y1, x2 - x1, y2 - y1);
0970: fill();
0971: resetRGBColorFill();
0972: }
0973:
0974: // if the element hasn't got any borders, nothing is added
0975: if (!rectangle.hasBorders()) {
0976: return;
0977: }
0978:
0979: // if any of the individual border colors are set
0980: // we draw the borders all around using the
0981: // different colors
0982: if (rectangle.isUseVariableBorders()) {
0983: variableRectangle(rectangle);
0984: } else {
0985: // the width is set to the width of the element
0986: if (rectangle.getBorderWidth() != Rectangle.UNDEFINED) {
0987: setLineWidth(rectangle.getBorderWidth());
0988: }
0989:
0990: // the color is set to the color of the element
0991: Color color = rectangle.getBorderColor();
0992: if (color != null) {
0993: setColorStroke(color);
0994: }
0995:
0996: // if the box is a rectangle, it is added as a rectangle
0997: if (rectangle.hasBorder(Rectangle.BOX)) {
0998: rectangle(x1, y1, x2 - x1, y2 - y1);
0999: }
1000: // if the border isn't a rectangle, the different sides are added apart
1001: else {
1002: if (rectangle.hasBorder(Rectangle.RIGHT)) {
1003: moveTo(x2, y1);
1004: lineTo(x2, y2);
1005: }
1006: if (rectangle.hasBorder(Rectangle.LEFT)) {
1007: moveTo(x1, y1);
1008: lineTo(x1, y2);
1009: }
1010: if (rectangle.hasBorder(Rectangle.BOTTOM)) {
1011: moveTo(x1, y1);
1012: lineTo(x2, y1);
1013: }
1014: if (rectangle.hasBorder(Rectangle.TOP)) {
1015: moveTo(x1, y2);
1016: lineTo(x2, y2);
1017: }
1018: }
1019:
1020: stroke();
1021:
1022: if (color != null) {
1023: resetRGBColorStroke();
1024: }
1025: }
1026: }
1027:
1028: /**
1029: * Closes the current subpath by appending a straight line segment from the current point
1030: * to the starting point of the subpath.
1031: */
1032:
1033: public void closePath() {
1034: content.append("h").append_i(separator);
1035: }
1036:
1037: /**
1038: * Ends the path without filling or stroking it.
1039: */
1040:
1041: public void newPath() {
1042: content.append("n").append_i(separator);
1043: }
1044:
1045: /**
1046: * Strokes the path.
1047: */
1048:
1049: public void stroke() {
1050: content.append("S").append_i(separator);
1051: }
1052:
1053: /**
1054: * Closes the path and strokes it.
1055: */
1056:
1057: public void closePathStroke() {
1058: content.append("s").append_i(separator);
1059: }
1060:
1061: /**
1062: * Fills the path, using the non-zero winding number rule to determine the region to fill.
1063: */
1064:
1065: public void fill() {
1066: content.append("f").append_i(separator);
1067: }
1068:
1069: /**
1070: * Fills the path, using the even-odd rule to determine the region to fill.
1071: */
1072:
1073: public void eoFill() {
1074: content.append("f*").append_i(separator);
1075: }
1076:
1077: /**
1078: * Fills the path using the non-zero winding number rule to determine the region to fill and strokes it.
1079: */
1080:
1081: public void fillStroke() {
1082: content.append("B").append_i(separator);
1083: }
1084:
1085: /**
1086: * Closes the path, fills it using the non-zero winding number rule to determine the region to fill and strokes it.
1087: */
1088:
1089: public void closePathFillStroke() {
1090: content.append("b").append_i(separator);
1091: }
1092:
1093: /**
1094: * Fills the path, using the even-odd rule to determine the region to fill and strokes it.
1095: */
1096:
1097: public void eoFillStroke() {
1098: content.append("B*").append_i(separator);
1099: }
1100:
1101: /**
1102: * Closes the path, fills it using the even-odd rule to determine the region to fill and strokes it.
1103: */
1104:
1105: public void closePathEoFillStroke() {
1106: content.append("b*").append_i(separator);
1107: }
1108:
1109: /**
1110: * Adds an <CODE>Image</CODE> to the page. The <CODE>Image</CODE> must have
1111: * absolute positioning.
1112: * @param image the <CODE>Image</CODE> object
1113: * @throws DocumentException if the <CODE>Image</CODE> does not have absolute positioning
1114: */
1115: public void addImage(Image image) throws DocumentException {
1116: addImage(image, false);
1117: }
1118:
1119: /**
1120: * Adds an <CODE>Image</CODE> to the page. The <CODE>Image</CODE> must have
1121: * absolute positioning. The image can be placed inline.
1122: * @param image the <CODE>Image</CODE> object
1123: * @param inlineImage <CODE>true</CODE> to place this image inline, <CODE>false</CODE> otherwise
1124: * @throws DocumentException if the <CODE>Image</CODE> does not have absolute positioning
1125: */
1126: public void addImage(Image image, boolean inlineImage)
1127: throws DocumentException {
1128: if (!image.hasAbsoluteY())
1129: throw new DocumentException(
1130: "The image must have absolute positioning.");
1131: float matrix[] = image.matrix();
1132: matrix[Image.CX] = image.getAbsoluteX() - matrix[Image.CX];
1133: matrix[Image.CY] = image.getAbsoluteY() - matrix[Image.CY];
1134: addImage(image, matrix[0], matrix[1], matrix[2], matrix[3],
1135: matrix[4], matrix[5], inlineImage);
1136: }
1137:
1138: /**
1139: * Adds an <CODE>Image</CODE> to the page. The positioning of the <CODE>Image</CODE>
1140: * is done with the transformation matrix. To position an <CODE>image</CODE> at (x,y)
1141: * use addImage(image, image_width, 0, 0, image_height, x, y).
1142: * @param image the <CODE>Image</CODE> object
1143: * @param a an element of the transformation matrix
1144: * @param b an element of the transformation matrix
1145: * @param c an element of the transformation matrix
1146: * @param d an element of the transformation matrix
1147: * @param e an element of the transformation matrix
1148: * @param f an element of the transformation matrix
1149: * @throws DocumentException on error
1150: */
1151: public void addImage(Image image, float a, float b, float c,
1152: float d, float e, float f) throws DocumentException {
1153: addImage(image, a, b, c, d, e, f, false);
1154: }
1155:
1156: /**
1157: * Adds an <CODE>Image</CODE> to the page. The positioning of the <CODE>Image</CODE>
1158: * is done with the transformation matrix. To position an <CODE>image</CODE> at (x,y)
1159: * use addImage(image, image_width, 0, 0, image_height, x, y). The image can be placed inline.
1160: * @param image the <CODE>Image</CODE> object
1161: * @param a an element of the transformation matrix
1162: * @param b an element of the transformation matrix
1163: * @param c an element of the transformation matrix
1164: * @param d an element of the transformation matrix
1165: * @param e an element of the transformation matrix
1166: * @param f an element of the transformation matrix
1167: * @param inlineImage <CODE>true</CODE> to place this image inline, <CODE>false</CODE> otherwise
1168: * @throws DocumentException on error
1169: */
1170: public void addImage(Image image, float a, float b, float c,
1171: float d, float e, float f, boolean inlineImage)
1172: throws DocumentException {
1173: try {
1174: if (image.getLayer() != null)
1175: beginLayer(image.getLayer());
1176: if (image.isImgTemplate()) {
1177: writer.addDirectImageSimple(image);
1178: PdfTemplate template = image.getTemplateData();
1179: float w = template.getWidth();
1180: float h = template.getHeight();
1181: addTemplate(template, a / w, b / w, c / h, d / h, e, f);
1182: } else {
1183: content.append("q ");
1184: content.append(a).append(' ');
1185: content.append(b).append(' ');
1186: content.append(c).append(' ');
1187: content.append(d).append(' ');
1188: content.append(e).append(' ');
1189: content.append(f).append(" cm");
1190: if (inlineImage) {
1191: content.append("\nBI\n");
1192: PdfImage pimage = new PdfImage(image, "", null);
1193: for (Iterator it = pimage.getKeys().iterator(); it
1194: .hasNext();) {
1195: PdfName key = (PdfName) it.next();
1196: PdfObject value = pimage.get(key);
1197: String s = (String) abrev.get(key);
1198: if (s == null)
1199: continue;
1200: content.append(s);
1201: boolean check = true;
1202: if (key.equals(PdfName.COLORSPACE)
1203: && value.isArray()) {
1204: ArrayList ar = ((PdfArray) value)
1205: .getArrayList();
1206: if (ar.size() == 4
1207: && PdfName.INDEXED
1208: .equals(ar.get(0))
1209: && ((PdfObject) ar.get(1)).isName()
1210: && ((PdfObject) ar.get(2))
1211: .isNumber()
1212: && ((PdfObject) ar.get(3))
1213: .isString()) {
1214: check = false;
1215: }
1216:
1217: }
1218: if (check && key.equals(PdfName.COLORSPACE)
1219: && !value.isName()) {
1220: PdfName cs = writer.getColorspaceName();
1221: PageResources prs = getPageResources();
1222: prs.addColor(cs, writer.addToBody(value)
1223: .getIndirectReference());
1224: value = cs;
1225: }
1226: value.toPdf(null, content);
1227: content.append('\n');
1228: }
1229: content.append("ID\n");
1230: pimage.writeContent(content);
1231: content.append("\nEI\nQ").append_i(separator);
1232: } else {
1233: PdfName name;
1234: PageResources prs = getPageResources();
1235: Image maskImage = image.getImageMask();
1236: if (maskImage != null) {
1237: name = writer.addDirectImageSimple(maskImage);
1238: prs.addXObject(name, writer
1239: .getImageReference(name));
1240: }
1241: name = writer.addDirectImageSimple(image);
1242: name = prs.addXObject(name, writer
1243: .getImageReference(name));
1244: content.append(' ').append(name.getBytes()).append(
1245: " Do Q").append_i(separator);
1246: }
1247: }
1248: if (image.hasBorders()) {
1249: saveState();
1250: float w = image.getWidth();
1251: float h = image.getHeight();
1252: concatCTM(a / w, b / w, c / h, d / h, e, f);
1253: rectangle(image);
1254: restoreState();
1255: }
1256: if (image.getLayer() != null)
1257: endLayer();
1258: Annotation annot = image.getAnnotation();
1259: if (annot == null)
1260: return;
1261: float[] r = new float[unitRect.length];
1262: for (int k = 0; k < unitRect.length; k += 2) {
1263: r[k] = a * unitRect[k] + c * unitRect[k + 1] + e;
1264: r[k + 1] = b * unitRect[k] + d * unitRect[k + 1] + f;
1265: }
1266: float llx = r[0];
1267: float lly = r[1];
1268: float urx = llx;
1269: float ury = lly;
1270: for (int k = 2; k < r.length; k += 2) {
1271: llx = Math.min(llx, r[k]);
1272: lly = Math.min(lly, r[k + 1]);
1273: urx = Math.max(urx, r[k]);
1274: ury = Math.max(ury, r[k + 1]);
1275: }
1276: annot = new Annotation(annot);
1277: annot.setDimensions(llx, lly, urx, ury);
1278: PdfAnnotation an = PdfAnnotationsImp.convertAnnotation(
1279: writer, annot, new Rectangle(llx, lly, urx, ury));
1280: if (an == null)
1281: return;
1282: addAnnotation(an);
1283: } catch (Exception ee) {
1284: throw new DocumentException(ee);
1285: }
1286: }
1287:
1288: /**
1289: * Makes this <CODE>PdfContentByte</CODE> empty.
1290: */
1291: public void reset() {
1292: content.reset();
1293: stateList.clear();
1294: state = new GraphicState();
1295: }
1296:
1297: /**
1298: * Starts the writing of text.
1299: */
1300: public void beginText() {
1301: state.xTLM = 0;
1302: state.yTLM = 0;
1303: content.append("BT").append_i(separator);
1304: }
1305:
1306: /**
1307: * Ends the writing of text and makes the current font invalid.
1308: */
1309: public void endText() {
1310: content.append("ET").append_i(separator);
1311: }
1312:
1313: /**
1314: * Saves the graphic state. <CODE>saveState</CODE> and
1315: * <CODE>restoreState</CODE> must be balanced.
1316: */
1317: public void saveState() {
1318: content.append("q").append_i(separator);
1319: stateList.add(new GraphicState(state));
1320: }
1321:
1322: /**
1323: * Restores the graphic state. <CODE>saveState</CODE> and
1324: * <CODE>restoreState</CODE> must be balanced.
1325: */
1326: public void restoreState() {
1327: content.append("Q").append_i(separator);
1328: int idx = stateList.size() - 1;
1329: if (idx < 0)
1330: throw new RuntimeException(
1331: "Unbalanced save/restore state operators.");
1332: state = (GraphicState) stateList.get(idx);
1333: stateList.remove(idx);
1334: }
1335:
1336: /**
1337: * Sets the character spacing parameter.
1338: *
1339: * @param charSpace a parameter
1340: */
1341: public void setCharacterSpacing(float charSpace) {
1342: state.charSpace = charSpace;
1343: content.append(charSpace).append(" Tc").append_i(separator);
1344: }
1345:
1346: /**
1347: * Sets the word spacing parameter.
1348: *
1349: * @param wordSpace a parameter
1350: */
1351: public void setWordSpacing(float wordSpace) {
1352: state.wordSpace = wordSpace;
1353: content.append(wordSpace).append(" Tw").append_i(separator);
1354: }
1355:
1356: /**
1357: * Sets the horizontal scaling parameter.
1358: *
1359: * @param scale a parameter
1360: */
1361: public void setHorizontalScaling(float scale) {
1362: state.scale = scale;
1363: content.append(scale).append(" Tz").append_i(separator);
1364: }
1365:
1366: /**
1367: * Sets the text leading parameter.
1368: * <P>
1369: * The leading parameter is measured in text space units. It specifies the vertical distance
1370: * between the baselines of adjacent lines of text.</P>
1371: *
1372: * @param leading the new leading
1373: */
1374: public void setLeading(float leading) {
1375: state.leading = leading;
1376: content.append(leading).append(" TL").append_i(separator);
1377: }
1378:
1379: /**
1380: * Set the font and the size for the subsequent text writing.
1381: *
1382: * @param bf the font
1383: * @param size the font size in points
1384: */
1385: public void setFontAndSize(BaseFont bf, float size) {
1386: checkWriter();
1387: state.size = size;
1388: state.fontDetails = writer.addSimple(bf);
1389: PageResources prs = getPageResources();
1390: PdfName name = state.fontDetails.getFontName();
1391: name = prs.addFont(name, state.fontDetails
1392: .getIndirectReference());
1393: content.append(name.getBytes()).append(' ').append(size)
1394: .append(" Tf").append_i(separator);
1395: }
1396:
1397: /**
1398: * Sets the text rendering parameter.
1399: *
1400: * @param rendering a parameter
1401: */
1402: public void setTextRenderingMode(int rendering) {
1403: content.append(rendering).append(" Tr").append_i(separator);
1404: }
1405:
1406: /**
1407: * Sets the text rise parameter.
1408: * <P>
1409: * This allows to write text in subscript or superscript mode.</P>
1410: *
1411: * @param rise a parameter
1412: */
1413: public void setTextRise(float rise) {
1414: content.append(rise).append(" Ts").append_i(separator);
1415: }
1416:
1417: /**
1418: * A helper to insert into the content stream the <CODE>text</CODE>
1419: * converted to bytes according to the font's encoding.
1420: *
1421: * @param text the text to write
1422: */
1423: private void showText2(String text) {
1424: if (state.fontDetails == null)
1425: throw new NullPointerException(
1426: "Font and size must be set before writing any text");
1427: byte b[] = state.fontDetails.convertToBytes(text);
1428: escapeString(b, content);
1429: }
1430:
1431: /**
1432: * Shows the <CODE>text</CODE>.
1433: *
1434: * @param text the text to write
1435: */
1436: public void showText(String text) {
1437: showText2(text);
1438: content.append("Tj").append_i(separator);
1439: }
1440:
1441: /**
1442: * Constructs a kern array for a text in a certain font
1443: * @param text the text
1444: * @param font the font
1445: * @return a PdfTextArray
1446: */
1447: public static PdfTextArray getKernArray(String text, BaseFont font) {
1448: PdfTextArray pa = new PdfTextArray();
1449: StringBuffer acc = new StringBuffer();
1450: int len = text.length() - 1;
1451: char c[] = text.toCharArray();
1452: if (len >= 0)
1453: acc.append(c, 0, 1);
1454: for (int k = 0; k < len; ++k) {
1455: char c2 = c[k + 1];
1456: int kern = font.getKerning(c[k], c2);
1457: if (kern == 0) {
1458: acc.append(c2);
1459: } else {
1460: pa.add(acc.toString());
1461: acc.setLength(0);
1462: acc.append(c, k + 1, 1);
1463: pa.add(-kern);
1464: }
1465: }
1466: pa.add(acc.toString());
1467: return pa;
1468: }
1469:
1470: /**
1471: * Shows the <CODE>text</CODE> kerned.
1472: *
1473: * @param text the text to write
1474: */
1475: public void showTextKerned(String text) {
1476: if (state.fontDetails == null)
1477: throw new NullPointerException(
1478: "Font and size must be set before writing any text");
1479: BaseFont bf = state.fontDetails.getBaseFont();
1480: if (bf.hasKernPairs())
1481: showText(getKernArray(text, bf));
1482: else
1483: showText(text);
1484: }
1485:
1486: /**
1487: * Moves to the next line and shows <CODE>text</CODE>.
1488: *
1489: * @param text the text to write
1490: */
1491: public void newlineShowText(String text) {
1492: state.yTLM -= state.leading;
1493: showText2(text);
1494: content.append("'").append_i(separator);
1495: }
1496:
1497: /**
1498: * Moves to the next line and shows text string, using the given values of the character and word spacing parameters.
1499: *
1500: * @param wordSpacing a parameter
1501: * @param charSpacing a parameter
1502: * @param text the text to write
1503: */
1504: public void newlineShowText(float wordSpacing, float charSpacing,
1505: String text) {
1506: state.yTLM -= state.leading;
1507: content.append(wordSpacing).append(' ').append(charSpacing);
1508: showText2(text);
1509: content.append("\"").append_i(separator);
1510:
1511: // The " operator sets charSpace and wordSpace into graphics state
1512: // (cfr PDF reference v1.6, table 5.6)
1513: state.charSpace = charSpacing;
1514: state.wordSpace = wordSpacing;
1515: }
1516:
1517: /**
1518: * Changes the text matrix.
1519: * <P>
1520: * Remark: this operation also initializes the current point position.</P>
1521: *
1522: * @param a operand 1,1 in the matrix
1523: * @param b operand 1,2 in the matrix
1524: * @param c operand 2,1 in the matrix
1525: * @param d operand 2,2 in the matrix
1526: * @param x operand 3,1 in the matrix
1527: * @param y operand 3,2 in the matrix
1528: */
1529: public void setTextMatrix(float a, float b, float c, float d,
1530: float x, float y) {
1531: state.xTLM = x;
1532: state.yTLM = y;
1533: content.append(a).append(' ').append(b).append_i(' ').append(c)
1534: .append_i(' ').append(d).append_i(' ').append(x)
1535: .append_i(' ').append(y).append(" Tm").append_i(
1536: separator);
1537: }
1538:
1539: /**
1540: * Changes the text matrix. The first four parameters are {1,0,0,1}.
1541: * <P>
1542: * Remark: this operation also initializes the current point position.</P>
1543: *
1544: * @param x operand 3,1 in the matrix
1545: * @param y operand 3,2 in the matrix
1546: */
1547: public void setTextMatrix(float x, float y) {
1548: setTextMatrix(1, 0, 0, 1, x, y);
1549: }
1550:
1551: /**
1552: * Moves to the start of the next line, offset from the start of the current line.
1553: *
1554: * @param x x-coordinate of the new current point
1555: * @param y y-coordinate of the new current point
1556: */
1557: public void moveText(float x, float y) {
1558: state.xTLM += x;
1559: state.yTLM += y;
1560: content.append(x).append(' ').append(y).append(" Td").append_i(
1561: separator);
1562: }
1563:
1564: /**
1565: * Moves to the start of the next line, offset from the start of the current line.
1566: * <P>
1567: * As a side effect, this sets the leading parameter in the text state.</P>
1568: *
1569: * @param x offset of the new current point
1570: * @param y y-coordinate of the new current point
1571: */
1572: public void moveTextWithLeading(float x, float y) {
1573: state.xTLM += x;
1574: state.yTLM += y;
1575: state.leading = -y;
1576: content.append(x).append(' ').append(y).append(" TD").append_i(
1577: separator);
1578: }
1579:
1580: /**
1581: * Moves to the start of the next line.
1582: */
1583: public void newlineText() {
1584: state.yTLM -= state.leading;
1585: content.append("T*").append_i(separator);
1586: }
1587:
1588: /**
1589: * Gets the size of this content.
1590: *
1591: * @return the size of the content
1592: */
1593: int size() {
1594: return content.size();
1595: }
1596:
1597: /**
1598: * Escapes a <CODE>byte</CODE> array according to the PDF conventions.
1599: *
1600: * @param b the <CODE>byte</CODE> array to escape
1601: * @return an escaped <CODE>byte</CODE> array
1602: */
1603: static byte[] escapeString(byte b[]) {
1604: ByteBuffer content = new ByteBuffer();
1605: escapeString(b, content);
1606: return content.toByteArray();
1607: }
1608:
1609: /**
1610: * Escapes a <CODE>byte</CODE> array according to the PDF conventions.
1611: *
1612: * @param b the <CODE>byte</CODE> array to escape
1613: * @param content the content
1614: */
1615: static void escapeString(byte b[], ByteBuffer content) {
1616: content.append_i('(');
1617: for (int k = 0; k < b.length; ++k) {
1618: byte c = b[k];
1619: switch ((int) c) {
1620: case '\r':
1621: content.append("\\r");
1622: break;
1623: case '\n':
1624: content.append("\\n");
1625: break;
1626: case '\t':
1627: content.append("\\t");
1628: break;
1629: case '\b':
1630: content.append("\\b");
1631: break;
1632: case '\f':
1633: content.append("\\f");
1634: break;
1635: case '(':
1636: case ')':
1637: case '\\':
1638: content.append_i('\\').append_i(c);
1639: break;
1640: default:
1641: content.append_i(c);
1642: }
1643: }
1644: content.append(")");
1645: }
1646:
1647: /**
1648: * Adds an outline to the document.
1649: *
1650: * @param outline the outline
1651: * @deprecated not needed anymore. The outlines are extracted
1652: * from the root outline
1653: */
1654: public void addOutline(PdfOutline outline) {
1655: // for compatibility
1656: }
1657:
1658: /**
1659: * Adds a named outline to the document.
1660: *
1661: * @param outline the outline
1662: * @param name the name for the local destination
1663: */
1664: public void addOutline(PdfOutline outline, String name) {
1665: checkWriter();
1666: pdf.addOutline(outline, name);
1667: }
1668:
1669: /**
1670: * Gets the root outline.
1671: *
1672: * @return the root outline
1673: */
1674: public PdfOutline getRootOutline() {
1675: checkWriter();
1676: return pdf.getRootOutline();
1677: }
1678:
1679: /**
1680: * Computes the width of the given string taking in account
1681: * the current values of "Character spacing", "Word Spacing"
1682: * and "Horizontal Scaling".
1683: * The additional spacing is not computed for the last character
1684: * of the string.
1685: * @param text the string to get width of
1686: * @param kerned the kerning option
1687: * @return the width
1688: */
1689:
1690: public float getEffectiveStringWidth(String text, boolean kerned) {
1691: BaseFont bf = state.fontDetails.getBaseFont();
1692:
1693: float w;
1694: if (kerned)
1695: w = bf.getWidthPointKerned(text, state.size);
1696: else
1697: w = bf.getWidthPoint(text, state.size);
1698:
1699: if (state.charSpace != 0.0f && text.length() > 1) {
1700: w += state.charSpace * (text.length() - 1);
1701: }
1702:
1703: int ft = bf.getFontType();
1704: if (state.wordSpace != 0.0f
1705: && (ft == BaseFont.FONT_TYPE_T1
1706: || ft == BaseFont.FONT_TYPE_TT || ft == BaseFont.FONT_TYPE_T3)) {
1707: for (int i = 0; i < (text.length() - 1); i++) {
1708: if (text.charAt(i) == ' ')
1709: w += state.wordSpace;
1710: }
1711: }
1712: if (state.scale != 100.0)
1713: w = (w * state.scale) / 100.0f;
1714:
1715: //System.out.println("String width = " + Float.toString(w));
1716: return w;
1717: }
1718:
1719: /**
1720: * Shows text right, left or center aligned with rotation.
1721: * @param alignment the alignment can be ALIGN_CENTER, ALIGN_RIGHT or ALIGN_LEFT
1722: * @param text the text to show
1723: * @param x the x pivot position
1724: * @param y the y pivot position
1725: * @param rotation the rotation to be applied in degrees counterclockwise
1726: */
1727: public void showTextAligned(int alignment, String text, float x,
1728: float y, float rotation) {
1729: showTextAligned(alignment, text, x, y, rotation, false);
1730: }
1731:
1732: private void showTextAligned(int alignment, String text, float x,
1733: float y, float rotation, boolean kerned) {
1734: if (state.fontDetails == null)
1735: throw new NullPointerException(
1736: "Font and size must be set before writing any text");
1737: if (rotation == 0) {
1738: switch (alignment) {
1739: case ALIGN_CENTER:
1740: x -= getEffectiveStringWidth(text, kerned) / 2;
1741: break;
1742: case ALIGN_RIGHT:
1743: x -= getEffectiveStringWidth(text, kerned);
1744: break;
1745: }
1746: setTextMatrix(x, y);
1747: if (kerned)
1748: showTextKerned(text);
1749: else
1750: showText(text);
1751: } else {
1752: double alpha = rotation * Math.PI / 180.0;
1753: float cos = (float) Math.cos(alpha);
1754: float sin = (float) Math.sin(alpha);
1755: float len;
1756: switch (alignment) {
1757: case ALIGN_CENTER:
1758: len = getEffectiveStringWidth(text, kerned) / 2;
1759: x -= len * cos;
1760: y -= len * sin;
1761: break;
1762: case ALIGN_RIGHT:
1763: len = getEffectiveStringWidth(text, kerned);
1764: x -= len * cos;
1765: y -= len * sin;
1766: break;
1767: }
1768: setTextMatrix(cos, sin, -sin, cos, x, y);
1769: if (kerned)
1770: showTextKerned(text);
1771: else
1772: showText(text);
1773: setTextMatrix(0f, 0f);
1774: }
1775: }
1776:
1777: /**
1778: * Shows text kerned right, left or center aligned with rotation.
1779: * @param alignment the alignment can be ALIGN_CENTER, ALIGN_RIGHT or ALIGN_LEFT
1780: * @param text the text to show
1781: * @param x the x pivot position
1782: * @param y the y pivot position
1783: * @param rotation the rotation to be applied in degrees counterclockwise
1784: */
1785: public void showTextAlignedKerned(int alignment, String text,
1786: float x, float y, float rotation) {
1787: showTextAligned(alignment, text, x, y, rotation, true);
1788: }
1789:
1790: /**
1791: * Concatenate a matrix to the current transformation matrix.
1792: * @param a an element of the transformation matrix
1793: * @param b an element of the transformation matrix
1794: * @param c an element of the transformation matrix
1795: * @param d an element of the transformation matrix
1796: * @param e an element of the transformation matrix
1797: * @param f an element of the transformation matrix
1798: **/
1799: public void concatCTM(float a, float b, float c, float d, float e,
1800: float f) {
1801: content.append(a).append(' ').append(b).append(' ').append(c)
1802: .append(' ');
1803: content.append(d).append(' ').append(e).append(' ').append(f)
1804: .append(" cm").append_i(separator);
1805: }
1806:
1807: /**
1808: * Generates an array of bezier curves to draw an arc.
1809: * <P>
1810: * (x1, y1) and (x2, y2) are the corners of the enclosing rectangle.
1811: * Angles, measured in degrees, start with 0 to the right (the positive X
1812: * axis) and increase counter-clockwise. The arc extends from startAng
1813: * to startAng+extent. I.e. startAng=0 and extent=180 yields an openside-down
1814: * semi-circle.
1815: * <P>
1816: * The resulting coordinates are of the form float[]{x1,y1,x2,y2,x3,y3, x4,y4}
1817: * such that the curve goes from (x1, y1) to (x4, y4) with (x2, y2) and
1818: * (x3, y3) as their respective Bezier control points.
1819: * <P>
1820: * Note: this code was taken from ReportLab (www.reportlab.org), an excelent
1821: * PDF generator for Python (BSD license: http://www.reportlab.org/devfaq.html#1.3 ).
1822: *
1823: * @param x1 a corner of the enclosing rectangle
1824: * @param y1 a corner of the enclosing rectangle
1825: * @param x2 a corner of the enclosing rectangle
1826: * @param y2 a corner of the enclosing rectangle
1827: * @param startAng starting angle in degrees
1828: * @param extent angle extent in degrees
1829: * @return a list of float[] with the bezier curves
1830: */
1831: public static ArrayList bezierArc(float x1, float y1, float x2,
1832: float y2, float startAng, float extent) {
1833: float tmp;
1834: if (x1 > x2) {
1835: tmp = x1;
1836: x1 = x2;
1837: x2 = tmp;
1838: }
1839: if (y2 > y1) {
1840: tmp = y1;
1841: y1 = y2;
1842: y2 = tmp;
1843: }
1844:
1845: float fragAngle;
1846: int Nfrag;
1847: if (Math.abs(extent) <= 90f) {
1848: fragAngle = extent;
1849: Nfrag = 1;
1850: } else {
1851: Nfrag = (int) (Math.ceil(Math.abs(extent) / 90f));
1852: fragAngle = extent / Nfrag;
1853: }
1854: float x_cen = (x1 + x2) / 2f;
1855: float y_cen = (y1 + y2) / 2f;
1856: float rx = (x2 - x1) / 2f;
1857: float ry = (y2 - y1) / 2f;
1858: float halfAng = (float) (fragAngle * Math.PI / 360.);
1859: float kappa = (float) (Math.abs(4. / 3.
1860: * (1. - Math.cos(halfAng)) / Math.sin(halfAng)));
1861: ArrayList pointList = new ArrayList();
1862: for (int i = 0; i < Nfrag; ++i) {
1863: float theta0 = (float) ((startAng + i * fragAngle)
1864: * Math.PI / 180.);
1865: float theta1 = (float) ((startAng + (i + 1) * fragAngle)
1866: * Math.PI / 180.);
1867: float cos0 = (float) Math.cos(theta0);
1868: float cos1 = (float) Math.cos(theta1);
1869: float sin0 = (float) Math.sin(theta0);
1870: float sin1 = (float) Math.sin(theta1);
1871: if (fragAngle > 0f) {
1872: pointList.add(new float[] { x_cen + rx * cos0,
1873: y_cen - ry * sin0,
1874: x_cen + rx * (cos0 - kappa * sin0),
1875: y_cen - ry * (sin0 + kappa * cos0),
1876: x_cen + rx * (cos1 + kappa * sin1),
1877: y_cen - ry * (sin1 - kappa * cos1),
1878: x_cen + rx * cos1, y_cen - ry * sin1 });
1879: } else {
1880: pointList.add(new float[] { x_cen + rx * cos0,
1881: y_cen - ry * sin0,
1882: x_cen + rx * (cos0 + kappa * sin0),
1883: y_cen - ry * (sin0 - kappa * cos0),
1884: x_cen + rx * (cos1 - kappa * sin1),
1885: y_cen - ry * (sin1 + kappa * cos1),
1886: x_cen + rx * cos1, y_cen - ry * sin1 });
1887: }
1888: }
1889: return pointList;
1890: }
1891:
1892: /**
1893: * Draws a partial ellipse inscribed within the rectangle x1,y1,x2,y2,
1894: * starting at startAng degrees and covering extent degrees. Angles
1895: * start with 0 to the right (+x) and increase counter-clockwise.
1896: *
1897: * @param x1 a corner of the enclosing rectangle
1898: * @param y1 a corner of the enclosing rectangle
1899: * @param x2 a corner of the enclosing rectangle
1900: * @param y2 a corner of the enclosing rectangle
1901: * @param startAng starting angle in degrees
1902: * @param extent angle extent in degrees
1903: */
1904: public void arc(float x1, float y1, float x2, float y2,
1905: float startAng, float extent) {
1906: ArrayList ar = bezierArc(x1, y1, x2, y2, startAng, extent);
1907: if (ar.isEmpty())
1908: return;
1909: float pt[] = (float[]) ar.get(0);
1910: moveTo(pt[0], pt[1]);
1911: for (int k = 0; k < ar.size(); ++k) {
1912: pt = (float[]) ar.get(k);
1913: curveTo(pt[2], pt[3], pt[4], pt[5], pt[6], pt[7]);
1914: }
1915: }
1916:
1917: /**
1918: * Draws an ellipse inscribed within the rectangle x1,y1,x2,y2.
1919: *
1920: * @param x1 a corner of the enclosing rectangle
1921: * @param y1 a corner of the enclosing rectangle
1922: * @param x2 a corner of the enclosing rectangle
1923: * @param y2 a corner of the enclosing rectangle
1924: */
1925: public void ellipse(float x1, float y1, float x2, float y2) {
1926: arc(x1, y1, x2, y2, 0f, 360f);
1927: }
1928:
1929: /**
1930: * Create a new colored tiling pattern.
1931: *
1932: * @param width the width of the pattern
1933: * @param height the height of the pattern
1934: * @param xstep the desired horizontal spacing between pattern cells.
1935: * May be either positive or negative, but not zero.
1936: * @param ystep the desired vertical spacing between pattern cells.
1937: * May be either positive or negative, but not zero.
1938: * @return the <CODE>PdfPatternPainter</CODE> where the pattern will be created
1939: */
1940: public PdfPatternPainter createPattern(float width, float height,
1941: float xstep, float ystep) {
1942: checkWriter();
1943: if (xstep == 0.0f || ystep == 0.0f)
1944: throw new RuntimeException(
1945: "XStep or YStep can not be ZERO.");
1946: PdfPatternPainter painter = new PdfPatternPainter(writer);
1947: painter.setWidth(width);
1948: painter.setHeight(height);
1949: painter.setXStep(xstep);
1950: painter.setYStep(ystep);
1951: writer.addSimplePattern(painter);
1952: return painter;
1953: }
1954:
1955: /**
1956: * Create a new colored tiling pattern. Variables xstep and ystep are set to the same values
1957: * of width and height.
1958: * @param width the width of the pattern
1959: * @param height the height of the pattern
1960: * @return the <CODE>PdfPatternPainter</CODE> where the pattern will be created
1961: */
1962: public PdfPatternPainter createPattern(float width, float height) {
1963: return createPattern(width, height, width, height);
1964: }
1965:
1966: /**
1967: * Create a new uncolored tiling pattern.
1968: *
1969: * @param width the width of the pattern
1970: * @param height the height of the pattern
1971: * @param xstep the desired horizontal spacing between pattern cells.
1972: * May be either positive or negative, but not zero.
1973: * @param ystep the desired vertical spacing between pattern cells.
1974: * May be either positive or negative, but not zero.
1975: * @param color the default color. Can be <CODE>null</CODE>
1976: * @return the <CODE>PdfPatternPainter</CODE> where the pattern will be created
1977: */
1978: public PdfPatternPainter createPattern(float width, float height,
1979: float xstep, float ystep, Color color) {
1980: checkWriter();
1981: if (xstep == 0.0f || ystep == 0.0f)
1982: throw new RuntimeException(
1983: "XStep or YStep can not be ZERO.");
1984: PdfPatternPainter painter = new PdfPatternPainter(writer, color);
1985: painter.setWidth(width);
1986: painter.setHeight(height);
1987: painter.setXStep(xstep);
1988: painter.setYStep(ystep);
1989: writer.addSimplePattern(painter);
1990: return painter;
1991: }
1992:
1993: /**
1994: * Create a new uncolored tiling pattern.
1995: * Variables xstep and ystep are set to the same values
1996: * of width and height.
1997: * @param width the width of the pattern
1998: * @param height the height of the pattern
1999: * @param color the default color. Can be <CODE>null</CODE>
2000: * @return the <CODE>PdfPatternPainter</CODE> where the pattern will be created
2001: */
2002: public PdfPatternPainter createPattern(float width, float height,
2003: Color color) {
2004: return createPattern(width, height, width, height, color);
2005: }
2006:
2007: /**
2008: * Creates a new template.
2009: * <P>
2010: * Creates a new template that is nothing more than a form XObject. This template can be included
2011: * in this <CODE>PdfContentByte</CODE> or in another template. Templates are only written
2012: * to the output when the document is closed permitting things like showing text in the first page
2013: * that is only defined in the last page.
2014: *
2015: * @param width the bounding box width
2016: * @param height the bounding box height
2017: * @return the templated created
2018: */
2019: public PdfTemplate createTemplate(float width, float height) {
2020: return createTemplate(width, height, null);
2021: }
2022:
2023: PdfTemplate createTemplate(float width, float height,
2024: PdfName forcedName) {
2025: checkWriter();
2026: PdfTemplate template = new PdfTemplate(writer);
2027: template.setWidth(width);
2028: template.setHeight(height);
2029: writer.addDirectTemplateSimple(template, forcedName);
2030: return template;
2031: }
2032:
2033: /**
2034: * Creates a new appearance to be used with form fields.
2035: *
2036: * @param width the bounding box width
2037: * @param height the bounding box height
2038: * @return the appearance created
2039: */
2040: public PdfAppearance createAppearance(float width, float height) {
2041: return createAppearance(width, height, null);
2042: }
2043:
2044: PdfAppearance createAppearance(float width, float height,
2045: PdfName forcedName) {
2046: checkWriter();
2047: PdfAppearance template = new PdfAppearance(writer);
2048: template.setWidth(width);
2049: template.setHeight(height);
2050: writer.addDirectTemplateSimple(template, forcedName);
2051: return template;
2052: }
2053:
2054: /**
2055: * Adds a PostScript XObject to this content.
2056: *
2057: * @param psobject the object
2058: */
2059: public void addPSXObject(PdfPSXObject psobject) {
2060: checkWriter();
2061: PdfName name = writer.addDirectTemplateSimple(psobject, null);
2062: PageResources prs = getPageResources();
2063: name = prs.addXObject(name, psobject.getIndirectReference());
2064: content.append(name.getBytes()).append(" Do").append_i(
2065: separator);
2066: }
2067:
2068: /**
2069: * Adds a template to this content.
2070: *
2071: * @param template the template
2072: * @param a an element of the transformation matrix
2073: * @param b an element of the transformation matrix
2074: * @param c an element of the transformation matrix
2075: * @param d an element of the transformation matrix
2076: * @param e an element of the transformation matrix
2077: * @param f an element of the transformation matrix
2078: */
2079: public void addTemplate(PdfTemplate template, float a, float b,
2080: float c, float d, float e, float f) {
2081: checkWriter();
2082: checkNoPattern(template);
2083: PdfName name = writer.addDirectTemplateSimple(template, null);
2084: PageResources prs = getPageResources();
2085: name = prs.addXObject(name, template.getIndirectReference());
2086: content.append("q ");
2087: content.append(a).append(' ');
2088: content.append(b).append(' ');
2089: content.append(c).append(' ');
2090: content.append(d).append(' ');
2091: content.append(e).append(' ');
2092: content.append(f).append(" cm ");
2093: content.append(name.getBytes()).append(" Do Q").append_i(
2094: separator);
2095: }
2096:
2097: void addTemplateReference(PdfIndirectReference template,
2098: PdfName name, float a, float b, float c, float d, float e,
2099: float f) {
2100: checkWriter();
2101: PageResources prs = getPageResources();
2102: name = prs.addXObject(name, template);
2103: content.append("q ");
2104: content.append(a).append(' ');
2105: content.append(b).append(' ');
2106: content.append(c).append(' ');
2107: content.append(d).append(' ');
2108: content.append(e).append(' ');
2109: content.append(f).append(" cm ");
2110: content.append(name.getBytes()).append(" Do Q").append_i(
2111: separator);
2112: }
2113:
2114: /**
2115: * Adds a template to this content.
2116: *
2117: * @param template the template
2118: * @param x the x location of this template
2119: * @param y the y location of this template
2120: */
2121: public void addTemplate(PdfTemplate template, float x, float y) {
2122: addTemplate(template, 1, 0, 0, 1, x, y);
2123: }
2124:
2125: /**
2126: * Changes the current color for filling paths (device dependent colors!).
2127: * <P>
2128: * Sets the color space to <B>DeviceCMYK</B> (or the <B>DefaultCMYK</B> color space),
2129: * and sets the color to use for filling paths.</P>
2130: * <P>
2131: * This method is described in the 'Portable Document Format Reference Manual version 1.3'
2132: * section 8.5.2.1 (page 331).</P>
2133: * <P>
2134: * Following the PDF manual, each operand must be a number between 0 (no ink) and
2135: * 1 (maximum ink). This method however accepts only integers between 0x00 and 0xFF.</P>
2136: *
2137: * @param cyan the intensity of cyan
2138: * @param magenta the intensity of magenta
2139: * @param yellow the intensity of yellow
2140: * @param black the intensity of black
2141: */
2142:
2143: public void setCMYKColorFill(int cyan, int magenta, int yellow,
2144: int black) {
2145: content.append((float) (cyan & 0xFF) / 0xFF);
2146: content.append(' ');
2147: content.append((float) (magenta & 0xFF) / 0xFF);
2148: content.append(' ');
2149: content.append((float) (yellow & 0xFF) / 0xFF);
2150: content.append(' ');
2151: content.append((float) (black & 0xFF) / 0xFF);
2152: content.append(" k").append_i(separator);
2153: }
2154:
2155: /**
2156: * Changes the current color for stroking paths (device dependent colors!).
2157: * <P>
2158: * Sets the color space to <B>DeviceCMYK</B> (or the <B>DefaultCMYK</B> color space),
2159: * and sets the color to use for stroking paths.</P>
2160: * <P>
2161: * This method is described in the 'Portable Document Format Reference Manual version 1.3'
2162: * section 8.5.2.1 (page 331).</P>
2163: * Following the PDF manual, each operand must be a number between 0 (miniumum intensity) and
2164: * 1 (maximum intensity). This method however accepts only integers between 0x00 and 0xFF.
2165: *
2166: * @param cyan the intensity of red
2167: * @param magenta the intensity of green
2168: * @param yellow the intensity of blue
2169: * @param black the intensity of black
2170: */
2171:
2172: public void setCMYKColorStroke(int cyan, int magenta, int yellow,
2173: int black) {
2174: content.append((float) (cyan & 0xFF) / 0xFF);
2175: content.append(' ');
2176: content.append((float) (magenta & 0xFF) / 0xFF);
2177: content.append(' ');
2178: content.append((float) (yellow & 0xFF) / 0xFF);
2179: content.append(' ');
2180: content.append((float) (black & 0xFF) / 0xFF);
2181: content.append(" K").append_i(separator);
2182: }
2183:
2184: /**
2185: * Changes the current color for filling paths (device dependent colors!).
2186: * <P>
2187: * Sets the color space to <B>DeviceRGB</B> (or the <B>DefaultRGB</B> color space),
2188: * and sets the color to use for filling paths.</P>
2189: * <P>
2190: * This method is described in the 'Portable Document Format Reference Manual version 1.3'
2191: * section 8.5.2.1 (page 331).</P>
2192: * <P>
2193: * Following the PDF manual, each operand must be a number between 0 (miniumum intensity) and
2194: * 1 (maximum intensity). This method however accepts only integers between 0x00 and 0xFF.</P>
2195: *
2196: * @param red the intensity of red
2197: * @param green the intensity of green
2198: * @param blue the intensity of blue
2199: */
2200:
2201: public void setRGBColorFill(int red, int green, int blue) {
2202: HelperRGB((float) (red & 0xFF) / 0xFF,
2203: (float) (green & 0xFF) / 0xFF,
2204: (float) (blue & 0xFF) / 0xFF);
2205: content.append(" rg").append_i(separator);
2206: }
2207:
2208: /**
2209: * Changes the current color for stroking paths (device dependent colors!).
2210: * <P>
2211: * Sets the color space to <B>DeviceRGB</B> (or the <B>DefaultRGB</B> color space),
2212: * and sets the color to use for stroking paths.</P>
2213: * <P>
2214: * This method is described in the 'Portable Document Format Reference Manual version 1.3'
2215: * section 8.5.2.1 (page 331).</P>
2216: * Following the PDF manual, each operand must be a number between 0 (miniumum intensity) and
2217: * 1 (maximum intensity). This method however accepts only integers between 0x00 and 0xFF.
2218: *
2219: * @param red the intensity of red
2220: * @param green the intensity of green
2221: * @param blue the intensity of blue
2222: */
2223:
2224: public void setRGBColorStroke(int red, int green, int blue) {
2225: HelperRGB((float) (red & 0xFF) / 0xFF,
2226: (float) (green & 0xFF) / 0xFF,
2227: (float) (blue & 0xFF) / 0xFF);
2228: content.append(" RG").append_i(separator);
2229: }
2230:
2231: /** Sets the stroke color. <CODE>color</CODE> can be an
2232: * <CODE>ExtendedColor</CODE>.
2233: * @param color the color
2234: */
2235: public void setColorStroke(Color color) {
2236: PdfXConformanceImp.checkPDFXConformance(writer,
2237: PdfXConformanceImp.PDFXKEY_COLOR, color);
2238: int type = ExtendedColor.getType(color);
2239: switch (type) {
2240: case ExtendedColor.TYPE_GRAY: {
2241: setGrayStroke(((GrayColor) color).getGray());
2242: break;
2243: }
2244: case ExtendedColor.TYPE_CMYK: {
2245: CMYKColor cmyk = (CMYKColor) color;
2246: setCMYKColorStrokeF(cmyk.getCyan(), cmyk.getMagenta(), cmyk
2247: .getYellow(), cmyk.getBlack());
2248: break;
2249: }
2250: case ExtendedColor.TYPE_SEPARATION: {
2251: SpotColor spot = (SpotColor) color;
2252: setColorStroke(spot.getPdfSpotColor(), spot.getTint());
2253: break;
2254: }
2255: case ExtendedColor.TYPE_PATTERN: {
2256: PatternColor pat = (PatternColor) color;
2257: setPatternStroke(pat.getPainter());
2258: break;
2259: }
2260: case ExtendedColor.TYPE_SHADING: {
2261: ShadingColor shading = (ShadingColor) color;
2262: setShadingStroke(shading.getPdfShadingPattern());
2263: break;
2264: }
2265: default:
2266: setRGBColorStroke(color.getRed(), color.getGreen(), color
2267: .getBlue());
2268: }
2269: }
2270:
2271: /** Sets the fill color. <CODE>color</CODE> can be an
2272: * <CODE>ExtendedColor</CODE>.
2273: * @param color the color
2274: */
2275: public void setColorFill(Color color) {
2276: PdfXConformanceImp.checkPDFXConformance(writer,
2277: PdfXConformanceImp.PDFXKEY_COLOR, color);
2278: int type = ExtendedColor.getType(color);
2279: switch (type) {
2280: case ExtendedColor.TYPE_GRAY: {
2281: setGrayFill(((GrayColor) color).getGray());
2282: break;
2283: }
2284: case ExtendedColor.TYPE_CMYK: {
2285: CMYKColor cmyk = (CMYKColor) color;
2286: setCMYKColorFillF(cmyk.getCyan(), cmyk.getMagenta(), cmyk
2287: .getYellow(), cmyk.getBlack());
2288: break;
2289: }
2290: case ExtendedColor.TYPE_SEPARATION: {
2291: SpotColor spot = (SpotColor) color;
2292: setColorFill(spot.getPdfSpotColor(), spot.getTint());
2293: break;
2294: }
2295: case ExtendedColor.TYPE_PATTERN: {
2296: PatternColor pat = (PatternColor) color;
2297: setPatternFill(pat.getPainter());
2298: break;
2299: }
2300: case ExtendedColor.TYPE_SHADING: {
2301: ShadingColor shading = (ShadingColor) color;
2302: setShadingFill(shading.getPdfShadingPattern());
2303: break;
2304: }
2305: default:
2306: setRGBColorFill(color.getRed(), color.getGreen(), color
2307: .getBlue());
2308: }
2309: }
2310:
2311: /** Sets the fill color to a spot color.
2312: * @param sp the spot color
2313: * @param tint the tint for the spot color. 0 is no color and 1
2314: * is 100% color
2315: */
2316: public void setColorFill(PdfSpotColor sp, float tint) {
2317: checkWriter();
2318: state.colorDetails = writer.addSimple(sp);
2319: PageResources prs = getPageResources();
2320: PdfName name = state.colorDetails.getColorName();
2321: name = prs.addColor(name, state.colorDetails
2322: .getIndirectReference());
2323: content.append(name.getBytes()).append(" cs ").append(tint)
2324: .append(" scn").append_i(separator);
2325: }
2326:
2327: /** Sets the stroke color to a spot color.
2328: * @param sp the spot color
2329: * @param tint the tint for the spot color. 0 is no color and 1
2330: * is 100% color
2331: */
2332: public void setColorStroke(PdfSpotColor sp, float tint) {
2333: checkWriter();
2334: state.colorDetails = writer.addSimple(sp);
2335: PageResources prs = getPageResources();
2336: PdfName name = state.colorDetails.getColorName();
2337: name = prs.addColor(name, state.colorDetails
2338: .getIndirectReference());
2339: content.append(name.getBytes()).append(" CS ").append(tint)
2340: .append(" SCN").append_i(separator);
2341: }
2342:
2343: /** Sets the fill color to a pattern. The pattern can be
2344: * colored or uncolored.
2345: * @param p the pattern
2346: */
2347: public void setPatternFill(PdfPatternPainter p) {
2348: if (p.isStencil()) {
2349: setPatternFill(p, p.getDefaultColor());
2350: return;
2351: }
2352: checkWriter();
2353: PageResources prs = getPageResources();
2354: PdfName name = writer.addSimplePattern(p);
2355: name = prs.addPattern(name, p.getIndirectReference());
2356: content.append(PdfName.PATTERN.getBytes()).append(" cs ")
2357: .append(name.getBytes()).append(" scn").append_i(
2358: separator);
2359: }
2360:
2361: /** Outputs the color values to the content.
2362: * @param color The color
2363: * @param tint the tint if it is a spot color, ignored otherwise
2364: */
2365: void outputColorNumbers(Color color, float tint) {
2366: PdfXConformanceImp.checkPDFXConformance(writer,
2367: PdfXConformanceImp.PDFXKEY_COLOR, color);
2368: int type = ExtendedColor.getType(color);
2369: switch (type) {
2370: case ExtendedColor.TYPE_RGB:
2371: content.append((float) (color.getRed()) / 0xFF);
2372: content.append(' ');
2373: content.append((float) (color.getGreen()) / 0xFF);
2374: content.append(' ');
2375: content.append((float) (color.getBlue()) / 0xFF);
2376: break;
2377: case ExtendedColor.TYPE_GRAY:
2378: content.append(((GrayColor) color).getGray());
2379: break;
2380: case ExtendedColor.TYPE_CMYK: {
2381: CMYKColor cmyk = (CMYKColor) color;
2382: content.append(cmyk.getCyan()).append(' ').append(
2383: cmyk.getMagenta());
2384: content.append(' ').append(cmyk.getYellow()).append(' ')
2385: .append(cmyk.getBlack());
2386: break;
2387: }
2388: case ExtendedColor.TYPE_SEPARATION:
2389: content.append(tint);
2390: break;
2391: default:
2392: throw new RuntimeException("Invalid color type.");
2393: }
2394: }
2395:
2396: /** Sets the fill color to an uncolored pattern.
2397: * @param p the pattern
2398: * @param color the color of the pattern
2399: */
2400: public void setPatternFill(PdfPatternPainter p, Color color) {
2401: if (ExtendedColor.getType(color) == ExtendedColor.TYPE_SEPARATION)
2402: setPatternFill(p, color, ((SpotColor) color).getTint());
2403: else
2404: setPatternFill(p, color, 0);
2405: }
2406:
2407: /** Sets the fill color to an uncolored pattern.
2408: * @param p the pattern
2409: * @param color the color of the pattern
2410: * @param tint the tint if the color is a spot color, ignored otherwise
2411: */
2412: public void setPatternFill(PdfPatternPainter p, Color color,
2413: float tint) {
2414: checkWriter();
2415: if (!p.isStencil())
2416: throw new RuntimeException(
2417: "An uncolored pattern was expected.");
2418: PageResources prs = getPageResources();
2419: PdfName name = writer.addSimplePattern(p);
2420: name = prs.addPattern(name, p.getIndirectReference());
2421: ColorDetails csDetail = writer
2422: .addSimplePatternColorspace(color);
2423: PdfName cName = prs.addColor(csDetail.getColorName(), csDetail
2424: .getIndirectReference());
2425: content.append(cName.getBytes()).append(" cs").append_i(
2426: separator);
2427: outputColorNumbers(color, tint);
2428: content.append(' ').append(name.getBytes()).append(" scn")
2429: .append_i(separator);
2430: }
2431:
2432: /** Sets the stroke color to an uncolored pattern.
2433: * @param p the pattern
2434: * @param color the color of the pattern
2435: */
2436: public void setPatternStroke(PdfPatternPainter p, Color color) {
2437: if (ExtendedColor.getType(color) == ExtendedColor.TYPE_SEPARATION)
2438: setPatternStroke(p, color, ((SpotColor) color).getTint());
2439: else
2440: setPatternStroke(p, color, 0);
2441: }
2442:
2443: /** Sets the stroke color to an uncolored pattern.
2444: * @param p the pattern
2445: * @param color the color of the pattern
2446: * @param tint the tint if the color is a spot color, ignored otherwise
2447: */
2448: public void setPatternStroke(PdfPatternPainter p, Color color,
2449: float tint) {
2450: checkWriter();
2451: if (!p.isStencil())
2452: throw new RuntimeException(
2453: "An uncolored pattern was expected.");
2454: PageResources prs = getPageResources();
2455: PdfName name = writer.addSimplePattern(p);
2456: name = prs.addPattern(name, p.getIndirectReference());
2457: ColorDetails csDetail = writer
2458: .addSimplePatternColorspace(color);
2459: PdfName cName = prs.addColor(csDetail.getColorName(), csDetail
2460: .getIndirectReference());
2461: content.append(cName.getBytes()).append(" CS").append_i(
2462: separator);
2463: outputColorNumbers(color, tint);
2464: content.append(' ').append(name.getBytes()).append(" SCN")
2465: .append_i(separator);
2466: }
2467:
2468: /** Sets the stroke color to a pattern. The pattern can be
2469: * colored or uncolored.
2470: * @param p the pattern
2471: */
2472: public void setPatternStroke(PdfPatternPainter p) {
2473: if (p.isStencil()) {
2474: setPatternStroke(p, p.getDefaultColor());
2475: return;
2476: }
2477: checkWriter();
2478: PageResources prs = getPageResources();
2479: PdfName name = writer.addSimplePattern(p);
2480: name = prs.addPattern(name, p.getIndirectReference());
2481: content.append(PdfName.PATTERN.getBytes()).append(" CS ")
2482: .append(name.getBytes()).append(" SCN").append_i(
2483: separator);
2484: }
2485:
2486: /**
2487: * Paints using a shading object.
2488: * @param shading the shading object
2489: */
2490: public void paintShading(PdfShading shading) {
2491: writer.addSimpleShading(shading);
2492: PageResources prs = getPageResources();
2493: PdfName name = prs.addShading(shading.getShadingName(), shading
2494: .getShadingReference());
2495: content.append(name.getBytes()).append(" sh").append_i(
2496: separator);
2497: ColorDetails details = shading.getColorDetails();
2498: if (details != null)
2499: prs.addColor(details.getColorName(), details
2500: .getIndirectReference());
2501: }
2502:
2503: /**
2504: * Paints using a shading pattern.
2505: * @param shading the shading pattern
2506: */
2507: public void paintShading(PdfShadingPattern shading) {
2508: paintShading(shading.getShading());
2509: }
2510:
2511: /**
2512: * Sets the shading fill pattern.
2513: * @param shading the shading pattern
2514: */
2515: public void setShadingFill(PdfShadingPattern shading) {
2516: writer.addSimpleShadingPattern(shading);
2517: PageResources prs = getPageResources();
2518: PdfName name = prs.addPattern(shading.getPatternName(), shading
2519: .getPatternReference());
2520: content.append(PdfName.PATTERN.getBytes()).append(" cs ")
2521: .append(name.getBytes()).append(" scn").append_i(
2522: separator);
2523: ColorDetails details = shading.getColorDetails();
2524: if (details != null)
2525: prs.addColor(details.getColorName(), details
2526: .getIndirectReference());
2527: }
2528:
2529: /**
2530: * Sets the shading stroke pattern
2531: * @param shading the shading pattern
2532: */
2533: public void setShadingStroke(PdfShadingPattern shading) {
2534: writer.addSimpleShadingPattern(shading);
2535: PageResources prs = getPageResources();
2536: PdfName name = prs.addPattern(shading.getPatternName(), shading
2537: .getPatternReference());
2538: content.append(PdfName.PATTERN.getBytes()).append(" CS ")
2539: .append(name.getBytes()).append(" SCN").append_i(
2540: separator);
2541: ColorDetails details = shading.getColorDetails();
2542: if (details != null)
2543: prs.addColor(details.getColorName(), details
2544: .getIndirectReference());
2545: }
2546:
2547: /** Check if we have a valid PdfWriter.
2548: *
2549: */
2550: protected void checkWriter() {
2551: if (writer == null)
2552: throw new NullPointerException(
2553: "The writer in PdfContentByte is null.");
2554: }
2555:
2556: /**
2557: * Show an array of text.
2558: * @param text array of text
2559: */
2560: public void showText(PdfTextArray text) {
2561: if (state.fontDetails == null)
2562: throw new NullPointerException(
2563: "Font and size must be set before writing any text");
2564: content.append("[");
2565: ArrayList arrayList = text.getArrayList();
2566: boolean lastWasNumber = false;
2567: for (int k = 0; k < arrayList.size(); ++k) {
2568: Object obj = arrayList.get(k);
2569: if (obj instanceof String) {
2570: showText2((String) obj);
2571: lastWasNumber = false;
2572: } else {
2573: if (lastWasNumber)
2574: content.append(' ');
2575: else
2576: lastWasNumber = true;
2577: content.append(((Float) obj).floatValue());
2578: }
2579: }
2580: content.append("]TJ").append_i(separator);
2581: }
2582:
2583: /**
2584: * Gets the <CODE>PdfWriter</CODE> in use by this object.
2585: * @return the <CODE>PdfWriter</CODE> in use by this object
2586: */
2587: public PdfWriter getPdfWriter() {
2588: return writer;
2589: }
2590:
2591: /**
2592: * Gets the <CODE>PdfDocument</CODE> in use by this object.
2593: * @return the <CODE>PdfDocument</CODE> in use by this object
2594: */
2595: public PdfDocument getPdfDocument() {
2596: return pdf;
2597: }
2598:
2599: /**
2600: * Implements a link to other part of the document. The jump will
2601: * be made to a local destination with the same name, that must exist.
2602: * @param name the name for this link
2603: * @param llx the lower left x corner of the activation area
2604: * @param lly the lower left y corner of the activation area
2605: * @param urx the upper right x corner of the activation area
2606: * @param ury the upper right y corner of the activation area
2607: */
2608: public void localGoto(String name, float llx, float lly, float urx,
2609: float ury) {
2610: pdf.localGoto(name, llx, lly, urx, ury);
2611: }
2612:
2613: /**
2614: * The local destination to where a local goto with the same
2615: * name will jump.
2616: * @param name the name of this local destination
2617: * @param destination the <CODE>PdfDestination</CODE> with the jump coordinates
2618: * @return <CODE>true</CODE> if the local destination was added,
2619: * <CODE>false</CODE> if a local destination with the same name
2620: * already exists
2621: */
2622: public boolean localDestination(String name,
2623: PdfDestination destination) {
2624: return pdf.localDestination(name, destination);
2625: }
2626:
2627: /**
2628: * Gets a duplicate of this <CODE>PdfContentByte</CODE>. All
2629: * the members are copied by reference but the buffer stays different.
2630: *
2631: * @return a copy of this <CODE>PdfContentByte</CODE>
2632: */
2633: public PdfContentByte getDuplicate() {
2634: return new PdfContentByte(writer);
2635: }
2636:
2637: /**
2638: * Implements a link to another document.
2639: * @param filename the filename for the remote document
2640: * @param name the name to jump to
2641: * @param llx the lower left x corner of the activation area
2642: * @param lly the lower left y corner of the activation area
2643: * @param urx the upper right x corner of the activation area
2644: * @param ury the upper right y corner of the activation area
2645: */
2646: public void remoteGoto(String filename, String name, float llx,
2647: float lly, float urx, float ury) {
2648: pdf.remoteGoto(filename, name, llx, lly, urx, ury);
2649: }
2650:
2651: /**
2652: * Implements a link to another document.
2653: * @param filename the filename for the remote document
2654: * @param page the page to jump to
2655: * @param llx the lower left x corner of the activation area
2656: * @param lly the lower left y corner of the activation area
2657: * @param urx the upper right x corner of the activation area
2658: * @param ury the upper right y corner of the activation area
2659: */
2660: public void remoteGoto(String filename, int page, float llx,
2661: float lly, float urx, float ury) {
2662: pdf.remoteGoto(filename, page, llx, lly, urx, ury);
2663: }
2664:
2665: /**
2666: * Adds a round rectangle to the current path.
2667: *
2668: * @param x x-coordinate of the starting point
2669: * @param y y-coordinate of the starting point
2670: * @param w width
2671: * @param h height
2672: * @param r radius of the arc corner
2673: */
2674: public void roundRectangle(float x, float y, float w, float h,
2675: float r) {
2676: if (w < 0) {
2677: x += w;
2678: w = -w;
2679: }
2680: if (h < 0) {
2681: y += h;
2682: h = -h;
2683: }
2684: if (r < 0)
2685: r = -r;
2686: float b = 0.4477f;
2687: moveTo(x + r, y);
2688: lineTo(x + w - r, y);
2689: curveTo(x + w - r * b, y, x + w, y + r * b, x + w, y + r);
2690: lineTo(x + w, y + h - r);
2691: curveTo(x + w, y + h - r * b, x + w - r * b, y + h, x + w - r,
2692: y + h);
2693: lineTo(x + r, y + h);
2694: curveTo(x + r * b, y + h, x, y + h - r * b, x, y + h - r);
2695: lineTo(x, y + r);
2696: curveTo(x, y + r * b, x + r * b, y, x + r, y);
2697: }
2698:
2699: /** Implements an action in an area.
2700: * @param action the <CODE>PdfAction</CODE>
2701: * @param llx the lower left x corner of the activation area
2702: * @param lly the lower left y corner of the activation area
2703: * @param urx the upper right x corner of the activation area
2704: * @param ury the upper right y corner of the activation area
2705: */
2706: public void setAction(PdfAction action, float llx, float lly,
2707: float urx, float ury) {
2708: pdf.setAction(action, llx, lly, urx, ury);
2709: }
2710:
2711: /** Outputs a <CODE>String</CODE> directly to the content.
2712: * @param s the <CODE>String</CODE>
2713: */
2714: public void setLiteral(String s) {
2715: content.append(s);
2716: }
2717:
2718: /** Outputs a <CODE>char</CODE> directly to the content.
2719: * @param c the <CODE>char</CODE>
2720: */
2721: public void setLiteral(char c) {
2722: content.append(c);
2723: }
2724:
2725: /** Outputs a <CODE>float</CODE> directly to the content.
2726: * @param n the <CODE>float</CODE>
2727: */
2728: public void setLiteral(float n) {
2729: content.append(n);
2730: }
2731:
2732: /** Throws an error if it is a pattern.
2733: * @param t the object to check
2734: */
2735: void checkNoPattern(PdfTemplate t) {
2736: if (t.getType() == PdfTemplate.TYPE_PATTERN)
2737: throw new RuntimeException(
2738: "Invalid use of a pattern. A template was expected.");
2739: }
2740:
2741: /**
2742: * Draws a TextField.
2743: * @param llx
2744: * @param lly
2745: * @param urx
2746: * @param ury
2747: * @param on
2748: */
2749: public void drawRadioField(float llx, float lly, float urx,
2750: float ury, boolean on) {
2751: if (llx > urx) {
2752: float x = llx;
2753: llx = urx;
2754: urx = x;
2755: }
2756: if (lly > ury) {
2757: float y = lly;
2758: lly = ury;
2759: ury = y;
2760: }
2761: // silver circle
2762: setLineWidth(1);
2763: setLineCap(1);
2764: setColorStroke(new Color(0xC0, 0xC0, 0xC0));
2765: arc(llx + 1f, lly + 1f, urx - 1f, ury - 1f, 0f, 360f);
2766: stroke();
2767: // gray circle-segment
2768: setLineWidth(1);
2769: setLineCap(1);
2770: setColorStroke(new Color(0xA0, 0xA0, 0xA0));
2771: arc(llx + 0.5f, lly + 0.5f, urx - 0.5f, ury - 0.5f, 45, 180);
2772: stroke();
2773: // black circle-segment
2774: setLineWidth(1);
2775: setLineCap(1);
2776: setColorStroke(new Color(0x00, 0x00, 0x00));
2777: arc(llx + 1.5f, lly + 1.5f, urx - 1.5f, ury - 1.5f, 45, 180);
2778: stroke();
2779: if (on) {
2780: // gray circle
2781: setLineWidth(1);
2782: setLineCap(1);
2783: setColorFill(new Color(0x00, 0x00, 0x00));
2784: arc(llx + 4f, lly + 4f, urx - 4f, ury - 4f, 0, 360);
2785: fill();
2786: }
2787: }
2788:
2789: /**
2790: * Draws a TextField.
2791: * @param llx
2792: * @param lly
2793: * @param urx
2794: * @param ury
2795: */
2796: public void drawTextField(float llx, float lly, float urx, float ury) {
2797: if (llx > urx) {
2798: float x = llx;
2799: llx = urx;
2800: urx = x;
2801: }
2802: if (lly > ury) {
2803: float y = lly;
2804: lly = ury;
2805: ury = y;
2806: }
2807: // silver rectangle not filled
2808: setColorStroke(new Color(0xC0, 0xC0, 0xC0));
2809: setLineWidth(1);
2810: setLineCap(0);
2811: rectangle(llx, lly, urx - llx, ury - lly);
2812: stroke();
2813: // white rectangle filled
2814: setLineWidth(1);
2815: setLineCap(0);
2816: setColorFill(new Color(0xFF, 0xFF, 0xFF));
2817: rectangle(llx + 0.5f, lly + 0.5f, urx - llx - 1f, ury - lly
2818: - 1f);
2819: fill();
2820: // silver lines
2821: setColorStroke(new Color(0xC0, 0xC0, 0xC0));
2822: setLineWidth(1);
2823: setLineCap(0);
2824: moveTo(llx + 1f, lly + 1.5f);
2825: lineTo(urx - 1.5f, lly + 1.5f);
2826: lineTo(urx - 1.5f, ury - 1f);
2827: stroke();
2828: // gray lines
2829: setColorStroke(new Color(0xA0, 0xA0, 0xA0));
2830: setLineWidth(1);
2831: setLineCap(0);
2832: moveTo(llx + 1f, lly + 1);
2833: lineTo(llx + 1f, ury - 1f);
2834: lineTo(urx - 1f, ury - 1f);
2835: stroke();
2836: // black lines
2837: setColorStroke(new Color(0x00, 0x00, 0x00));
2838: setLineWidth(1);
2839: setLineCap(0);
2840: moveTo(llx + 2f, lly + 2f);
2841: lineTo(llx + 2f, ury - 2f);
2842: lineTo(urx - 2f, ury - 2f);
2843: stroke();
2844: }
2845:
2846: /**
2847: * Draws a button.
2848: * @param llx
2849: * @param lly
2850: * @param urx
2851: * @param ury
2852: * @param text
2853: * @param bf
2854: * @param size
2855: */
2856: public void drawButton(float llx, float lly, float urx, float ury,
2857: String text, BaseFont bf, float size) {
2858: if (llx > urx) {
2859: float x = llx;
2860: llx = urx;
2861: urx = x;
2862: }
2863: if (lly > ury) {
2864: float y = lly;
2865: lly = ury;
2866: ury = y;
2867: }
2868: // black rectangle not filled
2869: setColorStroke(new Color(0x00, 0x00, 0x00));
2870: setLineWidth(1);
2871: setLineCap(0);
2872: rectangle(llx, lly, urx - llx, ury - lly);
2873: stroke();
2874: // silver rectangle filled
2875: setLineWidth(1);
2876: setLineCap(0);
2877: setColorFill(new Color(0xC0, 0xC0, 0xC0));
2878: rectangle(llx + 0.5f, lly + 0.5f, urx - llx - 1f, ury - lly
2879: - 1f);
2880: fill();
2881: // white lines
2882: setColorStroke(new Color(0xFF, 0xFF, 0xFF));
2883: setLineWidth(1);
2884: setLineCap(0);
2885: moveTo(llx + 1f, lly + 1f);
2886: lineTo(llx + 1f, ury - 1f);
2887: lineTo(urx - 1f, ury - 1f);
2888: stroke();
2889: // dark grey lines
2890: setColorStroke(new Color(0xA0, 0xA0, 0xA0));
2891: setLineWidth(1);
2892: setLineCap(0);
2893: moveTo(llx + 1f, lly + 1f);
2894: lineTo(urx - 1f, lly + 1f);
2895: lineTo(urx - 1f, ury - 1f);
2896: stroke();
2897: // text
2898: resetRGBColorFill();
2899: beginText();
2900: setFontAndSize(bf, size);
2901: showTextAligned(PdfContentByte.ALIGN_CENTER, text, llx
2902: + (urx - llx) / 2, lly + (ury - lly - size) / 2, 0);
2903: endText();
2904: }
2905:
2906: /** Gets a <CODE>Graphics2D</CODE> to write on. The graphics
2907: * are translated to PDF commands as shapes. No PDF fonts will appear.
2908: * @param width the width of the panel
2909: * @param height the height of the panel
2910: * @return a <CODE>Graphics2D</CODE>
2911: */
2912: public java.awt.Graphics2D createGraphicsShapes(float width,
2913: float height) {
2914: return new PdfGraphics2D(this , width, height, null, true,
2915: false, 0);
2916: }
2917:
2918: /** Gets a <CODE>Graphics2D</CODE> to print on. The graphics
2919: * are translated to PDF commands as shapes. No PDF fonts will appear.
2920: * @param width the width of the panel
2921: * @param height the height of the panel
2922: * @param printerJob a printer job
2923: * @return a <CODE>Graphics2D</CODE>
2924: */
2925: public java.awt.Graphics2D createPrinterGraphicsShapes(float width,
2926: float height, PrinterJob printerJob) {
2927: return new PdfPrinterGraphics2D(this , width, height, null,
2928: true, false, 0, printerJob);
2929: }
2930:
2931: /** Gets a <CODE>Graphics2D</CODE> to write on. The graphics
2932: * are translated to PDF commands.
2933: * @param width the width of the panel
2934: * @param height the height of the panel
2935: * @return a <CODE>Graphics2D</CODE>
2936: */
2937: public java.awt.Graphics2D createGraphics(float width, float height) {
2938: return new PdfGraphics2D(this , width, height, null, false,
2939: false, 0);
2940: }
2941:
2942: /** Gets a <CODE>Graphics2D</CODE> to print on. The graphics
2943: * are translated to PDF commands.
2944: * @param width the width of the panel
2945: * @param height the height of the panel
2946: * @param printerJob
2947: * @return a <CODE>Graphics2D</CODE>
2948: */
2949: public java.awt.Graphics2D createPrinterGraphics(float width,
2950: float height, PrinterJob printerJob) {
2951: return new PdfPrinterGraphics2D(this , width, height, null,
2952: false, false, 0, printerJob);
2953: }
2954:
2955: /** Gets a <CODE>Graphics2D</CODE> to write on. The graphics
2956: * are translated to PDF commands.
2957: * @param width the width of the panel
2958: * @param height the height of the panel
2959: * @param convertImagesToJPEG
2960: * @param quality
2961: * @return a <CODE>Graphics2D</CODE>
2962: */
2963: public java.awt.Graphics2D createGraphics(float width,
2964: float height, boolean convertImagesToJPEG, float quality) {
2965: return new PdfGraphics2D(this , width, height, null, false,
2966: convertImagesToJPEG, quality);
2967: }
2968:
2969: /** Gets a <CODE>Graphics2D</CODE> to print on. The graphics
2970: * are translated to PDF commands.
2971: * @param width the width of the panel
2972: * @param height the height of the panel
2973: * @param convertImagesToJPEG
2974: * @param quality
2975: * @param printerJob
2976: * @return a <CODE>Graphics2D</CODE>
2977: */
2978: public java.awt.Graphics2D createPrinterGraphics(float width,
2979: float height, boolean convertImagesToJPEG, float quality,
2980: PrinterJob printerJob) {
2981: return new PdfPrinterGraphics2D(this , width, height, null,
2982: false, convertImagesToJPEG, quality, printerJob);
2983: }
2984:
2985: /** Gets a <CODE>Graphics2D</CODE> to print on. The graphics
2986: * are translated to PDF commands.
2987: * @param width
2988: * @param height
2989: * @param convertImagesToJPEG
2990: * @param quality
2991: * @return A Graphics2D object
2992: */
2993: public java.awt.Graphics2D createGraphicsShapes(float width,
2994: float height, boolean convertImagesToJPEG, float quality) {
2995: return new PdfGraphics2D(this , width, height, null, true,
2996: convertImagesToJPEG, quality);
2997: }
2998:
2999: /** Gets a <CODE>Graphics2D</CODE> to print on. The graphics
3000: * are translated to PDF commands.
3001: * @param width
3002: * @param height
3003: * @param convertImagesToJPEG
3004: * @param quality
3005: * @param printerJob
3006: * @return a Graphics2D object
3007: */
3008: public java.awt.Graphics2D createPrinterGraphicsShapes(float width,
3009: float height, boolean convertImagesToJPEG, float quality,
3010: PrinterJob printerJob) {
3011: return new PdfPrinterGraphics2D(this , width, height, null,
3012: true, convertImagesToJPEG, quality, printerJob);
3013: }
3014:
3015: /** Gets a <CODE>Graphics2D</CODE> to write on. The graphics
3016: * are translated to PDF commands.
3017: * @param width the width of the panel
3018: * @param height the height of the panel
3019: * @param fontMapper the mapping from awt fonts to <CODE>BaseFont</CODE>
3020: * @return a <CODE>Graphics2D</CODE>
3021: */
3022: public java.awt.Graphics2D createGraphics(float width,
3023: float height, FontMapper fontMapper) {
3024: return new PdfGraphics2D(this , width, height, fontMapper,
3025: false, false, 0);
3026: }
3027:
3028: /** Gets a <CODE>Graphics2D</CODE> to print on. The graphics
3029: * are translated to PDF commands.
3030: * @param width the width of the panel
3031: * @param height the height of the panel
3032: * @param fontMapper the mapping from awt fonts to <CODE>BaseFont</CODE>
3033: * @param printerJob a printer job
3034: * @return a <CODE>Graphics2D</CODE>
3035: */
3036: public java.awt.Graphics2D createPrinterGraphics(float width,
3037: float height, FontMapper fontMapper, PrinterJob printerJob) {
3038: return new PdfPrinterGraphics2D(this , width, height,
3039: fontMapper, false, false, 0, printerJob);
3040: }
3041:
3042: /** Gets a <CODE>Graphics2D</CODE> to write on. The graphics
3043: * are translated to PDF commands.
3044: * @param width the width of the panel
3045: * @param height the height of the panel
3046: * @param fontMapper the mapping from awt fonts to <CODE>BaseFont</CODE>
3047: * @param convertImagesToJPEG converts awt images to jpeg before inserting in pdf
3048: * @param quality the quality of the jpeg
3049: * @return a <CODE>Graphics2D</CODE>
3050: */
3051: public java.awt.Graphics2D createGraphics(float width,
3052: float height, FontMapper fontMapper,
3053: boolean convertImagesToJPEG, float quality) {
3054: return new PdfGraphics2D(this , width, height, fontMapper,
3055: false, convertImagesToJPEG, quality);
3056: }
3057:
3058: /** Gets a <CODE>Graphics2D</CODE> to print on. The graphics
3059: * are translated to PDF commands.
3060: * @param width the width of the panel
3061: * @param height the height of the panel
3062: * @param fontMapper the mapping from awt fonts to <CODE>BaseFont</CODE>
3063: * @param convertImagesToJPEG converts awt images to jpeg before inserting in pdf
3064: * @param quality the quality of the jpeg
3065: * @param printerJob a printer job
3066: * @return a <CODE>Graphics2D</CODE>
3067: */
3068: public java.awt.Graphics2D createPrinterGraphics(float width,
3069: float height, FontMapper fontMapper,
3070: boolean convertImagesToJPEG, float quality,
3071: PrinterJob printerJob) {
3072: return new PdfPrinterGraphics2D(this , width, height,
3073: fontMapper, false, convertImagesToJPEG, quality,
3074: printerJob);
3075: }
3076:
3077: PageResources getPageResources() {
3078: return pdf.getPageResources();
3079: }
3080:
3081: /** Sets the graphic state
3082: * @param gstate the graphic state
3083: */
3084: public void setGState(PdfGState gstate) {
3085: PdfObject obj[] = writer.addSimpleExtGState(gstate);
3086: PageResources prs = getPageResources();
3087: PdfName name = prs.addExtGState((PdfName) obj[0],
3088: (PdfIndirectReference) obj[1]);
3089: content.append(name.getBytes()).append(" gs").append_i(
3090: separator);
3091: }
3092:
3093: /**
3094: * Begins a graphic block whose visibility is controled by the <CODE>layer</CODE>.
3095: * Blocks can be nested. Each block must be terminated by an {@link #endLayer()}.<p>
3096: * Note that nested layers with {@link PdfLayer#addChild(PdfLayer)} only require a single
3097: * call to this method and a single call to {@link #endLayer()}; all the nesting control
3098: * is built in.
3099: * @param layer the layer
3100: */
3101: public void beginLayer(PdfOCG layer) {
3102: if ((layer instanceof PdfLayer)
3103: && ((PdfLayer) layer).getTitle() != null)
3104: throw new IllegalArgumentException("A title is not a layer");
3105: if (layerDepth == null)
3106: layerDepth = new ArrayList();
3107: if (layer instanceof PdfLayerMembership) {
3108: layerDepth.add(new Integer(1));
3109: beginLayer2(layer);
3110: return;
3111: }
3112: int n = 0;
3113: PdfLayer la = (PdfLayer) layer;
3114: while (la != null) {
3115: if (la.getTitle() == null) {
3116: beginLayer2(la);
3117: ++n;
3118: }
3119: la = la.getParent();
3120: }
3121: layerDepth.add(new Integer(n));
3122: }
3123:
3124: private void beginLayer2(PdfOCG layer) {
3125: PdfName name = (PdfName) writer.addSimpleProperty(layer, layer
3126: .getRef())[0];
3127: PageResources prs = getPageResources();
3128: name = prs.addProperty(name, layer.getRef());
3129: content.append("/OC ").append(name.getBytes()).append(" BDC")
3130: .append_i(separator);
3131: }
3132:
3133: /**
3134: * Ends a layer controled graphic block. It will end the most recent open block.
3135: */
3136: public void endLayer() {
3137: int n = 1;
3138: if (layerDepth != null && !layerDepth.isEmpty()) {
3139: n = ((Integer) layerDepth.get(layerDepth.size() - 1))
3140: .intValue();
3141: layerDepth.remove(layerDepth.size() - 1);
3142: }
3143: while (n-- > 0)
3144: content.append("EMC").append_i(separator);
3145: }
3146:
3147: /** Concatenates a transformation to the current transformation
3148: * matrix.
3149: * @param af the transformation
3150: */
3151: public void transform(AffineTransform af) {
3152: double arr[] = new double[6];
3153: af.getMatrix(arr);
3154: content.append(arr[0]).append(' ').append(arr[1]).append(' ')
3155: .append(arr[2]).append(' ');
3156: content.append(arr[3]).append(' ').append(arr[4]).append(' ')
3157: .append(arr[5]).append(" cm").append_i(separator);
3158: }
3159:
3160: void addAnnotation(PdfAnnotation annot) {
3161: writer.addAnnotation(annot);
3162: }
3163:
3164: /**
3165: * Sets the default colorspace.
3166: * @param name the name of the colorspace. It can be <CODE>PdfName.DEFAULTGRAY</CODE>, <CODE>PdfName.DEFAULTRGB</CODE>
3167: * or <CODE>PdfName.DEFAULTCMYK</CODE>
3168: * @param obj the colorspace. A <CODE>null</CODE> or <CODE>PdfNull</CODE> removes any colorspace with the same name
3169: */
3170: public void setDefaultColorspace(PdfName name, PdfObject obj) {
3171: PageResources prs = getPageResources();
3172: prs.addDefaultColor(name, obj);
3173: }
3174:
3175: /**
3176: * Begins a marked content sequence. This sequence will be tagged with the structure <CODE>struc</CODE>.
3177: * The same structure can be used several times to connect text that belongs to the same logical segment
3178: * but is in a different location, like the same paragraph crossing to another page, for example.
3179: * @param struc the tagging structure
3180: */
3181: public void beginMarkedContentSequence(PdfStructureElement struc) {
3182: PdfObject obj = struc.get(PdfName.K);
3183: int mark = pdf.getMarkPoint();
3184: if (obj != null) {
3185: PdfArray ar = null;
3186: if (obj.isNumber()) {
3187: ar = new PdfArray();
3188: ar.add(obj);
3189: struc.put(PdfName.K, ar);
3190: } else if (obj.isArray()) {
3191: ar = (PdfArray) obj;
3192: if (!((PdfObject) ar.getArrayList().get(0)).isNumber())
3193: throw new IllegalArgumentException(
3194: "The structure has kids.");
3195: } else
3196: throw new IllegalArgumentException(
3197: "Unknown object at /K "
3198: + obj.getClass().toString());
3199: PdfDictionary dic = new PdfDictionary(PdfName.MCR);
3200: dic.put(PdfName.PG, writer.getCurrentPage());
3201: dic.put(PdfName.MCID, new PdfNumber(mark));
3202: ar.add(dic);
3203: struc.setPageMark(writer.getPageNumber() - 1, -1);
3204: } else {
3205: struc.setPageMark(writer.getPageNumber() - 1, mark);
3206: struc.put(PdfName.PG, writer.getCurrentPage());
3207: }
3208: pdf.incMarkPoint();
3209: content.append(struc.get(PdfName.S).getBytes()).append(
3210: " <</MCID ").append(mark).append(">> BDC").append_i(
3211: separator);
3212: }
3213:
3214: /**
3215: * Ends a marked content sequence
3216: */
3217: public void endMarkedContentSequence() {
3218: content.append("EMC").append_i(separator);
3219: }
3220:
3221: /**
3222: * Begins a marked content sequence. If property is <CODE>null</CODE> the mark will be of the type
3223: * <CODE>BMC</CODE> otherwise it will be <CODE>BDC</CODE>.
3224: * @param tag the tag
3225: * @param property the property
3226: * @param inline <CODE>true</CODE> to include the property in the content or <CODE>false</CODE>
3227: * to include the property in the resource dictionary with the possibility of reusing
3228: */
3229: public void beginMarkedContentSequence(PdfName tag,
3230: PdfDictionary property, boolean inline) {
3231: if (property == null) {
3232: content.append(tag.getBytes()).append(" BMC").append_i(
3233: separator);
3234: return;
3235: }
3236: content.append(tag.getBytes()).append(' ');
3237: if (inline)
3238: try {
3239: property.toPdf(writer, content);
3240: } catch (Exception e) {
3241: throw new ExceptionConverter(e);
3242: }
3243: else {
3244: PdfObject[] objs;
3245: if (writer.propertyExists(property))
3246: objs = writer.addSimpleProperty(property, null);
3247: else
3248: objs = writer.addSimpleProperty(property, writer
3249: .getPdfIndirectReference());
3250: PdfName name = (PdfName) objs[0];
3251: PageResources prs = getPageResources();
3252: name = prs
3253: .addProperty(name, (PdfIndirectReference) objs[1]);
3254: content.append(name.getBytes());
3255: }
3256: content.append(" BDC").append_i(separator);
3257: }
3258:
3259: /**
3260: * This is just a shorthand to <CODE>beginMarkedContentSequence(tag, null, false)</CODE>.
3261: * @param tag the tag
3262: */
3263: public void beginMarkedContentSequence(PdfName tag) {
3264: beginMarkedContentSequence(tag, null, false);
3265: }
3266: }
|