Save This Page
Home » iText-src-2.1.3 » com.lowagie » text » pdf » [javadoc | source]
    1   /*
    2    * $Id: PdfContentByte.java 3427 2008-05-24 18:32:31Z xlv $
    3    *
    4    * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
    5    *
    6    * The contents of this file are subject to the Mozilla Public License Version 1.1
    7    * (the "License"); you may not use this file except in compliance with the License.
    8    * You may obtain a copy of the License at http://www.mozilla.org/MPL/
    9    *
   10    * Software distributed under the License is distributed on an "AS IS" basis,
   11    * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
   12    * for the specific language governing rights and limitations under the License.
   13    *
   14    * The Original Code is 'iText, a free JAVA-PDF library'.
   15    *
   16    * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
   17    * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
   18    * All Rights Reserved.
   19    * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
   20    * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
   21    *
   22    * Contributor(s): all the names of the contributors are added in the source code
   23    * where applicable.
   24    *
   25    * Alternatively, the contents of this file may be used under the terms of the
   26    * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
   27    * provisions of LGPL are applicable instead of those above.  If you wish to
   28    * allow use of your version of this file only under the terms of the LGPL
   29    * License and not to allow others to use your version of this file under
   30    * the MPL, indicate your decision by deleting the provisions above and
   31    * replace them with the notice and other provisions required by the LGPL.
   32    * If you do not delete the provisions above, a recipient may use your version
   33    * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
   34    *
   35    * This library is free software; you can redistribute it and/or modify it
   36    * under the terms of the MPL as stated above or under the terms of the GNU
   37    * Library General Public License as published by the Free Software Foundation;
   38    * either version 2 of the License, or any later version.
   39    *
   40    * This library is distributed in the hope that it will be useful, but WITHOUT
   41    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
   42    * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
   43    * details.
   44    *
   45    * If you didn't download this code from the following link, you should check if
   46    * you aren't using an obsolete version:
   47    * http://www.lowagie.com/iText/
   48    */
   49   
   50   package com.lowagie.text.pdf;
   51   import java.awt.Color;
   52   import java.awt.geom.AffineTransform;
   53   import java.awt.print.PrinterJob;
   54   import java.util.ArrayList;
   55   import java.util.HashMap;
   56   import java.util.Iterator;
   57   
   58   import com.lowagie.text.Annotation;
   59   import com.lowagie.text.DocumentException;
   60   import com.lowagie.text.Element;
   61   import com.lowagie.text.ExceptionConverter;
   62   import com.lowagie.text.Image;
   63   import com.lowagie.text.Rectangle;
   64   import com.lowagie.text.pdf.internal.PdfAnnotationsImp;
   65   import com.lowagie.text.pdf.internal.PdfXConformanceImp;
   66   
   67   /**
   68    * <CODE>PdfContentByte</CODE> is an object containing the user positioned
   69    * text and graphic contents of a page. It knows how to apply the proper
   70    * font encoding.
   71    */
   72   
   73   public class PdfContentByte {
   74       
   75       /**
   76        * This class keeps the graphic state of the current page
   77        */
   78       
   79       static class GraphicState {
   80           
   81           /** This is the font in use */
   82           FontDetails fontDetails;
   83           
   84           /** This is the color in use */
   85           ColorDetails colorDetails;
   86           
   87           /** This is the font size in use */
   88           float size;
   89           
   90           /** The x position of the text line matrix. */
   91           protected float xTLM = 0;
   92           /** The y position of the text line matrix. */
   93           protected float yTLM = 0;
   94           
   95           /** The current text leading. */
   96           protected float leading = 0;
   97   
   98           /** The current horizontal scaling */
   99           protected float scale = 100;
  100   
  101           /** The current character spacing */
  102           protected float charSpace = 0;
  103   
  104           /** The current word spacing */
  105           protected float wordSpace = 0;
  106           
  107           GraphicState() {
  108           }
  109   
  110           GraphicState(GraphicState cp) {
  111               fontDetails = cp.fontDetails;
  112               colorDetails = cp.colorDetails;
  113               size = cp.size;
  114               xTLM = cp.xTLM;
  115               yTLM = cp.yTLM;
  116               leading = cp.leading;
  117               scale = cp.scale;
  118               charSpace = cp.charSpace;
  119               wordSpace = cp.wordSpace;
  120           }
  121       }
  122       
  123       /** The alignment is center */
  124       public static final int ALIGN_CENTER = Element.ALIGN_CENTER;
  125       
  126       /** The alignment is left */
  127       public static final int ALIGN_LEFT = Element.ALIGN_LEFT;
  128       
  129       /** The alignment is right */
  130       public static final int ALIGN_RIGHT = Element.ALIGN_RIGHT;
  131   
  132       /** A possible line cap value */
  133       public static final int LINE_CAP_BUTT = 0;
  134       /** A possible line cap value */
  135       public static final int LINE_CAP_ROUND = 1;
  136       /** A possible line cap value */
  137       public static final int LINE_CAP_PROJECTING_SQUARE = 2;
  138   
  139       /** A possible line join value */
  140       public static final int LINE_JOIN_MITER = 0;
  141       /** A possible line join value */
  142       public static final int LINE_JOIN_ROUND = 1;
  143       /** A possible line join value */
  144       public static final int LINE_JOIN_BEVEL = 2;
  145   
  146       /** A possible text rendering value */
  147       public static final int TEXT_RENDER_MODE_FILL = 0;
  148       /** A possible text rendering value */
  149       public static final int TEXT_RENDER_MODE_STROKE = 1;
  150       /** A possible text rendering value */
  151       public static final int TEXT_RENDER_MODE_FILL_STROKE = 2;
  152       /** A possible text rendering value */
  153       public static final int TEXT_RENDER_MODE_INVISIBLE = 3;
  154       /** A possible text rendering value */
  155       public static final int TEXT_RENDER_MODE_FILL_CLIP = 4;
  156       /** A possible text rendering value */
  157       public static final int TEXT_RENDER_MODE_STROKE_CLIP = 5;
  158       /** A possible text rendering value */
  159       public static final int TEXT_RENDER_MODE_FILL_STROKE_CLIP = 6;
  160       /** A possible text rendering value */
  161       public static final int TEXT_RENDER_MODE_CLIP = 7;
  162       
  163       private static final float[] unitRect = {0, 0, 0, 1, 1, 0, 1, 1};
  164       // membervariables
  165       
  166       /** This is the actual content */
  167       protected ByteBuffer content = new ByteBuffer();
  168       
  169       /** This is the writer */
  170       protected PdfWriter writer;
  171       
  172       /** This is the PdfDocument */
  173       protected PdfDocument pdf;
  174       
  175       /** This is the GraphicState in use */
  176       protected GraphicState state = new GraphicState();
  177       
  178       /** The list were we save/restore the state */
  179       protected ArrayList stateList = new ArrayList();
  180       
  181       /** The list were we save/restore the layer depth */
  182       protected ArrayList layerDepth;
  183       
  184       /** The separator between commands.
  185        */
  186       protected int separator = '\n';
  187       
  188       private static HashMap abrev = new HashMap();
  189       
  190       static {
  191           abrev.put(PdfName.BITSPERCOMPONENT, "/BPC ");
  192           abrev.put(PdfName.COLORSPACE, "/CS ");
  193           abrev.put(PdfName.DECODE, "/D ");
  194           abrev.put(PdfName.DECODEPARMS, "/DP ");
  195           abrev.put(PdfName.FILTER, "/F ");
  196           abrev.put(PdfName.HEIGHT, "/H ");
  197           abrev.put(PdfName.IMAGEMASK, "/IM ");
  198           abrev.put(PdfName.INTENT, "/Intent ");
  199           abrev.put(PdfName.INTERPOLATE, "/I ");
  200           abrev.put(PdfName.WIDTH, "/W ");
  201       }
  202       
  203       // constructors
  204       
  205       /**
  206        * Constructs a new <CODE>PdfContentByte</CODE>-object.
  207        *
  208        * @param wr the writer associated to this content
  209        */
  210       
  211       public PdfContentByte(PdfWriter wr) {
  212           if (wr != null) {
  213               writer = wr;
  214               pdf = writer.getPdfDocument();
  215           }
  216       }
  217       
  218       // methods to get the content of this object
  219       
  220       /**
  221        * Returns the <CODE>String</CODE> representation of this <CODE>PdfContentByte</CODE>-object.
  222        *
  223        * @return      a <CODE>String</CODE>
  224        */
  225       
  226       public String toString() {
  227           return content.toString();
  228       }
  229       
  230       /**
  231        * Gets the internal buffer.
  232        * @return the internal buffer
  233        */
  234       public ByteBuffer getInternalBuffer() {
  235           return content;
  236       }
  237       
  238       /** Returns the PDF representation of this <CODE>PdfContentByte</CODE>-object.
  239        *
  240        * @param writer the <CODE>PdfWriter</CODE>
  241        * @return a <CODE>byte</CODE> array with the representation
  242        */
  243       
  244       public byte[] toPdf(PdfWriter writer) {
  245           return content.toByteArray();
  246       }
  247       
  248       // methods to add graphical content
  249       
  250       /**
  251        * Adds the content of another <CODE>PdfContent</CODE>-object to this object.
  252        *
  253        * @param       other       another <CODE>PdfByteContent</CODE>-object
  254        */
  255       
  256       public void add(PdfContentByte other) {
  257           if (other.writer != null && writer != other.writer)
  258               throw new RuntimeException("Inconsistent writers. Are you mixing two documents?");
  259           content.append(other.content);
  260       }
  261       
  262       /**
  263        * Gets the x position of the text line matrix.
  264        *
  265        * @return the x position of the text line matrix
  266        */
  267       public float getXTLM() {
  268           return state.xTLM;
  269       }
  270       
  271       /**
  272        * Gets the y position of the text line matrix.
  273        *
  274        * @return the y position of the text line matrix
  275        */
  276       public float getYTLM() {
  277           return state.yTLM;
  278       }
  279       
  280       /**
  281        * Gets the current text leading.
  282        *
  283        * @return the current text leading
  284        */
  285       public float getLeading() {
  286           return state.leading;
  287       }
  288       
  289       /**
  290        * Gets the current character spacing.
  291        *
  292        * @return the current character spacing
  293        */
  294       public float getCharacterSpacing() {
  295           return state.charSpace;
  296       }
  297   
  298       /**
  299        * Gets the current word spacing.
  300        *
  301        * @return the current word spacing
  302        */
  303       public float getWordSpacing() {
  304           return state.wordSpace;
  305       }
  306   
  307       /**
  308        * Gets the current character spacing.
  309        *
  310        * @return the current character spacing
  311        */
  312       public float getHorizontalScaling() {
  313           return state.scale;
  314       }
  315   
  316       /**
  317        * Changes the <VAR>Flatness</VAR>.
  318        * <P>
  319        * <VAR>Flatness</VAR> sets the maximum permitted distance in device pixels between the
  320        * mathematically correct path and an approximation constructed from straight line segments.<BR>
  321        *
  322        * @param       flatness        a value
  323        */
  324       
  325       public void setFlatness(float flatness) {
  326           if (flatness >= 0 && flatness <= 100) {
  327               content.append(flatness).append(" i").append_i(separator);
  328           }
  329       }
  330       
  331       /**
  332        * Changes the <VAR>Line cap style</VAR>.
  333        * <P>
  334        * The <VAR>line cap style</VAR> specifies the shape to be used at the end of open subpaths
  335        * when they are stroked.<BR>
  336        * Allowed values are LINE_CAP_BUTT, LINE_CAP_ROUND and LINE_CAP_PROJECTING_SQUARE.<BR>
  337        *
  338        * @param       style       a value
  339        */
  340       
  341       public void setLineCap(int style) {
  342           if (style >= 0 && style <= 2) {
  343               content.append(style).append(" J").append_i(separator);
  344           }
  345       }
  346       
  347       /**
  348        * Changes the value of the <VAR>line dash pattern</VAR>.
  349        * <P>
  350        * The line dash pattern controls the pattern of dashes and gaps used to stroke paths.
  351        * It is specified by an <I>array</I> and a <I>phase</I>. The array specifies the length
  352        * of the alternating dashes and gaps. The phase specifies the distance into the dash
  353        * pattern to start the dash.<BR>
  354        *
  355        * @param       phase       the value of the phase
  356        */
  357       
  358       public void setLineDash(float phase) {
  359           content.append("[] ").append(phase).append(" d").append_i(separator);
  360       }
  361       
  362       /**
  363        * Changes the value of the <VAR>line dash pattern</VAR>.
  364        * <P>
  365        * The line dash pattern controls the pattern of dashes and gaps used to stroke paths.
  366        * It is specified by an <I>array</I> and a <I>phase</I>. The array specifies the length
  367        * of the alternating dashes and gaps. The phase specifies the distance into the dash
  368        * pattern to start the dash.<BR>
  369        *
  370        * @param       phase       the value of the phase
  371        * @param       unitsOn     the number of units that must be 'on' (equals the number of units that must be 'off').
  372        */
  373       
  374       public void setLineDash(float unitsOn, float phase) {
  375           content.append("[").append(unitsOn).append("] ").append(phase).append(" d").append_i(separator);
  376       }
  377       
  378       /**
  379        * Changes the value of the <VAR>line dash pattern</VAR>.
  380        * <P>
  381        * The line dash pattern controls the pattern of dashes and gaps used to stroke paths.
  382        * It is specified by an <I>array</I> and a <I>phase</I>. The array specifies the length
  383        * of the alternating dashes and gaps. The phase specifies the distance into the dash
  384        * pattern to start the dash.<BR>
  385        *
  386        * @param       phase       the value of the phase
  387        * @param       unitsOn     the number of units that must be 'on'
  388        * @param       unitsOff    the number of units that must be 'off'
  389        */
  390       
  391       public void setLineDash(float unitsOn, float unitsOff, float phase) {
  392           content.append("[").append(unitsOn).append(' ').append(unitsOff).append("] ").append(phase).append(" d").append_i(separator);
  393       }
  394       
  395       /**
  396        * Changes the value of the <VAR>line dash pattern</VAR>.
  397        * <P>
  398        * The line dash pattern controls the pattern of dashes and gaps used to stroke paths.
  399        * It is specified by an <I>array</I> and a <I>phase</I>. The array specifies the length
  400        * of the alternating dashes and gaps. The phase specifies the distance into the dash
  401        * pattern to start the dash.<BR>
  402        *
  403        * @param       array       length of the alternating dashes and gaps
  404        * @param       phase       the value of the phase
  405        */
  406       
  407       public final void setLineDash(float[] array, float phase) {
  408           content.append("[");
  409           for (int i = 0; i < array.length; i++) {
  410               content.append(array[i]);
  411               if (i < array.length - 1) content.append(' ');
  412           }
  413           content.append("] ").append(phase).append(" d").append_i(separator);
  414       }
  415   
  416       /**
  417        * Changes the <VAR>Line join style</VAR>.
  418        * <P>
  419        * The <VAR>line join style</VAR> specifies the shape to be used at the corners of paths
  420        * that are stroked.<BR>
  421        * Allowed values are LINE_JOIN_MITER (Miter joins), LINE_JOIN_ROUND (Round joins) and LINE_JOIN_BEVEL (Bevel joins).<BR>
  422        *
  423        * @param       style       a value
  424        */
  425       
  426       public void setLineJoin(int style) {
  427           if (style >= 0 && style <= 2) {
  428               content.append(style).append(" j").append_i(separator);
  429           }
  430       }
  431       
  432       /**
  433        * Changes the <VAR>line width</VAR>.
  434        * <P>
  435        * The line width specifies the thickness of the line used to stroke a path and is measured
  436        * in user space units.<BR>
  437        *
  438        * @param       w           a width
  439        */
  440       
  441       public void setLineWidth(float w) {
  442           content.append(w).append(" w").append_i(separator);
  443       }
  444       
  445       /**
  446        * Changes the <VAR>Miter limit</VAR>.
  447        * <P>
  448        * When two line segments meet at a sharp angle and mitered joins have been specified as the
  449        * line join style, it is possible for the miter to extend far beyond the thickness of the line
  450        * stroking path. The miter limit imposes a maximum on the ratio of the miter length to the line
  451        * witdh. When the limit is exceeded, the join is converted from a miter to a bevel.<BR>
  452        *
  453        * @param       miterLimit      a miter limit
  454        */
  455       
  456       public void setMiterLimit(float miterLimit) {
  457           if (miterLimit > 1) {
  458               content.append(miterLimit).append(" M").append_i(separator);
  459           }
  460       }
  461       
  462       /**
  463        * Modify the current clipping path by intersecting it with the current path, using the
  464        * nonzero winding number rule to determine which regions lie inside the clipping
  465        * path.
  466        */
  467       
  468       public void clip() {
  469           content.append("W").append_i(separator);
  470       }
  471       
  472       /**
  473        * Modify the current clipping path by intersecting it with the current path, using the
  474        * even-odd rule to determine which regions lie inside the clipping path.
  475        */
  476       
  477       public void eoClip() {
  478           content.append("W*").append_i(separator);
  479       }
  480       
  481       /**
  482        * Changes the currentgray tint for filling paths (device dependent colors!).
  483        * <P>
  484        * Sets the color space to <B>DeviceGray</B> (or the <B>DefaultGray</B> color space),
  485        * and sets the gray tint to use for filling paths.</P>
  486        *
  487        * @param   gray    a value between 0 (black) and 1 (white)
  488        */
  489       
  490       public void setGrayFill(float gray) {
  491           content.append(gray).append(" g").append_i(separator);
  492       }
  493       
  494       /**
  495        * Changes the current gray tint for filling paths to black.
  496        */
  497       
  498       public void resetGrayFill() {
  499           content.append("0 g").append_i(separator);
  500       }
  501       
  502       /**
  503        * Changes the currentgray tint for stroking paths (device dependent colors!).
  504        * <P>
  505        * Sets the color space to <B>DeviceGray</B> (or the <B>DefaultGray</B> color space),
  506        * and sets the gray tint to use for stroking paths.</P>
  507        *
  508        * @param   gray    a value between 0 (black) and 1 (white)
  509        */
  510       
  511       public void setGrayStroke(float gray) {
  512           content.append(gray).append(" G").append_i(separator);
  513       }
  514       
  515       /**
  516        * Changes the current gray tint for stroking paths to black.
  517        */
  518       
  519       public void resetGrayStroke() {
  520           content.append("0 G").append_i(separator);
  521       }
  522       
  523       /**
  524        * Helper to validate and write the RGB color components
  525        * @param   red     the intensity of red. A value between 0 and 1
  526        * @param   green   the intensity of green. A value between 0 and 1
  527        * @param   blue    the intensity of blue. A value between 0 and 1
  528        */
  529       private void HelperRGB(float red, float green, float blue) {
  530       	PdfXConformanceImp.checkPDFXConformance(writer, PdfXConformanceImp.PDFXKEY_RGB, null);
  531           if (red < 0)
  532               red = 0.0f;
  533           else if (red > 1.0f)
  534               red = 1.0f;
  535           if (green < 0)
  536               green = 0.0f;
  537           else if (green > 1.0f)
  538               green = 1.0f;
  539           if (blue < 0)
  540               blue = 0.0f;
  541           else if (blue > 1.0f)
  542               blue = 1.0f;
  543           content.append(red).append(' ').append(green).append(' ').append(blue);
  544       }
  545       
  546       /**
  547        * Changes the current color for filling paths (device dependent colors!).
  548        * <P>
  549        * Sets the color space to <B>DeviceRGB</B> (or the <B>DefaultRGB</B> color space),
  550        * and sets the color to use for filling paths.</P>
  551        * <P>
  552        * Following the PDF manual, each operand must be a number between 0 (minimum intensity) and
  553        * 1 (maximum intensity).</P>
  554        *
  555        * @param   red     the intensity of red. A value between 0 and 1
  556        * @param   green   the intensity of green. A value between 0 and 1
  557        * @param   blue    the intensity of blue. A value between 0 and 1
  558        */
  559       
  560       public void setRGBColorFillF(float red, float green, float blue) {
  561           HelperRGB(red, green, blue);
  562           content.append(" rg").append_i(separator);
  563       }
  564       
  565       /**
  566        * Changes the current color for filling paths to black.
  567        */
  568       
  569       public void resetRGBColorFill() {
  570           content.append("0 g").append_i(separator);
  571       }
  572       
  573       /**
  574        * Changes the current color for stroking paths (device dependent colors!).
  575        * <P>
  576        * Sets the color space to <B>DeviceRGB</B> (or the <B>DefaultRGB</B> color space),
  577        * and sets the color to use for stroking paths.</P>
  578        * <P>
  579        * Following the PDF manual, each operand must be a number between 0 (miniumum intensity) and
  580        * 1 (maximum intensity).
  581        *
  582        * @param   red     the intensity of red. A value between 0 and 1
  583        * @param   green   the intensity of green. A value between 0 and 1
  584        * @param   blue    the intensity of blue. A value between 0 and 1
  585        */
  586       
  587       public void setRGBColorStrokeF(float red, float green, float blue) {
  588           HelperRGB(red, green, blue);
  589           content.append(" RG").append_i(separator);
  590       }
  591       
  592       /**
  593        * Changes the current color for stroking paths to black.
  594        *
  595        */
  596       
  597       public void resetRGBColorStroke() {
  598           content.append("0 G").append_i(separator);
  599       }
  600       
  601       /**
  602        * Helper to validate and write the CMYK color components.
  603        *
  604        * @param   cyan    the intensity of cyan. A value between 0 and 1
  605        * @param   magenta the intensity of magenta. A value between 0 and 1
  606        * @param   yellow  the intensity of yellow. A value between 0 and 1
  607        * @param   black   the intensity of black. A value between 0 and 1
  608        */
  609       private void HelperCMYK(float cyan, float magenta, float yellow, float black) {
  610           if (cyan < 0)
  611               cyan = 0.0f;
  612           else if (cyan > 1.0f)
  613               cyan = 1.0f;
  614           if (magenta < 0)
  615               magenta = 0.0f;
  616           else if (magenta > 1.0f)
  617               magenta = 1.0f;
  618           if (yellow < 0)
  619               yellow = 0.0f;
  620           else if (yellow > 1.0f)
  621               yellow = 1.0f;
  622           if (black < 0)
  623               black = 0.0f;
  624           else if (black > 1.0f)
  625               black = 1.0f;
  626           content.append(cyan).append(' ').append(magenta).append(' ').append(yellow).append(' ').append(black);
  627       }
  628       
  629       /**
  630        * Changes the current color for filling paths (device dependent colors!).
  631        * <P>
  632        * Sets the color space to <B>DeviceCMYK</B> (or the <B>DefaultCMYK</B> color space),
  633        * and sets the color to use for filling paths.</P>
  634        * <P>
  635        * Following the PDF manual, each operand must be a number between 0 (no ink) and
  636        * 1 (maximum ink).</P>
  637        *
  638        * @param   cyan    the intensity of cyan. A value between 0 and 1
  639        * @param   magenta the intensity of magenta. A value between 0 and 1
  640        * @param   yellow  the intensity of yellow. A value between 0 and 1
  641        * @param   black   the intensity of black. A value between 0 and 1
  642        */
  643       
  644       public void setCMYKColorFillF(float cyan, float magenta, float yellow, float black) {
  645           HelperCMYK(cyan, magenta, yellow, black);
  646           content.append(" k").append_i(separator);
  647       }
  648       
  649       /**
  650        * Changes the current color for filling paths to black.
  651        *
  652        */
  653       
  654       public void resetCMYKColorFill() {
  655           content.append("0 0 0 1 k").append_i(separator);
  656       }
  657       
  658       /**
  659        * Changes the current color for stroking paths (device dependent colors!).
  660        * <P>
  661        * Sets the color space to <B>DeviceCMYK</B> (or the <B>DefaultCMYK</B> color space),
  662        * and sets the color to use for stroking paths.</P>
  663        * <P>
  664        * Following the PDF manual, each operand must be a number between 0 (miniumum intensity) and
  665        * 1 (maximum intensity).
  666        *
  667        * @param   cyan    the intensity of cyan. A value between 0 and 1
  668        * @param   magenta the intensity of magenta. A value between 0 and 1
  669        * @param   yellow  the intensity of yellow. A value between 0 and 1
  670        * @param   black   the intensity of black. A value between 0 and 1
  671        */
  672       
  673       public void setCMYKColorStrokeF(float cyan, float magenta, float yellow, float black) {
  674           HelperCMYK(cyan, magenta, yellow, black);
  675           content.append(" K").append_i(separator);
  676       }
  677       
  678       /**
  679        * Changes the current color for stroking paths to black.
  680        *
  681        */
  682       
  683       public void resetCMYKColorStroke() {
  684           content.append("0 0 0 1 K").append_i(separator);
  685       }
  686       
  687       /**
  688        * Move the current point <I>(x, y)</I>, omitting any connecting line segment.
  689        *
  690        * @param       x               new x-coordinate
  691        * @param       y               new y-coordinate
  692        */
  693       
  694       public void moveTo(float x, float y) {
  695           content.append(x).append(' ').append(y).append(" m").append_i(separator);
  696       }
  697       
  698       /**
  699        * Appends a straight line segment from the current point <I>(x, y)</I>. The new current
  700        * point is <I>(x, y)</I>.
  701        *
  702        * @param       x               new x-coordinate
  703        * @param       y               new y-coordinate
  704        */
  705       
  706       public void lineTo(float x, float y) {
  707           content.append(x).append(' ').append(y).append(" l").append_i(separator);
  708       }
  709       
  710       /**
  711        * Appends a B&#xea;zier curve to the path, starting from the current point.
  712        *
  713        * @param       x1      x-coordinate of the first control point
  714        * @param       y1      y-coordinate of the first control point
  715        * @param       x2      x-coordinate of the second control point
  716        * @param       y2      y-coordinate of the second control point
  717        * @param       x3      x-coordinate of the ending point (= new current point)
  718        * @param       y3      y-coordinate of the ending point (= new current point)
  719        */
  720       
  721       public void curveTo(float x1, float y1, float x2, float y2, float x3, float y3) {
  722           content.append(x1).append(' ').append(y1).append(' ').append(x2).append(' ').append(y2).append(' ').append(x3).append(' ').append(y3).append(" c").append_i(separator);
  723       }
  724       
  725       /**
  726        * Appends a B&#xea;zier curve to the path, starting from the current point.
  727        *
  728        * @param       x2      x-coordinate of the second control point
  729        * @param       y2      y-coordinate of the second control point
  730        * @param       x3      x-coordinate of the ending point (= new current point)
  731        * @param       y3      y-coordinate of the ending point (= new current point)
  732        */
  733       
  734       public void curveTo(float x2, float y2, float x3, float y3) {
  735           content.append(x2).append(' ').append(y2).append(' ').append(x3).append(' ').append(y3).append(" v").append_i(separator);
  736       }
  737       
  738       /**
  739        * Appends a B&#xea;zier curve to the path, starting from the current point.
  740        *
  741        * @param       x1      x-coordinate of the first control point
  742        * @param       y1      y-coordinate of the first control point
  743        * @param       x3      x-coordinate of the ending point (= new current point)
  744        * @param       y3      y-coordinate of the ending point (= new current point)
  745        */
  746       
  747       public void curveFromTo(float x1, float y1, float x3, float y3) {
  748           content.append(x1).append(' ').append(y1).append(' ').append(x3).append(' ').append(y3).append(" y").append_i(separator);
  749       }
  750       
  751       /** Draws a circle. The endpoint will (x+r, y).
  752        *
  753        * @param x x center of circle
  754        * @param y y center of circle
  755        * @param r radius of circle
  756        */
  757       public void circle(float x, float y, float r) {
  758           float b = 0.5523f;
  759           moveTo(x + r, y);
  760           curveTo(x + r, y + r * b, x + r * b, y + r, x, y + r);
  761           curveTo(x - r * b, y + r, x - r, y + r * b, x - r, y);
  762           curveTo(x - r, y - r * b, x - r * b, y - r, x, y - r);
  763           curveTo(x + r * b, y - r, x + r, y - r * b, x + r, y);
  764       }
  765       
  766       
  767       
  768       /**
  769        * Adds a rectangle to the current path.
  770        *
  771        * @param       x       x-coordinate of the starting point
  772        * @param       y       y-coordinate of the starting point
  773        * @param       w       width
  774        * @param       h       height
  775        */
  776       
  777       public void rectangle(float x, float y, float w, float h) {
  778           content.append(x).append(' ').append(y).append(' ').append(w).append(' ').append(h).append(" re").append_i(separator);
  779       }
  780       
  781       private boolean compareColors(Color c1, Color c2) {
  782           if (c1 == null && c2 == null)
  783               return true;
  784           if (c1 == null || c2 == null)
  785               return false;
  786           if (c1 instanceof ExtendedColor)
  787               return c1.equals(c2);
  788           return c2.equals(c1);
  789       }
  790       
  791       /**
  792        * Adds a variable width border to the current path.
  793        * Only use if {@link com.lowagie.text.Rectangle#isUseVariableBorders() Rectangle.isUseVariableBorders}
  794        * = true.
  795        * @param rect a <CODE>Rectangle</CODE>
  796        */
  797       public void variableRectangle(Rectangle rect) {
  798           float t = rect.getTop();
  799           float b = rect.getBottom();
  800           float r = rect.getRight();
  801           float l = rect.getLeft();
  802           float wt = rect.getBorderWidthTop();
  803           float wb = rect.getBorderWidthBottom();
  804           float wr = rect.getBorderWidthRight();
  805           float wl = rect.getBorderWidthLeft();
  806           Color ct = rect.getBorderColorTop();
  807           Color cb = rect.getBorderColorBottom();
  808           Color cr = rect.getBorderColorRight();
  809           Color cl = rect.getBorderColorLeft();
  810           saveState();
  811           setLineCap(PdfContentByte.LINE_CAP_BUTT);
  812           setLineJoin(PdfContentByte.LINE_JOIN_MITER);
  813           float clw = 0;
  814           boolean cdef = false;
  815           Color ccol = null;
  816           boolean cdefi = false;
  817           Color cfil = null;
  818           // draw top
  819           if (wt > 0) {
  820               setLineWidth(clw = wt);
  821               cdef = true;
  822               if (ct == null)
  823                   resetRGBColorStroke();
  824               else
  825                   setColorStroke(ct);
  826               ccol = ct;
  827               moveTo(l, t - wt / 2f);
  828               lineTo(r, t - wt / 2f);
  829               stroke();
  830           }
  831   
  832           // Draw bottom
  833           if (wb > 0) {
  834               if (wb != clw)
  835                   setLineWidth(clw = wb);
  836               if (!cdef || !compareColors(ccol, cb)) {
  837                   cdef = true;
  838                   if (cb == null)
  839                       resetRGBColorStroke();
  840                   else
  841                       setColorStroke(cb);
  842                   ccol = cb;
  843               }
  844               moveTo(r, b + wb / 2f);
  845               lineTo(l, b + wb / 2f);
  846               stroke();
  847           }
  848   
  849           // Draw right
  850           if (wr > 0) {
  851               if (wr != clw)
  852                   setLineWidth(clw = wr);
  853               if (!cdef || !compareColors(ccol, cr)) {
  854                   cdef = true;
  855                   if (cr == null)
  856                       resetRGBColorStroke();
  857                   else
  858                       setColorStroke(cr);
  859                   ccol = cr;
  860               }
  861               boolean bt = compareColors(ct, cr);
  862               boolean bb = compareColors(cb, cr);
  863               moveTo(r - wr / 2f, bt ? t : t - wt);
  864               lineTo(r - wr / 2f, bb ? b : b + wb);
  865               stroke();
  866               if (!bt || !bb) {
  867                   cdefi = true;
  868                   if (cr == null)
  869                       resetRGBColorFill();
  870                   else
  871                       setColorFill(cr);
  872                   cfil = cr;
  873                   if (!bt) {
  874                       moveTo(r, t);
  875                       lineTo(r, t - wt);
  876                       lineTo(r - wr, t - wt);
  877                       fill();
  878                   }
  879                   if (!bb) {
  880                       moveTo(r, b);
  881                       lineTo(r, b + wb);
  882                       lineTo(r - wr, b + wb);
  883                       fill();
  884                   }
  885               }
  886           }
  887           
  888           // Draw Left
  889           if (wl > 0) {
  890               if (wl != clw)
  891                   setLineWidth(wl);
  892               if (!cdef || !compareColors(ccol, cl)) {
  893                   if (cl == null)
  894                       resetRGBColorStroke();
  895                   else
  896                       setColorStroke(cl);
  897               }
  898               boolean bt = compareColors(ct, cl);
  899               boolean bb = compareColors(cb, cl);
  900               moveTo(l + wl / 2f, bt ? t : t - wt);
  901               lineTo(l + wl / 2f, bb ? b : b + wb);
  902               stroke();
  903               if (!bt || !bb) {
  904                   if (!cdefi || !compareColors(cfil, cl)) {
  905                       if (cl == null)
  906                           resetRGBColorFill();
  907                       else
  908                           setColorFill(cl);
  909                   }
  910                   if (!bt) {
  911                       moveTo(l, t);
  912                       lineTo(l, t - wt);
  913                       lineTo(l + wl, t - wt);
  914                       fill();
  915                   }
  916                   if (!bb) {
  917                       moveTo(l, b);
  918                       lineTo(l, b + wb);
  919                       lineTo(l + wl, b + wb);
  920                       fill();
  921                   }
  922               }
  923           }
  924           restoreState();
  925       }
  926   
  927       /**
  928        * Adds a border (complete or partially) to the current path..
  929        *
  930        * @param       rectangle       a <CODE>Rectangle</CODE>
  931        */
  932       
  933       public void rectangle(Rectangle rectangle) {
  934           // the coordinates of the border are retrieved
  935           float x1 = rectangle.getLeft();
  936           float y1 = rectangle.getBottom();
  937           float x2 = rectangle.getRight();
  938           float y2 = rectangle.getTop();
  939   
  940           // the backgroundcolor is set
  941           Color background = rectangle.getBackgroundColor();
  942           if (background != null) {
  943               setColorFill(background);
  944               rectangle(x1, y1, x2 - x1, y2 - y1);
  945               fill();
  946               resetRGBColorFill();
  947           }
  948   
  949           // if the element hasn't got any borders, nothing is added
  950           if (! rectangle.hasBorders()) {
  951               return;
  952           }
  953   
  954           // if any of the individual border colors are set
  955           // we draw the borders all around using the
  956           // different colors
  957           if (rectangle.isUseVariableBorders()) {
  958               variableRectangle(rectangle);
  959           }
  960           else {
  961               // the width is set to the width of the element
  962               if (rectangle.getBorderWidth() != Rectangle.UNDEFINED) {
  963                   setLineWidth(rectangle.getBorderWidth());
  964               }
  965   
  966               // the color is set to the color of the element
  967               Color color = rectangle.getBorderColor();
  968               if (color != null) {
  969                   setColorStroke(color);
  970               }
  971   
  972               // if the box is a rectangle, it is added as a rectangle
  973               if (rectangle.hasBorder(Rectangle.BOX)) {
  974                  rectangle(x1, y1, x2 - x1, y2 - y1);
  975               }
  976               // if the border isn't a rectangle, the different sides are added apart
  977               else {
  978                   if (rectangle.hasBorder(Rectangle.RIGHT)) {
  979                       moveTo(x2, y1);
  980                       lineTo(x2, y2);
  981                   }
  982                   if (rectangle.hasBorder(Rectangle.LEFT)) {
  983                       moveTo(x1, y1);
  984                       lineTo(x1, y2);
  985                   }
  986                   if (rectangle.hasBorder(Rectangle.BOTTOM)) {
  987                       moveTo(x1, y1);
  988                       lineTo(x2, y1);
  989                   }
  990                   if (rectangle.hasBorder(Rectangle.TOP)) {
  991                       moveTo(x1, y2);
  992                       lineTo(x2, y2);
  993                   }
  994               }
  995   
  996               stroke();
  997   
  998               if (color != null) {
  999                   resetRGBColorStroke();
 1000               }
 1001           }
 1002       }
 1003   
 1004       /**
 1005        * Closes the current subpath by appending a straight line segment from the current point
 1006        * to the starting point of the subpath.
 1007        */
 1008       
 1009       public void closePath() {
 1010           content.append("h").append_i(separator);
 1011       }
 1012       
 1013       /**
 1014        * Ends the path without filling or stroking it.
 1015        */
 1016       
 1017       public void newPath() {
 1018           content.append("n").append_i(separator);
 1019       }
 1020       
 1021       /**
 1022        * Strokes the path.
 1023        */
 1024       
 1025       public void stroke() {
 1026           content.append("S").append_i(separator);
 1027       }
 1028       
 1029       /**
 1030        * Closes the path and strokes it.
 1031        */
 1032       
 1033       public void closePathStroke() {
 1034           content.append("s").append_i(separator);
 1035       }
 1036       
 1037       /**
 1038        * Fills the path, using the non-zero winding number rule to determine the region to fill.
 1039        */
 1040       
 1041       public void fill() {
 1042           content.append("f").append_i(separator);
 1043       }
 1044       
 1045       /**
 1046        * Fills the path, using the even-odd rule to determine the region to fill.
 1047        */
 1048       
 1049       public void eoFill() {
 1050           content.append("f*").append_i(separator);
 1051       }
 1052       
 1053       /**
 1054        * Fills the path using the non-zero winding number rule to determine the region to fill and strokes it.
 1055        */
 1056       
 1057       public void fillStroke() {
 1058           content.append("B").append_i(separator);
 1059       }
 1060       
 1061       /**
 1062        * Closes the path, fills it using the non-zero winding number rule to determine the region to fill and strokes it.
 1063        */
 1064       
 1065       public void closePathFillStroke() {
 1066           content.append("b").append_i(separator);
 1067       }
 1068       
 1069       /**
 1070        * Fills the path, using the even-odd rule to determine the region to fill and strokes it.
 1071        */
 1072       
 1073       public void eoFillStroke() {
 1074           content.append("B*").append_i(separator);
 1075       }
 1076       
 1077       /**
 1078        * Closes the path, fills it using the even-odd rule to determine the region to fill and strokes it.
 1079        */
 1080       
 1081       public void closePathEoFillStroke() {
 1082           content.append("b*").append_i(separator);
 1083       }
 1084       
 1085       /**
 1086        * Adds an <CODE>Image</CODE> to the page. The <CODE>Image</CODE> must have
 1087        * absolute positioning.
 1088        * @param image the <CODE>Image</CODE> object
 1089        * @throws DocumentException if the <CODE>Image</CODE> does not have absolute positioning
 1090        */
 1091       public void addImage(Image image) throws DocumentException {
 1092           addImage(image, false);
 1093       }
 1094       
 1095       /**
 1096        * Adds an <CODE>Image</CODE> to the page. The <CODE>Image</CODE> must have
 1097        * absolute positioning. The image can be placed inline.
 1098        * @param image the <CODE>Image</CODE> object
 1099        * @param inlineImage <CODE>true</CODE> to place this image inline, <CODE>false</CODE> otherwise
 1100        * @throws DocumentException if the <CODE>Image</CODE> does not have absolute positioning
 1101        */
 1102       public void addImage(Image image, boolean inlineImage) throws DocumentException {
 1103           if (!image.hasAbsoluteY())
 1104               throw new DocumentException("The image must have absolute positioning.");
 1105           float matrix[] = image.matrix();
 1106           matrix[Image.CX] = image.getAbsoluteX() - matrix[Image.CX];
 1107           matrix[Image.CY] = image.getAbsoluteY() - matrix[Image.CY];
 1108           addImage(image, matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5], inlineImage);
 1109       }
 1110       
 1111       /**
 1112        * Adds an <CODE>Image</CODE> to the page. The positioning of the <CODE>Image</CODE>
 1113        * is done with the transformation matrix. To position an <CODE>image</CODE> at (x,y)
 1114        * use addImage(image, image_width, 0, 0, image_height, x, y).
 1115        * @param image the <CODE>Image</CODE> object
 1116        * @param a an element of the transformation matrix
 1117        * @param b an element of the transformation matrix
 1118        * @param c an element of the transformation matrix
 1119        * @param d an element of the transformation matrix
 1120        * @param e an element of the transformation matrix
 1121        * @param f an element of the transformation matrix
 1122        * @throws DocumentException on error
 1123        */
 1124       public void addImage(Image image, float a, float b, float c, float d, float e, float f) throws DocumentException {
 1125           addImage(image, a, b, c, d, e, f, false);
 1126       }
 1127       
 1128       /**
 1129        * Adds an <CODE>Image</CODE> to the page. The positioning of the <CODE>Image</CODE>
 1130        * is done with the transformation matrix. To position an <CODE>image</CODE> at (x,y)
 1131        * use addImage(image, image_width, 0, 0, image_height, x, y). The image can be placed inline.
 1132        * @param image the <CODE>Image</CODE> object
 1133        * @param a an element of the transformation matrix
 1134        * @param b an element of the transformation matrix
 1135        * @param c an element of the transformation matrix
 1136        * @param d an element of the transformation matrix
 1137        * @param e an element of the transformation matrix
 1138        * @param f an element of the transformation matrix
 1139        * @param inlineImage <CODE>true</CODE> to place this image inline, <CODE>false</CODE> otherwise
 1140        * @throws DocumentException on error
 1141        */
 1142       public void addImage(Image image, float a, float b, float c, float d, float e, float f, boolean inlineImage) throws DocumentException {
 1143           try {
 1144               if (image.getLayer() != null)
 1145                   beginLayer(image.getLayer());
 1146               if (image.isImgTemplate()) {
 1147                   writer.addDirectImageSimple(image);
 1148                   PdfTemplate template = image.getTemplateData();
 1149                   float w = template.getWidth();
 1150                   float h = template.getHeight();
 1151                   addTemplate(template, a / w, b / w, c / h, d / h, e, f);
 1152               }
 1153               else {
 1154                   content.append("q ");
 1155                   content.append(a).append(' ');
 1156                   content.append(b).append(' ');
 1157                   content.append(c).append(' ');
 1158                   content.append(d).append(' ');
 1159                   content.append(e).append(' ');
 1160                   content.append(f).append(" cm");
 1161                   if (inlineImage) {
 1162                       content.append("\nBI\n");
 1163                       PdfImage pimage = new PdfImage(image, "", null);
 1164                       for (Iterator it = pimage.getKeys().iterator(); it.hasNext();) {
 1165                           PdfName key = (PdfName)it.next();
 1166                           PdfObject value = pimage.get(key);
 1167                           String s = (String)abrev.get(key);
 1168                           if (s == null)
 1169                               continue;
 1170                           content.append(s);
 1171                           boolean check = true;
 1172                           if (key.equals(PdfName.COLORSPACE) && value.isArray()) {
 1173                               ArrayList ar = ((PdfArray)value).getArrayList();
 1174                               if (ar.size() == 4 
 1175                                   && PdfName.INDEXED.equals(ar.get(0)) 
 1176                                   && ((PdfObject)ar.get(1)).isName()
 1177                                   && ((PdfObject)ar.get(2)).isNumber()
 1178                                   && ((PdfObject)ar.get(3)).isString()
 1179                               ) {
 1180                                   check = false;
 1181                               }
 1182                               
 1183                           }
 1184                           if (check && key.equals(PdfName.COLORSPACE) && !value.isName()) {
 1185                               PdfName cs = writer.getColorspaceName();
 1186                               PageResources prs = getPageResources();
 1187                               prs.addColor(cs, writer.addToBody(value).getIndirectReference());
 1188                               value = cs;
 1189                           }
 1190                           value.toPdf(null, content);
 1191                           content.append('\n');
 1192                       }
 1193                       content.append("ID\n");
 1194                       pimage.writeContent(content);
 1195                       content.append("\nEI\nQ").append_i(separator);
 1196                   }
 1197                   else {
 1198                       PdfName name;
 1199                       PageResources prs = getPageResources();
 1200                       Image maskImage = image.getImageMask();
 1201                       if (maskImage != null) {
 1202                           name = writer.addDirectImageSimple(maskImage);
 1203                           prs.addXObject(name, writer.getImageReference(name));
 1204                       }
 1205                       name = writer.addDirectImageSimple(image);
 1206                       name = prs.addXObject(name, writer.getImageReference(name));
 1207                       content.append(' ').append(name.getBytes()).append(" Do Q").append_i(separator);
 1208                   }
 1209               }
 1210               if (image.hasBorders()) {
 1211                   saveState();
 1212                   float w = image.getWidth();
 1213                   float h = image.getHeight();
 1214                   concatCTM(a / w, b / w, c / h, d / h, e, f);
 1215                   rectangle(image);
 1216                   restoreState();
 1217               }
 1218               if (image.getLayer() != null)
 1219                   endLayer();
 1220               Annotation annot = image.getAnnotation();
 1221               if (annot == null)
 1222                   return;
 1223               float[] r = new float[unitRect.length];
 1224               for (int k = 0; k < unitRect.length; k += 2) {
 1225                   r[k] = a * unitRect[k] + c * unitRect[k + 1] + e;
 1226                   r[k + 1] = b * unitRect[k] + d * unitRect[k + 1] + f;
 1227               }
 1228               float llx = r[0];
 1229               float lly = r[1];
 1230               float urx = llx;
 1231               float ury = lly;
 1232               for (int k = 2; k < r.length; k += 2) {
 1233                   llx = Math.min(llx, r[k]);
 1234                   lly = Math.min(lly, r[k + 1]);
 1235                   urx = Math.max(urx, r[k]);
 1236                   ury = Math.max(ury, r[k + 1]);
 1237               }
 1238               annot = new Annotation(annot);
 1239               annot.setDimensions(llx, lly, urx, ury);
 1240               PdfAnnotation an = PdfAnnotationsImp.convertAnnotation(writer, annot, new Rectangle(llx, lly, urx, ury));
 1241               if (an == null)
 1242                   return;
 1243               addAnnotation(an);
 1244           }
 1245           catch (Exception ee) {
 1246               throw new DocumentException(ee);
 1247           }
 1248       }
 1249       
 1250       /**
 1251        * Makes this <CODE>PdfContentByte</CODE> empty.
 1252        */
 1253       public void reset() {
 1254           content.reset();
 1255           stateList.clear();
 1256           state = new GraphicState();
 1257       }
 1258       
 1259       /**
 1260        * Starts the writing of text.
 1261        */
 1262       public void beginText() {
 1263           state.xTLM = 0;
 1264           state.yTLM = 0;
 1265           content.append("BT").append_i(separator);
 1266       }
 1267       
 1268       /**
 1269        * Ends the writing of text and makes the current font invalid.
 1270        */
 1271       public void endText() {
 1272           content.append("ET").append_i(separator);
 1273       }
 1274       
 1275       /**
 1276        * Saves the graphic state. <CODE>saveState</CODE> and
 1277        * <CODE>restoreState</CODE> must be balanced.
 1278        */
 1279       public void saveState() {
 1280           content.append("q").append_i(separator);
 1281           stateList.add(new GraphicState(state));
 1282       }
 1283       
 1284       /**
 1285        * Restores the graphic state. <CODE>saveState</CODE> and
 1286        * <CODE>restoreState</CODE> must be balanced.
 1287        */
 1288       public void restoreState() {
 1289           content.append("Q").append_i(separator);
 1290           int idx = stateList.size() - 1;
 1291           if (idx < 0)
 1292               throw new RuntimeException("Unbalanced save/restore state operators.");
 1293           state = (GraphicState)stateList.get(idx);
 1294           stateList.remove(idx);
 1295       }
 1296       
 1297       /**
 1298        * Sets the character spacing parameter.
 1299        *
 1300        * @param       charSpace           a parameter
 1301        */
 1302       public void setCharacterSpacing(float charSpace) {
 1303           state.charSpace = charSpace;
 1304           content.append(charSpace).append(" Tc").append_i(separator);
 1305       }
 1306       
 1307       /**
 1308        * Sets the word spacing parameter.
 1309        *
 1310        * @param       wordSpace           a parameter
 1311        */
 1312       public void setWordSpacing(float wordSpace) {
 1313           state.wordSpace = wordSpace;
 1314           content.append(wordSpace).append(" Tw").append_i(separator);
 1315       }
 1316       
 1317       /**
 1318        * Sets the horizontal scaling parameter.
 1319        *
 1320        * @param       scale               a parameter
 1321        */
 1322       public void setHorizontalScaling(float scale) {
 1323           state.scale = scale;
 1324           content.append(scale).append(" Tz").append_i(separator);
 1325       }
 1326       
 1327       /**
 1328        * Sets the text leading parameter.
 1329        * <P>
 1330        * The leading parameter is measured in text space units. It specifies the vertical distance
 1331        * between the baselines of adjacent lines of text.</P>
 1332        *
 1333        * @param       leading         the new leading
 1334        */
 1335       public void setLeading(float leading) {
 1336           state.leading = leading;
 1337           content.append(leading).append(" TL").append_i(separator);
 1338       }
 1339       
 1340       /**
 1341        * Set the font and the size for the subsequent text writing.
 1342        *
 1343        * @param bf the font
 1344        * @param size the font size in points
 1345        */
 1346       public void setFontAndSize(BaseFont bf, float size) {
 1347           checkWriter();
 1348           if (size < 0.0001f && size > -0.0001f)
 1349               throw new IllegalArgumentException("Font size too small: " + size);
 1350           state.size = size;
 1351           state.fontDetails = writer.addSimple(bf);
 1352           PageResources prs = getPageResources();
 1353           PdfName name = state.fontDetails.getFontName();
 1354           name = prs.addFont(name, state.fontDetails.getIndirectReference());
 1355           content.append(name.getBytes()).append(' ').append(size).append(" Tf").append_i(separator);
 1356       }
 1357       
 1358       /**
 1359        * Sets the text rendering parameter.
 1360        *
 1361        * @param       rendering               a parameter
 1362        */
 1363       public void setTextRenderingMode(int rendering) {
 1364           content.append(rendering).append(" Tr").append_i(separator);
 1365       }
 1366       
 1367       /**
 1368        * Sets the text rise parameter.
 1369        * <P>
 1370        * This allows to write text in subscript or superscript mode.</P>
 1371        *
 1372        * @param       rise                a parameter
 1373        */
 1374       public void setTextRise(float rise) {
 1375           content.append(rise).append(" Ts").append_i(separator);
 1376       }
 1377       
 1378       /**
 1379        * A helper to insert into the content stream the <CODE>text</CODE>
 1380        * converted to bytes according to the font's encoding.
 1381        *
 1382        * @param text the text to write
 1383        */
 1384       private void showText2(String text) {
 1385           if (state.fontDetails == null)
 1386               throw new NullPointerException("Font and size must be set before writing any text");
 1387           byte b[] = state.fontDetails.convertToBytes(text);
 1388           escapeString(b, content);
 1389       }
 1390       
 1391       /**
 1392        * Shows the <CODE>text</CODE>.
 1393        *
 1394        * @param text the text to write
 1395        */
 1396       public void showText(String text) {
 1397           showText2(text);
 1398           content.append("Tj").append_i(separator);
 1399       }
 1400       
 1401       /**
 1402        * Constructs a kern array for a text in a certain font
 1403        * @param text the text
 1404        * @param font the font
 1405        * @return a PdfTextArray
 1406        */
 1407       public static PdfTextArray getKernArray(String text, BaseFont font) {
 1408           PdfTextArray pa = new PdfTextArray();
 1409           StringBuffer acc = new StringBuffer();
 1410           int len = text.length() - 1;
 1411           char c[] = text.toCharArray();
 1412           if (len >= 0)
 1413               acc.append(c, 0, 1);
 1414           for (int k = 0; k < len; ++k) {
 1415               char c2 = c[k + 1];
 1416               int kern = font.getKerning(c[k], c2);
 1417               if (kern == 0) {
 1418                   acc.append(c2);
 1419               }
 1420               else {
 1421                   pa.add(acc.toString());
 1422                   acc.setLength(0);
 1423                   acc.append(c, k + 1, 1);
 1424                   pa.add(-kern);
 1425               }
 1426           }
 1427           pa.add(acc.toString());
 1428           return pa;
 1429       }
 1430       
 1431       /**
 1432        * Shows the <CODE>text</CODE> kerned.
 1433        *
 1434        * @param text the text to write
 1435        */
 1436       public void showTextKerned(String text) {
 1437           if (state.fontDetails == null)
 1438               throw new NullPointerException("Font and size must be set before writing any text");
 1439           BaseFont bf = state.fontDetails.getBaseFont();
 1440           if (bf.hasKernPairs())
 1441               showText(getKernArray(text, bf));
 1442           else
 1443               showText(text);
 1444       }
 1445       
 1446       /**
 1447        * Moves to the next line and shows <CODE>text</CODE>.
 1448        *
 1449        * @param text the text to write
 1450        */
 1451       public void newlineShowText(String text) {
 1452           state.yTLM -= state.leading;
 1453           showText2(text);
 1454           content.append("'").append_i(separator);
 1455       }
 1456       
 1457       /**
 1458        * Moves to the next line and shows text string, using the given values of the character and word spacing parameters.
 1459        *
 1460        * @param       wordSpacing     a parameter
 1461        * @param       charSpacing     a parameter
 1462        * @param text the text to write
 1463        */
 1464       public void newlineShowText(float wordSpacing, float charSpacing, String text) {
 1465           state.yTLM -= state.leading;
 1466           content.append(wordSpacing).append(' ').append(charSpacing);
 1467           showText2(text);
 1468           content.append("\"").append_i(separator);
 1469           
 1470           // The " operator sets charSpace and wordSpace into graphics state
 1471           // (cfr PDF reference v1.6, table 5.6)
 1472           state.charSpace = charSpacing;
 1473           state.wordSpace = wordSpacing;
 1474       }
 1475       
 1476       /**
 1477        * Changes the text matrix.
 1478        * <P>
 1479        * Remark: this operation also initializes the current point position.</P>
 1480        *
 1481        * @param       a           operand 1,1 in the matrix
 1482        * @param       b           operand 1,2 in the matrix
 1483        * @param       c           operand 2,1 in the matrix
 1484        * @param       d           operand 2,2 in the matrix
 1485        * @param       x           operand 3,1 in the matrix
 1486        * @param       y           operand 3,2 in the matrix
 1487        */
 1488       public void setTextMatrix(float a, float b, float c, float d, float x, float y) {
 1489           state.xTLM = x;
 1490           state.yTLM = y;
 1491           content.append(a).append(' ').append(b).append_i(' ')
 1492           .append(c).append_i(' ').append(d).append_i(' ')
 1493           .append(x).append_i(' ').append(y).append(" Tm").append_i(separator);
 1494       }
 1495       
 1496       /**
 1497        * Changes the text matrix. The first four parameters are {1,0,0,1}.
 1498        * <P>
 1499        * Remark: this operation also initializes the current point position.</P>
 1500        *
 1501        * @param       x           operand 3,1 in the matrix
 1502        * @param       y           operand 3,2 in the matrix
 1503        */
 1504       public void setTextMatrix(float x, float y) {
 1505           setTextMatrix(1, 0, 0, 1, x, y);
 1506       }
 1507       
 1508       /**
 1509        * Moves to the start of the next line, offset from the start of the current line.
 1510        *
 1511        * @param       x           x-coordinate of the new current point
 1512        * @param       y           y-coordinate of the new current point
 1513        */
 1514       public void moveText(float x, float y) {
 1515           state.xTLM += x;
 1516           state.yTLM += y;
 1517           content.append(x).append(' ').append(y).append(" Td").append_i(separator);
 1518       }
 1519       
 1520       /**
 1521        * Moves to the start of the next line, offset from the start of the current line.
 1522        * <P>
 1523        * As a side effect, this sets the leading parameter in the text state.</P>
 1524        *
 1525        * @param       x           offset of the new current point
 1526        * @param       y           y-coordinate of the new current point
 1527        */
 1528       public void moveTextWithLeading(float x, float y) {
 1529           state.xTLM += x;
 1530           state.yTLM += y;
 1531           state.leading = -y;
 1532           content.append(x).append(' ').append(y).append(" TD").append_i(separator);
 1533       }
 1534       
 1535       /**
 1536        * Moves to the start of the next line.
 1537        */
 1538       public void newlineText() {
 1539           state.yTLM -= state.leading;
 1540           content.append("T*").append_i(separator);
 1541       }
 1542       
 1543       /**
 1544        * Gets the size of this content.
 1545        *
 1546        * @return the size of the content
 1547        */
 1548       int size() {
 1549           return content.size();
 1550       }
 1551       
 1552       /**
 1553        * Escapes a <CODE>byte</CODE> array according to the PDF conventions.
 1554        *
 1555        * @param b the <CODE>byte</CODE> array to escape
 1556        * @return an escaped <CODE>byte</CODE> array
 1557        */
 1558       static byte[] escapeString(byte b[]) {
 1559           ByteBuffer content = new ByteBuffer();
 1560           escapeString(b, content);
 1561           return content.toByteArray();
 1562       }
 1563       
 1564       /**
 1565        * Escapes a <CODE>byte</CODE> array according to the PDF conventions.
 1566        *
 1567        * @param b the <CODE>byte</CODE> array to escape
 1568        * @param content the content
 1569        */
 1570       static void escapeString(byte b[], ByteBuffer content) {
 1571           content.append_i('(');
 1572           for (int k = 0; k < b.length; ++k) {
 1573               byte c = b[k];
 1574               switch (c) {
 1575                   case '\r':
 1576                       content.append("\\r");
 1577                       break;
 1578                   case '\n':
 1579                       content.append("\\n");
 1580                       break;
 1581                   case '\t':
 1582                       content.append("\\t");
 1583                       break;
 1584                   case '\b':
 1585                       content.append("\\b");
 1586                       break;
 1587                   case '\f':
 1588                       content.append("\\f");
 1589                       break;
 1590                   case '(':
 1591                   case ')':
 1592                   case '\\':
 1593                       content.append_i('\\').append_i(c);
 1594                       break;
 1595                   default:
 1596                       content.append_i(c);
 1597               }
 1598           }
 1599           content.append(")");
 1600       }
 1601   
 1602       /**
 1603        * Adds a named outline to the document.
 1604        *
 1605        * @param outline the outline
 1606        * @param name the name for the local destination
 1607        */
 1608       public void addOutline(PdfOutline outline, String name) {
 1609           checkWriter();
 1610           pdf.addOutline(outline, name);
 1611       }
 1612       /**
 1613        * Gets the root outline.
 1614        *
 1615        * @return the root outline
 1616        */
 1617       public PdfOutline getRootOutline() {
 1618           checkWriter();
 1619           return pdf.getRootOutline();
 1620       }
 1621       
 1622       /**
 1623        * Computes the width of the given string taking in account
 1624        * the current values of "Character spacing", "Word Spacing"
 1625        * and "Horizontal Scaling".
 1626        * The additional spacing is not computed for the last character
 1627        * of the string.
 1628        * @param text the string to get width of
 1629        * @param kerned the kerning option
 1630        * @return the width
 1631        */
 1632   
 1633       public float getEffectiveStringWidth(String text, boolean kerned) {
 1634           BaseFont bf = state.fontDetails.getBaseFont();
 1635           
 1636           float w;
 1637           if (kerned)
 1638               w = bf.getWidthPointKerned(text, state.size);
 1639           else
 1640               w = bf.getWidthPoint(text, state.size);
 1641           
 1642           if (state.charSpace != 0.0f && text.length() > 1) {
 1643               w += state.charSpace * (text.length() -1);
 1644           }
 1645           
 1646           int ft = bf.getFontType();
 1647           if (state.wordSpace != 0.0f && (ft == BaseFont.FONT_TYPE_T1 || ft == BaseFont.FONT_TYPE_TT || ft == BaseFont.FONT_TYPE_T3)) {
 1648               for (int i = 0; i < (text.length() -1); i++) {
 1649                   if (text.charAt(i) == ' ')
 1650                       w += state.wordSpace;
 1651               }
 1652           }
 1653           if (state.scale != 100.0)
 1654               w = (w * state.scale) / 100.0f;
 1655           
 1656           //System.out.println("String width = " + Float.toString(w));
 1657           return w;
 1658       }
 1659       
 1660       /**
 1661        * Shows text right, left or center aligned with rotation.
 1662        * @param alignment the alignment can be ALIGN_CENTER, ALIGN_RIGHT or ALIGN_LEFT
 1663        * @param text the text to show
 1664        * @param x the x pivot position
 1665        * @param y the y pivot position
 1666        * @param rotation the rotation to be applied in degrees counterclockwise
 1667        */
 1668       public void showTextAligned(int alignment, String text, float x, float y, float rotation) {
 1669           showTextAligned(alignment, text, x, y, rotation, false);
 1670       }
 1671       
 1672       private void showTextAligned(int alignment, String text, float x, float y, float rotation, boolean kerned) {
 1673           if (state.fontDetails == null)
 1674               throw new NullPointerException("Font and size must be set before writing any text");
 1675           if (rotation == 0) {
 1676               switch (alignment) {
 1677                   case ALIGN_CENTER:
 1678                       x -= getEffectiveStringWidth(text, kerned) / 2;
 1679                       break;
 1680                   case ALIGN_RIGHT:
 1681                       x -= getEffectiveStringWidth(text, kerned);
 1682                       break;
 1683               }
 1684               setTextMatrix(x, y);
 1685               if (kerned)
 1686                   showTextKerned(text);
 1687               else
 1688                   showText(text);
 1689           }
 1690           else {
 1691               double alpha = rotation * Math.PI / 180.0;
 1692               float cos = (float)Math.cos(alpha);
 1693               float sin = (float)Math.sin(alpha);
 1694               float len;
 1695               switch (alignment) {
 1696                   case ALIGN_CENTER:
 1697                       len = getEffectiveStringWidth(text, kerned) / 2;
 1698                       x -=  len * cos;
 1699                       y -=  len * sin;
 1700                       break;
 1701                   case ALIGN_RIGHT:
 1702                       len = getEffectiveStringWidth(text, kerned);
 1703                       x -=  len * cos;
 1704                       y -=  len * sin;
 1705                       break;
 1706               }
 1707               setTextMatrix(cos, sin, -sin, cos, x, y);
 1708               if (kerned)
 1709                   showTextKerned(text);
 1710               else
 1711                   showText(text);
 1712               setTextMatrix(0f, 0f);
 1713           }
 1714       }
 1715       
 1716       /**
 1717        * Shows text kerned right, left or center aligned with rotation.
 1718        * @param alignment the alignment can be ALIGN_CENTER, ALIGN_RIGHT or ALIGN_LEFT
 1719        * @param text the text to show
 1720        * @param x the x pivot position
 1721        * @param y the y pivot position
 1722        * @param rotation the rotation to be applied in degrees counterclockwise
 1723        */
 1724       public void showTextAlignedKerned(int alignment, String text, float x, float y, float rotation) {
 1725           showTextAligned(alignment, text, x, y, rotation, true);
 1726       }
 1727       
 1728       /**
 1729        * Concatenate a matrix to the current transformation matrix.
 1730        * @param a an element of the transformation matrix
 1731        * @param b an element of the transformation matrix
 1732        * @param c an element of the transformation matrix
 1733        * @param d an element of the transformation matrix
 1734        * @param e an element of the transformation matrix
 1735        * @param f an element of the transformation matrix
 1736        **/
 1737       public void concatCTM(float a, float b, float c, float d, float e, float f) {
 1738           content.append(a).append(' ').append(b).append(' ').append(c).append(' ');
 1739           content.append(d).append(' ').append(e).append(' ').append(f).append(" cm").append_i(separator);
 1740       }
 1741       
 1742       /**
 1743        * Generates an array of bezier curves to draw an arc.
 1744        * <P>
 1745        * (x1, y1) and (x2, y2) are the corners of the enclosing rectangle.
 1746        * Angles, measured in degrees, start with 0 to the right (the positive X
 1747        * axis) and increase counter-clockwise.  The arc extends from startAng
 1748        * to startAng+extent.  I.e. startAng=0 and extent=180 yields an openside-down
 1749        * semi-circle.
 1750        * <P>
 1751        * The resulting coordinates are of the form float[]{x1,y1,x2,y2,x3,y3, x4,y4}
 1752        * such that the curve goes from (x1, y1) to (x4, y4) with (x2, y2) and
 1753        * (x3, y3) as their respective Bezier control points.
 1754        * <P>
 1755        * Note: this code was taken from ReportLab (www.reportlab.org), an excellent
 1756        * PDF generator for Python (BSD license: http://www.reportlab.org/devfaq.html#1.3 ).
 1757        *
 1758        * @param x1 a corner of the enclosing rectangle
 1759        * @param y1 a corner of the enclosing rectangle
 1760        * @param x2 a corner of the enclosing rectangle
 1761        * @param y2 a corner of the enclosing rectangle
 1762        * @param startAng starting angle in degrees
 1763        * @param extent angle extent in degrees
 1764        * @return a list of float[] with the bezier curves
 1765        */
 1766       public static ArrayList bezierArc(float x1, float y1, float x2, float y2, float startAng, float extent) {
 1767           float tmp;
 1768           if (x1 > x2) {
 1769               tmp = x1;
 1770               x1 = x2;
 1771               x2 = tmp;
 1772           }
 1773           if (y2 > y1) {
 1774               tmp = y1;
 1775               y1 = y2;
 1776               y2 = tmp;
 1777           }
 1778           
 1779           float fragAngle;
 1780           int Nfrag;
 1781           if (Math.abs(extent) <= 90f) {
 1782               fragAngle = extent;
 1783               Nfrag = 1;
 1784           }
 1785           else {
 1786               Nfrag = (int)(Math.ceil(Math.abs(extent)/90f));
 1787               fragAngle = extent / Nfrag;
 1788           }
 1789           float x_cen = (x1+x2)/2f;
 1790           float y_cen = (y1+y2)/2f;
 1791           float rx = (x2-x1)/2f;
 1792           float ry = (y2-y1)/2f;
 1793           float halfAng = (float)(fragAngle * Math.PI / 360.);
 1794           float kappa = (float)(Math.abs(4. / 3. * (1. - Math.cos(halfAng)) / Math.sin(halfAng)));
 1795           ArrayList pointList = new ArrayList();
 1796           for (int i = 0; i < Nfrag; ++i) {
 1797               float theta0 = (float)((startAng + i*fragAngle) * Math.PI / 180.);
 1798               float theta1 = (float)((startAng + (i+1)*fragAngle) * Math.PI / 180.);
 1799               float cos0 = (float)Math.cos(theta0);
 1800               float cos1 = (float)Math.cos(theta1);
 1801               float sin0 = (float)Math.sin(theta0);
 1802               float sin1 = (float)Math.sin(theta1);
 1803               if (fragAngle > 0f) {
 1804                   pointList.add(new float[]{x_cen + rx * cos0,
 1805                   y_cen - ry * sin0,
 1806                   x_cen + rx * (cos0 - kappa * sin0),
 1807                   y_cen - ry * (sin0 + kappa * cos0),
 1808                   x_cen + rx * (cos1 + kappa * sin1),
 1809                   y_cen - ry * (sin1 - kappa * cos1),
 1810                   x_cen + rx * cos1,
 1811                   y_cen - ry * sin1});
 1812               }
 1813               else {
 1814                   pointList.add(new float[]{x_cen + rx * cos0,
 1815                   y_cen - ry * sin0,
 1816                   x_cen + rx * (cos0 + kappa * sin0),
 1817                   y_cen - ry * (sin0 - kappa * cos0),
 1818                   x_cen + rx * (cos1 - kappa * sin1),
 1819                   y_cen - ry * (sin1 + kappa * cos1),
 1820                   x_cen + rx * cos1,
 1821                   y_cen - ry * sin1});
 1822               }
 1823           }
 1824           return pointList;
 1825       }
 1826       
 1827       /**
 1828        * Draws a partial ellipse inscribed within the rectangle x1,y1,x2,y2,
 1829        * starting at startAng degrees and covering extent degrees. Angles
 1830        * start with 0 to the right (+x) and increase counter-clockwise.
 1831        *
 1832        * @param x1 a corner of the enclosing rectangle
 1833        * @param y1 a corner of the enclosing rectangle
 1834        * @param x2 a corner of the enclosing rectangle
 1835        * @param y2 a corner of the enclosing rectangle
 1836        * @param startAng starting angle in degrees
 1837        * @param extent angle extent in degrees
 1838        */
 1839       public void arc(float x1, float y1, float x2, float y2, float startAng, float extent) {
 1840           ArrayList ar = bezierArc(x1, y1, x2, y2, startAng, extent);
 1841           if (ar.isEmpty())
 1842               return;
 1843           float pt[] = (float [])ar.get(0);
 1844           moveTo(pt[0], pt[1]);
 1845           for (int k = 0; k < ar.size(); ++k) {
 1846               pt = (float [])ar.get(k);
 1847               curveTo(pt[2], pt[3], pt[4], pt[5], pt[6], pt[7]);
 1848           }
 1849       }
 1850       
 1851       /**
 1852        * Draws an ellipse inscribed within the rectangle x1,y1,x2,y2.
 1853        *
 1854        * @param x1 a corner of the enclosing rectangle
 1855        * @param y1 a corner of the enclosing rectangle
 1856        * @param x2 a corner of the enclosing rectangle
 1857        * @param y2 a corner of the enclosing rectangle
 1858        */
 1859       public void ellipse(float x1, float y1, float x2, float y2) {
 1860           arc(x1, y1, x2, y2, 0f, 360f);
 1861       }
 1862       
 1863       /**
 1864        * Create a new colored tiling pattern.
 1865        *
 1866        * @param width the width of the pattern
 1867        * @param height the height of the pattern
 1868        * @param xstep the desired horizontal spacing between pattern cells.
 1869        * May be either positive or negative, but not zero.
 1870        * @param ystep the desired vertical spacing between pattern cells.
 1871        * May be either positive or negative, but not zero.
 1872        * @return the <CODE>PdfPatternPainter</CODE> where the pattern will be created
 1873        */
 1874       public PdfPatternPainter createPattern(float width, float height, float xstep, float ystep) {
 1875           checkWriter();
 1876           if ( xstep == 0.0f || ystep == 0.0f )
 1877               throw new RuntimeException("XStep or YStep can not be ZERO.");
 1878           PdfPatternPainter painter = new PdfPatternPainter(writer);
 1879           painter.setWidth(width);
 1880           painter.setHeight(height);
 1881           painter.setXStep(xstep);
 1882           painter.setYStep(ystep);
 1883           writer.addSimplePattern(painter);
 1884           return painter;
 1885       }
 1886       
 1887       /**
 1888        * Create a new colored tiling pattern. Variables xstep and ystep are set to the same values
 1889        * of width and height.
 1890        * @param width the width of the pattern
 1891        * @param height the height of the pattern
 1892        * @return the <CODE>PdfPatternPainter</CODE> where the pattern will be created
 1893        */
 1894       public PdfPatternPainter createPattern(float width, float height) {
 1895           return createPattern(width, height, width, height);
 1896       }
 1897       
 1898       /**
 1899        * Create a new uncolored tiling pattern.
 1900        *
 1901        * @param width the width of the pattern
 1902        * @param height the height of the pattern
 1903        * @param xstep the desired horizontal spacing between pattern cells.
 1904        * May be either positive or negative, but not zero.
 1905        * @param ystep the desired vertical spacing between pattern cells.
 1906        * May be either positive or negative, but not zero.
 1907        * @param color the default color. Can be <CODE>null</CODE>
 1908        * @return the <CODE>PdfPatternPainter</CODE> where the pattern will be created
 1909        */
 1910       public PdfPatternPainter createPattern(float width, float height, float xstep, float ystep, Color color) {
 1911           checkWriter();
 1912           if ( xstep == 0.0f || ystep == 0.0f )
 1913               throw new RuntimeException("XStep or YStep can not be ZERO.");
 1914           PdfPatternPainter painter = new PdfPatternPainter(writer, color);
 1915           painter.setWidth(width);
 1916           painter.setHeight(height);
 1917           painter.setXStep(xstep);
 1918           painter.setYStep(ystep);
 1919           writer.addSimplePattern(painter);
 1920           return painter;
 1921       }
 1922       
 1923       /**
 1924        * Create a new uncolored tiling pattern.
 1925        * Variables xstep and ystep are set to the same values
 1926        * of width and height.
 1927        * @param width the width of the pattern
 1928        * @param height the height of the pattern
 1929        * @param color the default color. Can be <CODE>null</CODE>
 1930        * @return the <CODE>PdfPatternPainter</CODE> where the pattern will be created
 1931        */
 1932       public PdfPatternPainter createPattern(float width, float height, Color color) {
 1933           return createPattern(width, height, width, height, color);
 1934       }
 1935       
 1936       /**
 1937        * Creates a new template.
 1938        * <P>
 1939        * Creates a new template that is nothing more than a form XObject. This template can be included
 1940        * in this <CODE>PdfContentByte</CODE> or in another template. Templates are only written
 1941        * to the output when the document is closed permitting things like showing text in the first page
 1942        * that is only defined in the last page.
 1943        *
 1944        * @param width the bounding box width
 1945        * @param height the bounding box height
 1946        * @return the created template
 1947        */
 1948       public PdfTemplate createTemplate(float width, float height) {
 1949           return createTemplate(width, height, null);
 1950       }
 1951       
 1952       PdfTemplate createTemplate(float width, float height, PdfName forcedName) {
 1953           checkWriter();
 1954           PdfTemplate template = new PdfTemplate(writer);
 1955           template.setWidth(width);
 1956           template.setHeight(height);
 1957           writer.addDirectTemplateSimple(template, forcedName);
 1958           return template;
 1959       }
 1960       
 1961       /**
 1962        * Creates a new appearance to be used with form fields.
 1963        *
 1964        * @param width the bounding box width
 1965        * @param height the bounding box height
 1966        * @return the appearance created
 1967        */
 1968       public PdfAppearance createAppearance(float width, float height) {
 1969           return createAppearance(width, height, null);
 1970       }
 1971       
 1972       PdfAppearance createAppearance(float width, float height, PdfName forcedName) {
 1973           checkWriter();
 1974           PdfAppearance template = new PdfAppearance(writer);
 1975           template.setWidth(width);
 1976           template.setHeight(height);
 1977           writer.addDirectTemplateSimple(template, forcedName);
 1978           return template;
 1979       }
 1980       
 1981       /**
 1982        * Adds a PostScript XObject to this content.
 1983        *
 1984        * @param psobject the object
 1985        */
 1986       public void addPSXObject(PdfPSXObject psobject) {
 1987           checkWriter();
 1988           PdfName name = writer.addDirectTemplateSimple(psobject, null);
 1989           PageResources prs = getPageResources();
 1990           name = prs.addXObject(name, psobject.getIndirectReference());
 1991           content.append(name.getBytes()).append(" Do").append_i(separator);
 1992       }
 1993       
 1994       /**
 1995        * Adds a template to this content.
 1996        *
 1997        * @param template the template
 1998        * @param a an element of the transformation matrix
 1999        * @param b an element of the transformation matrix
 2000        * @param c an element of the transformation matrix
 2001        * @param d an element of the transformation matrix
 2002        * @param e an element of the transformation matrix
 2003        * @param f an element of the transformation matrix
 2004        */
 2005       public void addTemplate(PdfTemplate template, float a, float b, float c, float d, float e, float f) {
 2006           checkWriter();
 2007           checkNoPattern(template);
 2008           PdfName name = writer.addDirectTemplateSimple(template, null);
 2009           PageResources prs = getPageResources();
 2010           name = prs.addXObject(name, template.getIndirectReference());
 2011           content.append("q ");
 2012           content.append(a).append(' ');
 2013           content.append(b).append(' ');
 2014           content.append(c).append(' ');
 2015           content.append(d).append(' ');
 2016           content.append(e).append(' ');
 2017           content.append(f).append(" cm ");
 2018           content.append(name.getBytes()).append(" Do Q").append_i(separator);
 2019       }
 2020       
 2021       void addTemplateReference(PdfIndirectReference template, PdfName name, float a, float b, float c, float d, float e, float f) {
 2022           checkWriter();
 2023           PageResources prs = getPageResources();
 2024           name = prs.addXObject(name, template);
 2025           content.append("q ");
 2026           content.append(a).append(' ');
 2027           content.append(b).append(' ');
 2028           content.append(c).append(' ');
 2029           content.append(d).append(' ');
 2030           content.append(e).append(' ');
 2031           content.append(f).append(" cm ");
 2032           content.append(name.getBytes()).append(" Do Q").append_i(separator);
 2033       }
 2034       
 2035       /**
 2036        * Adds a template to this content.
 2037        *
 2038        * @param template the template
 2039        * @param x the x location of this template
 2040        * @param y the y location of this template
 2041        */
 2042       public void addTemplate(PdfTemplate template, float x, float y) {
 2043           addTemplate(template, 1, 0, 0, 1, x, y);
 2044       }
 2045       
 2046       /**
 2047        * Changes the current color for filling paths (device dependent colors!).
 2048        * <P>
 2049        * Sets the color space to <B>DeviceCMYK</B> (or the <B>DefaultCMYK</B> color space),
 2050        * and sets the color to use for filling paths.</P>
 2051        * <P>
 2052        * This method is described in the 'Portable Document Format Reference Manual version 1.3'
 2053        * section 8.5.2.1 (page 331).</P>
 2054        * <P>
 2055        * Following the PDF manual, each operand must be a number between 0 (no ink) and
 2056        * 1 (maximum ink). This method however accepts only integers between 0x00 and 0xFF.</P>
 2057        *
 2058        * @param cyan the intensity of cyan
 2059        * @param magenta the intensity of magenta
 2060        * @param yellow the intensity of yellow
 2061        * @param black the intensity of black
 2062        */
 2063       
 2064       public void setCMYKColorFill(int cyan, int magenta, int yellow, int black) {
 2065           content.append((float)(cyan & 0xFF) / 0xFF);
 2066           content.append(' ');
 2067           content.append((float)(magenta & 0xFF) / 0xFF);
 2068           content.append(' ');
 2069           content.append((float)(yellow & 0xFF) / 0xFF);
 2070           content.append(' ');
 2071           content.append((float)(black & 0xFF) / 0xFF);
 2072           content.append(" k").append_i(separator);
 2073       }
 2074       /**
 2075        * Changes the current color for stroking paths (device dependent colors!).
 2076        * <P>
 2077        * Sets the color space to <B>DeviceCMYK</B> (or the <B>DefaultCMYK</B> color space),
 2078        * and sets the color to use for stroking paths.</P>
 2079        * <P>
 2080        * This method is described in the 'Portable Document Format Reference Manual version 1.3'
 2081        * section 8.5.2.1 (page 331).</P>
 2082        * Following the PDF manual, each operand must be a number between 0 (minimum intensity) and
 2083        * 1 (maximum intensity). This method however accepts only integers between 0x00 and 0xFF.
 2084        *
 2085        * @param cyan the intensity of red
 2086        * @param magenta the intensity of green
 2087        * @param yellow the intensity of blue
 2088        * @param black the intensity of black
 2089        */
 2090       
 2091       public void setCMYKColorStroke(int cyan, int magenta, int yellow, int black) {
 2092           content.append((float)(cyan & 0xFF) / 0xFF);
 2093           content.append(' ');
 2094           content.append((float)(magenta & 0xFF) / 0xFF);
 2095           content.append(' ');
 2096           content.append((float)(yellow & 0xFF) / 0xFF);
 2097           content.append(' ');
 2098           content.append((float)(black & 0xFF) / 0xFF);
 2099           content.append(" K").append_i(separator);
 2100       }
 2101       
 2102       /**
 2103        * Changes the current color for filling paths (device dependent colors!).
 2104        * <P>
 2105        * Sets the color space to <B>DeviceRGB</B> (or the <B>DefaultRGB</B> color space),
 2106        * and sets the color to use for filling paths.</P>
 2107        * <P>
 2108        * This method is described in the 'Portable Document Format Reference Manual version 1.3'
 2109        * section 8.5.2.1 (page 331).</P>
 2110        * <P>
 2111        * Following the PDF manual, each operand must be a number between 0 (minimum intensity) and
 2112        * 1 (maximum intensity). This method however accepts only integers between 0x00 and 0xFF.</P>
 2113        *
 2114        * @param red the intensity of red
 2115        * @param green the intensity of green
 2116        * @param blue the intensity of blue
 2117        */
 2118       
 2119       public void setRGBColorFill(int red, int green, int blue) {
 2120           HelperRGB((float)(red & 0xFF) / 0xFF, (float)(green & 0xFF) / 0xFF, (float)(blue & 0xFF) / 0xFF);
 2121           content.append(" rg").append_i(separator);
 2122       }
 2123       
 2124       /**
 2125        * Changes the current color for stroking paths (device dependent colors!).
 2126        * <P>
 2127        * Sets the color space to <B>DeviceRGB</B> (or the <B>DefaultRGB</B> color space),
 2128        * and sets the color to use for stroking paths.</P>
 2129        * <P>
 2130        * This method is described in the 'Portable Document Format Reference Manual version 1.3'
 2131        * section 8.5.2.1 (page 331).</P>
 2132        * Following the PDF manual, each operand must be a number between 0 (minimum intensity) and
 2133        * 1 (maximum intensity). This method however accepts only integers between 0x00 and 0xFF.
 2134        *
 2135        * @param red the intensity of red
 2136        * @param green the intensity of green
 2137        * @param blue the intensity of blue
 2138        */
 2139       
 2140       public void setRGBColorStroke(int red, int green, int blue) {
 2141           HelperRGB((float)(red & 0xFF) / 0xFF, (float)(green & 0xFF) / 0xFF, (float)(blue & 0xFF) / 0xFF);
 2142           content.append(" RG").append_i(separator);
 2143       }
 2144       
 2145       /** Sets the stroke color. <CODE>color</CODE> can be an
 2146        * <CODE>ExtendedColor</CODE>.
 2147        * @param color the color
 2148        */
 2149       public void setColorStroke(Color color) {
 2150       	PdfXConformanceImp.checkPDFXConformance(writer, PdfXConformanceImp.PDFXKEY_COLOR, color);
 2151           int type = ExtendedColor.getType(color);
 2152           switch (type) {
 2153               case ExtendedColor.TYPE_GRAY: {
 2154                   setGrayStroke(((GrayColor)color).getGray());
 2155                   break;
 2156               }
 2157               case ExtendedColor.TYPE_CMYK: {
 2158                   CMYKColor cmyk = (CMYKColor)color;
 2159                   setCMYKColorStrokeF(cmyk.getCyan(), cmyk.getMagenta(), cmyk.getYellow(), cmyk.getBlack());
 2160                   break;
 2161               }
 2162               case ExtendedColor.TYPE_SEPARATION: {
 2163                   SpotColor spot = (SpotColor)color;
 2164                   setColorStroke(spot.getPdfSpotColor(), spot.getTint());
 2165                   break;
 2166               }
 2167               case ExtendedColor.TYPE_PATTERN: {
 2168                   PatternColor pat = (PatternColor) color;
 2169                   setPatternStroke(pat.getPainter());
 2170                   break;
 2171               }
 2172               case ExtendedColor.TYPE_SHADING: {
 2173                   ShadingColor shading = (ShadingColor) color;
 2174                   setShadingStroke(shading.getPdfShadingPattern());
 2175                   break;
 2176               }
 2177               default:
 2178                   setRGBColorStroke(color.getRed(), color.getGreen(), color.getBlue());
 2179           }
 2180       }
 2181       
 2182       /** Sets the fill color. <CODE>color</CODE> can be an
 2183        * <CODE>ExtendedColor</CODE>.
 2184        * @param color the color
 2185        */
 2186       public void setColorFill(Color color) {
 2187       	PdfXConformanceImp.checkPDFXConformance(writer, PdfXConformanceImp.PDFXKEY_COLOR, color);
 2188           int type = ExtendedColor.getType(color);
 2189           switch (type) {
 2190               case ExtendedColor.TYPE_GRAY: {
 2191                   setGrayFill(((GrayColor)color).getGray());
 2192                   break;
 2193               }
 2194               case ExtendedColor.TYPE_CMYK: {
 2195                   CMYKColor cmyk = (CMYKColor)color;
 2196                   setCMYKColorFillF(cmyk.getCyan(), cmyk.getMagenta(), cmyk.getYellow(), cmyk.getBlack());
 2197                   break;
 2198               }
 2199               case ExtendedColor.TYPE_SEPARATION: {
 2200                   SpotColor spot = (SpotColor)color;
 2201                   setColorFill(spot.getPdfSpotColor(), spot.getTint());
 2202                   break;
 2203               }
 2204               case ExtendedColor.TYPE_PATTERN: {
 2205                   PatternColor pat = (PatternColor) color;
 2206                   setPatternFill(pat.getPainter());
 2207                   break;
 2208               }
 2209               case ExtendedColor.TYPE_SHADING: {
 2210                   ShadingColor shading = (ShadingColor) color;
 2211                   setShadingFill(shading.getPdfShadingPattern());
 2212                   break;
 2213               }
 2214               default:
 2215                   setRGBColorFill(color.getRed(), color.getGreen(), color.getBlue());
 2216           }
 2217       }
 2218       
 2219       /** Sets the fill color to a spot color.
 2220        * @param sp the spot color
 2221        * @param tint the tint for the spot color. 0 is no color and 1
 2222        * is 100% color
 2223        */
 2224       public void setColorFill(PdfSpotColor sp, float tint) {
 2225           checkWriter();
 2226           state.colorDetails = writer.addSimple(sp);
 2227           PageResources prs = getPageResources();
 2228           PdfName name = state.colorDetails.getColorName();
 2229           name = prs.addColor(name, state.colorDetails.getIndirectReference());
 2230           content.append(name.getBytes()).append(" cs ").append(tint).append(" scn").append_i(separator);
 2231       }
 2232       
 2233       /** Sets the stroke color to a spot color.
 2234        * @param sp the spot color
 2235        * @param tint the tint for the spot color. 0 is no color and 1
 2236        * is 100% color
 2237        */
 2238       public void setColorStroke(PdfSpotColor sp, float tint) {
 2239           checkWriter();
 2240           state.colorDetails = writer.addSimple(sp);
 2241           PageResources prs = getPageResources();
 2242           PdfName name = state.colorDetails.getColorName();
 2243           name = prs.addColor(name, state.colorDetails.getIndirectReference());
 2244           content.append(name.getBytes()).append(" CS ").append(tint).append(" SCN").append_i(separator);
 2245       }
 2246       
 2247       /** Sets the fill color to a pattern. The pattern can be
 2248        * colored or uncolored.
 2249        * @param p the pattern
 2250        */
 2251       public void setPatternFill(PdfPatternPainter p) {
 2252           if (p.isStencil()) {
 2253               setPatternFill(p, p.getDefaultColor());
 2254               return;
 2255           }
 2256           checkWriter();
 2257           PageResources prs = getPageResources();
 2258           PdfName name = writer.addSimplePattern(p);
 2259           name = prs.addPattern(name, p.getIndirectReference());
 2260           content.append(PdfName.PATTERN.getBytes()).append(" cs ").append(name.getBytes()).append(" scn").append_i(separator);
 2261       }
 2262       
 2263       /** Outputs the color values to the content.
 2264        * @param color The color
 2265        * @param tint the tint if it is a spot color, ignored otherwise
 2266        */
 2267       void outputColorNumbers(Color color, float tint) {
 2268       	PdfXConformanceImp.checkPDFXConformance(writer, PdfXConformanceImp.PDFXKEY_COLOR, color);
 2269           int type = ExtendedColor.getType(color);
 2270           switch (type) {
 2271               case ExtendedColor.TYPE_RGB:
 2272                   content.append((float)(color.getRed()) / 0xFF);
 2273                   content.append(' ');
 2274                   content.append((float)(color.getGreen()) / 0xFF);
 2275                   content.append(' ');
 2276                   content.append((float)(color.getBlue()) / 0xFF);
 2277                   break;
 2278               case ExtendedColor.TYPE_GRAY:
 2279                   content.append(((GrayColor)color).getGray());
 2280                   break;
 2281               case ExtendedColor.TYPE_CMYK: {
 2282                   CMYKColor cmyk = (CMYKColor)color;
 2283                   content.append(cmyk.getCyan()).append(' ').append(cmyk.getMagenta());
 2284                   content.append(' ').append(cmyk.getYellow()).append(' ').append(cmyk.getBlack());
 2285                   break;
 2286               }
 2287               case ExtendedColor.TYPE_SEPARATION:
 2288                   content.append(tint);
 2289                   break;
 2290               default:
 2291                   throw new RuntimeException("Invalid color type.");
 2292           }
 2293       }
 2294       
 2295       /** Sets the fill color to an uncolored pattern.
 2296        * @param p the pattern
 2297        * @param color the color of the pattern
 2298        */
 2299       public void setPatternFill(PdfPatternPainter p, Color color) {
 2300           if (ExtendedColor.getType(color) == ExtendedColor.TYPE_SEPARATION)
 2301               setPatternFill(p, color, ((SpotColor)color).getTint());
 2302           else
 2303               setPatternFill(p, color, 0);
 2304       }
 2305       
 2306       /** Sets the fill color to an uncolored pattern.
 2307        * @param p the pattern
 2308        * @param color the color of the pattern
 2309        * @param tint the tint if the color is a spot color, ignored otherwise
 2310        */
 2311       public void setPatternFill(PdfPatternPainter p, Color color, float tint) {
 2312           checkWriter();
 2313           if (!p.isStencil())
 2314               throw new RuntimeException("An uncolored pattern was expected.");
 2315           PageResources prs = getPageResources();
 2316           PdfName name = writer.addSimplePattern(p);
 2317           name = prs.addPattern(name, p.getIndirectReference());
 2318           ColorDetails csDetail = writer.addSimplePatternColorspace(color);
 2319           PdfName cName = prs.addColor(csDetail.getColorName(), csDetail.getIndirectReference());
 2320           content.append(cName.getBytes()).append(" cs").append_i(separator);
 2321           outputColorNumbers(color, tint);
 2322           content.append(' ').append(name.getBytes()).append(" scn").append_i(separator);
 2323       }
 2324       
 2325       /** Sets the stroke color to an uncolored pattern.
 2326        * @param p the pattern
 2327        * @param color the color of the pattern
 2328        */
 2329       public void setPatternStroke(PdfPatternPainter p, Color color) {
 2330           if (ExtendedColor.getType(color) == ExtendedColor.TYPE_SEPARATION)
 2331               setPatternStroke(p, color, ((SpotColor)color).getTint());
 2332           else
 2333               setPatternStroke(p, color, 0);
 2334       }
 2335       
 2336       /** Sets the stroke color to an uncolored pattern.
 2337        * @param p the pattern
 2338        * @param color the color of the pattern
 2339        * @param tint the tint if the color is a spot color, ignored otherwise
 2340        */
 2341       public void setPatternStroke(PdfPatternPainter p, Color color, float tint) {
 2342           checkWriter();
 2343           if (!p.isStencil())
 2344               throw new RuntimeException("An uncolored pattern was expected.");
 2345           PageResources prs = getPageResources();
 2346           PdfName name = writer.addSimplePattern(p);
 2347           name = prs.addPattern(name, p.getIndirectReference());
 2348           ColorDetails csDetail = writer.addSimplePatternColorspace(color);
 2349           PdfName cName = prs.addColor(csDetail.getColorName(), csDetail.getIndirectReference());
 2350           content.append(cName.getBytes()).append(" CS").append_i(separator);
 2351           outputColorNumbers(color, tint);
 2352           content.append(' ').append(name.getBytes()).append(" SCN").append_i(separator);
 2353       }
 2354       
 2355       /** Sets the stroke color to a pattern. The pattern can be
 2356        * colored or uncolored.
 2357        * @param p the pattern
 2358        */
 2359       public void setPatternStroke(PdfPatternPainter p) {
 2360           if (p.isStencil()) {
 2361               setPatternStroke(p, p.getDefaultColor());
 2362               return;
 2363           }
 2364           checkWriter();
 2365           PageResources prs = getPageResources();
 2366           PdfName name = writer.addSimplePattern(p);
 2367           name = prs.addPattern(name, p.getIndirectReference());
 2368           content.append(PdfName.PATTERN.getBytes()).append(" CS ").append(name.getBytes()).append(" SCN").append_i(separator);
 2369       }
 2370       
 2371       /**
 2372        * Paints using a shading object. 
 2373        * @param shading the shading object
 2374        */
 2375       public void paintShading(PdfShading shading) {
 2376           writer.addSimpleShading(shading);
 2377           PageResources prs = getPageResources();
 2378           PdfName name = prs.addShading(shading.getShadingName(), shading.getShadingReference());
 2379           content.append(name.getBytes()).append(" sh").append_i(separator);
 2380           ColorDetails details = shading.getColorDetails();
 2381           if (details != null)
 2382               prs.addColor(details.getColorName(), details.getIndirectReference());
 2383       }
 2384       
 2385       /**
 2386        * Paints using a shading pattern. 
 2387        * @param shading the shading pattern
 2388        */
 2389       public void paintShading(PdfShadingPattern shading) {
 2390           paintShading(shading.getShading());
 2391       }
 2392       
 2393       /**
 2394        * Sets the shading fill pattern.
 2395        * @param shading the shading pattern
 2396        */
 2397       public void setShadingFill(PdfShadingPattern shading) {
 2398           writer.addSimpleShadingPattern(shading);
 2399           PageResources prs = getPageResources();
 2400           PdfName name = prs.addPattern(shading.getPatternName(), shading.getPatternReference());
 2401           content.append(PdfName.PATTERN.getBytes()).append(" cs ").append(name.getBytes()).append(" scn").append_i(separator);
 2402           ColorDetails details = shading.getColorDetails();
 2403           if (details != null)
 2404               prs.addColor(details.getColorName(), details.getIndirectReference());
 2405       }
 2406       
 2407       /**
 2408        * Sets the shading stroke pattern
 2409        * @param shading the shading pattern
 2410        */
 2411       public void setShadingStroke(PdfShadingPattern shading) {
 2412           writer.addSimpleShadingPattern(shading);
 2413           PageResources prs = getPageResources();
 2414           PdfName name = prs.addPattern(shading.getPatternName(), shading.getPatternReference());
 2415           content.append(PdfName.PATTERN.getBytes()).append(" CS ").append(name.getBytes()).append(" SCN").append_i(separator);
 2416           ColorDetails details = shading.getColorDetails();
 2417           if (details != null)
 2418               prs.addColor(details.getColorName(), details.getIndirectReference());
 2419       }
 2420       
 2421       /** Check if we have a valid PdfWriter.
 2422        *
 2423        */
 2424       protected void checkWriter() {
 2425           if (writer == null)
 2426               throw new NullPointerException("The writer in PdfContentByte is null.");
 2427       }
 2428       
 2429       /**
 2430        * Show an array of text.
 2431        * @param text array of text
 2432        */
 2433       public void showText(PdfTextArray text) {
 2434           if (state.fontDetails == null)
 2435               throw new NullPointerException("Font and size must be set before writing any text");
 2436           content.append("[");
 2437           ArrayList arrayList = text.getArrayList();
 2438           boolean lastWasNumber = false;
 2439           for (int k = 0; k < arrayList.size(); ++k) {
 2440               Object obj = arrayList.get(k);
 2441               if (obj instanceof String) {
 2442                   showText2((String)obj);
 2443                   lastWasNumber = false;
 2444               }
 2445               else {
 2446                   if (lastWasNumber)
 2447                       content.append(' ');
 2448                   else
 2449                       lastWasNumber = true;
 2450                   content.append(((Float)obj).floatValue());
 2451               }
 2452           }
 2453           content.append("]TJ").append_i(separator);
 2454       }
 2455       
 2456       /**
 2457        * Gets the <CODE>PdfWriter</CODE> in use by this object.
 2458        * @return the <CODE>PdfWriter</CODE> in use by this object
 2459        */
 2460       public PdfWriter getPdfWriter() {
 2461           return writer;
 2462       }
 2463       
 2464       /**
 2465        * Gets the <CODE>PdfDocument</CODE> in use by this object.
 2466        * @return the <CODE>PdfDocument</CODE> in use by this object
 2467        */
 2468       public PdfDocument getPdfDocument() {
 2469           return pdf;
 2470       }
 2471       
 2472       /**
 2473        * Implements a link to other part of the document. The jump will
 2474        * be made to a local destination with the same name, that must exist.
 2475        * @param name the name for this link
 2476        * @param llx the lower left x corner of the activation area
 2477        * @param lly the lower left y corner of the activation area
 2478        * @param urx the upper right x corner of the activation area
 2479        * @param ury the upper right y corner of the activation area
 2480        */
 2481       public void localGoto(String name, float llx, float lly, float urx, float ury) {
 2482           pdf.localGoto(name, llx, lly, urx, ury);
 2483       }
 2484       
 2485       /**
 2486        * The local destination to where a local goto with the same
 2487        * name will jump.
 2488        * @param name the name of this local destination
 2489        * @param destination the <CODE>PdfDestination</CODE> with the jump coordinates
 2490        * @return <CODE>true</CODE> if the local destination was added,
 2491        * <CODE>false</CODE> if a local destination with the same name
 2492        * already exists
 2493        */
 2494       public boolean localDestination(String name, PdfDestination destination) {
 2495           return pdf.localDestination(name, destination);
 2496       }
 2497       
 2498       /**
 2499        * Gets a duplicate of this <CODE>PdfContentByte</CODE>. All
 2500        * the members are copied by reference but the buffer stays different.
 2501        *
 2502        * @return a copy of this <CODE>PdfContentByte</CODE>
 2503        */
 2504       public PdfContentByte getDuplicate() {
 2505           return new PdfContentByte(writer);
 2506       }
 2507       
 2508       /**
 2509        * Implements a link to another document.
 2510        * @param filename the filename for the remote document
 2511        * @param name the name to jump to
 2512        * @param llx the lower left x corner of the activation area
 2513        * @param lly the lower left y corner of the activation area
 2514        * @param urx the upper right x corner of the activation area
 2515        * @param ury the upper right y corner of the activation area
 2516        */
 2517       public void remoteGoto(String filename, String name, float llx, float lly, float urx, float ury) {
 2518           pdf.remoteGoto(filename, name, llx, lly, urx, ury);
 2519       }
 2520       
 2521       /**
 2522        * Implements a link to another document.
 2523        * @param filename the filename for the remote document
 2524        * @param page the page to jump to
 2525        * @param llx the lower left x corner of the activation area
 2526        * @param lly the lower left y corner of the activation area
 2527        * @param urx the upper right x corner of the activation area
 2528        * @param ury the upper right y corner of the activation area
 2529        */
 2530       public void remoteGoto(String filename, int page, float llx, float lly, float urx, float ury) {
 2531           pdf.remoteGoto(filename, page, llx, lly, urx, ury);
 2532       }
 2533       /**
 2534        * Adds a round rectangle to the current path.
 2535        *
 2536        * @param x x-coordinate of the starting point
 2537        * @param y y-coordinate of the starting point
 2538        * @param w width
 2539        * @param h height
 2540        * @param r radius of the arc corner
 2541        */
 2542       public void roundRectangle(float x, float y, float w, float h, float r) {
 2543           if (w < 0) {
 2544               x += w;
 2545               w = -w;
 2546           }
 2547           if (h < 0) {
 2548               y += h;
 2549               h = -h;
 2550           }
 2551           if (r < 0)
 2552               r = -r;
 2553           float b = 0.4477f;
 2554           moveTo(x + r, y);
 2555           lineTo(x + w - r, y);
 2556           curveTo(x + w - r * b, y, x + w, y + r * b, x + w, y + r);
 2557           lineTo(x + w, y + h - r);
 2558           curveTo(x + w, y + h - r * b, x + w - r * b, y + h, x + w - r, y + h);
 2559           lineTo(x + r, y + h);
 2560           curveTo(x + r * b, y + h, x, y + h - r * b, x, y + h - r);
 2561           lineTo(x, y + r);
 2562           curveTo(x, y + r * b, x + r * b, y, x + r, y);
 2563       }
 2564       
 2565       /** Implements an action in an area.
 2566        * @param action the <CODE>PdfAction</CODE>
 2567        * @param llx the lower left x corner of the activation area
 2568        * @param lly the lower left y corner of the activation area
 2569        * @param urx the upper right x corner of the activation area
 2570        * @param ury the upper right y corner of the activation area
 2571        */
 2572       public void setAction(PdfAction action, float llx, float lly, float urx, float ury) {
 2573           pdf.setAction(action, llx, lly, urx, ury);
 2574       }
 2575       
 2576       /** Outputs a <CODE>String</CODE> directly to the content.
 2577        * @param s the <CODE>String</CODE>
 2578        */
 2579       public void setLiteral(String s) {
 2580           content.append(s);
 2581       }
 2582       
 2583       /** Outputs a <CODE>char</CODE> directly to the content.
 2584        * @param c the <CODE>char</CODE>
 2585        */
 2586       public void setLiteral(char c) {
 2587           content.append(c);
 2588       }
 2589       
 2590       /** Outputs a <CODE>float</CODE> directly to the content.
 2591        * @param n the <CODE>float</CODE>
 2592        */
 2593       public void setLiteral(float n) {
 2594           content.append(n);
 2595       }
 2596       
 2597       /** Throws an error if it is a pattern.
 2598        * @param t the object to check
 2599        */
 2600       void checkNoPattern(PdfTemplate t) {
 2601           if (t.getType() == PdfTemplate.TYPE_PATTERN)
 2602               throw new RuntimeException("Invalid use of a pattern. A template was expected.");
 2603       }
 2604       
 2605       /**
 2606        * Draws a TextField.
 2607        * @param llx
 2608        * @param lly
 2609        * @param urx
 2610        * @param ury
 2611        * @param on
 2612        */
 2613       public void drawRadioField(float llx, float lly, float urx, float ury, boolean on) {
 2614           if (llx > urx) { float x = llx; llx = urx; urx = x; }
 2615           if (lly > ury) { float y = lly; lly = ury; ury = y; }
 2616           // silver circle
 2617           setLineWidth(1);
 2618           setLineCap(1);
 2619           setColorStroke(new Color(0xC0, 0xC0, 0xC0));
 2620           arc(llx + 1f, lly + 1f, urx - 1f, ury - 1f, 0f, 360f);
 2621           stroke();
 2622           // gray circle-segment
 2623           setLineWidth(1);
 2624           setLineCap(1);
 2625           setColorStroke(new Color(0xA0, 0xA0, 0xA0));
 2626           arc(llx + 0.5f, lly + 0.5f, urx - 0.5f, ury - 0.5f, 45, 180);
 2627           stroke();
 2628           // black circle-segment
 2629           setLineWidth(1);
 2630           setLineCap(1);
 2631           setColorStroke(new Color(0x00, 0x00, 0x00));
 2632           arc(llx + 1.5f, lly + 1.5f, urx - 1.5f, ury - 1.5f, 45, 180);
 2633           stroke();
 2634           if (on) {
 2635               // gray circle
 2636               setLineWidth(1);
 2637               setLineCap(1);
 2638               setColorFill(new Color(0x00, 0x00, 0x00));
 2639               arc(llx + 4f, lly + 4f, urx - 4f, ury - 4f, 0, 360);
 2640               fill();
 2641           }
 2642       }
 2643       
 2644       /**
 2645        * Draws a TextField.
 2646        * @param llx
 2647        * @param lly
 2648        * @param urx
 2649        * @param ury
 2650        */   
 2651       public void drawTextField(float llx, float lly, float urx, float ury) {
 2652           if (llx > urx) { float x = llx; llx = urx; urx = x; }
 2653           if (lly > ury) { float y = lly; lly = ury; ury = y; }
 2654           // silver rectangle not filled
 2655           setColorStroke(new Color(0xC0, 0xC0, 0xC0));
 2656           setLineWidth(1);
 2657           setLineCap(0);
 2658           rectangle(llx, lly, urx - llx, ury - lly);
 2659           stroke();
 2660           // white rectangle filled
 2661           setLineWidth(1);
 2662           setLineCap(0);
 2663           setColorFill(new Color(0xFF, 0xFF, 0xFF));
 2664           rectangle(llx + 0.5f, lly + 0.5f, urx - llx - 1f, ury -lly - 1f);
 2665           fill();
 2666           // silver lines
 2667           setColorStroke(new Color(0xC0, 0xC0, 0xC0));
 2668           setLineWidth(1);
 2669           setLineCap(0);
 2670           moveTo(llx + 1f, lly + 1.5f);
 2671           lineTo(urx - 1.5f, lly + 1.5f);
 2672           lineTo(urx - 1.5f, ury - 1f);
 2673           stroke();
 2674           // gray lines
 2675           setColorStroke(new Color(0xA0, 0xA0, 0xA0));
 2676           setLineWidth(1);
 2677           setLineCap(0);
 2678           moveTo(llx + 1f, lly + 1);
 2679           lineTo(llx + 1f, ury - 1f);
 2680           lineTo(urx - 1f, ury - 1f);
 2681           stroke();
 2682           // black lines
 2683           setColorStroke(new Color(0x00, 0x00, 0x00));
 2684           setLineWidth(1);
 2685           setLineCap(0);
 2686           moveTo(llx + 2f, lly + 2f);
 2687           lineTo(llx + 2f, ury - 2f);
 2688           lineTo(urx - 2f, ury - 2f);
 2689           stroke();
 2690       }
 2691       
 2692       /**
 2693        * Draws a button.
 2694        * @param llx
 2695        * @param lly
 2696        * @param urx
 2697        * @param ury
 2698        * @param text
 2699        * @param bf
 2700        * @param size
 2701        */
 2702       public void drawButton(float llx, float lly, float urx, float ury, String text, BaseFont bf, float size) {
 2703           if (llx > urx) { float x = llx; llx = urx; urx = x; }
 2704           if (lly > ury) { float y = lly; lly = ury; ury = y; }
 2705           // black rectangle not filled
 2706           setColorStroke(new Color(0x00, 0x00, 0x00));
 2707           setLineWidth(1);
 2708           setLineCap(0);
 2709           rectangle(llx, lly, urx - llx, ury - lly);
 2710           stroke();
 2711           // silver rectangle filled
 2712           setLineWidth(1);
 2713           setLineCap(0);
 2714           setColorFill(new Color(0xC0, 0xC0, 0xC0));
 2715           rectangle(llx + 0.5f, lly + 0.5f, urx - llx - 1f, ury -lly - 1f);
 2716           fill();
 2717           // white lines
 2718           setColorStroke(new Color(0xFF, 0xFF, 0xFF));
 2719           setLineWidth(1);
 2720           setLineCap(0);
 2721           moveTo(llx + 1f, lly + 1f);
 2722           lineTo(llx + 1f, ury - 1f);
 2723           lineTo(urx - 1f, ury - 1f);
 2724           stroke();
 2725           // dark grey lines
 2726           setColorStroke(new Color(0xA0, 0xA0, 0xA0));
 2727           setLineWidth(1);
 2728           setLineCap(0);
 2729           moveTo(llx + 1f, lly + 1f);
 2730           lineTo(urx - 1f, lly + 1f);
 2731           lineTo(urx - 1f, ury - 1f);
 2732           stroke();
 2733           // text
 2734           resetRGBColorFill();
 2735           beginText();
 2736           setFontAndSize(bf, size);
 2737           showTextAligned(PdfContentByte.ALIGN_CENTER, text, llx + (urx - llx) / 2, lly + (ury - lly - size) / 2, 0);
 2738           endText();
 2739       }
 2740       
 2741       /** Gets a <CODE>Graphics2D</CODE> to write on. The graphics
 2742        * are translated to PDF commands as shapes. No PDF fonts will appear.
 2743        * @param width the width of the panel
 2744        * @param height the height of the panel
 2745        * @return a <CODE>Graphics2D</CODE>
 2746        */
 2747       public java.awt.Graphics2D createGraphicsShapes(float width, float height) {
 2748           return new PdfGraphics2D(this, width, height, null, true, false, 0);
 2749       }
 2750       
 2751       /** Gets a <CODE>Graphics2D</CODE> to print on. The graphics
 2752        * are translated to PDF commands as shapes. No PDF fonts will appear.
 2753        * @param width the width of the panel
 2754        * @param height the height of the panel
 2755        * @param printerJob a printer job
 2756        * @return a <CODE>Graphics2D</CODE>
 2757        */
 2758       public java.awt.Graphics2D createPrinterGraphicsShapes(float width, float height, PrinterJob printerJob) {
 2759           return new PdfPrinterGraphics2D(this, width, height, null, true, false, 0, printerJob);
 2760       }
 2761   
 2762       /** Gets a <CODE>Graphics2D</CODE> to write on. The graphics
 2763        * are translated to PDF commands.
 2764        * @param width the width of the panel
 2765        * @param height the height of the panel
 2766        * @return a <CODE>Graphics2D</CODE>
 2767        */
 2768       public java.awt.Graphics2D createGraphics(float width, float height) {
 2769           return new PdfGraphics2D(this, width, height, null, false, false, 0);
 2770       }
 2771       
 2772       /** Gets a <CODE>Graphics2D</CODE> to print on. The graphics
 2773        * are translated to PDF commands.
 2774        * @param width the width of the panel
 2775        * @param height the height of the panel
 2776        * @param printerJob
 2777        * @return a <CODE>Graphics2D</CODE>
 2778        */
 2779       public java.awt.Graphics2D createPrinterGraphics(float width, float height, PrinterJob printerJob) {
 2780           return new PdfPrinterGraphics2D(this, width, height, null, false, false, 0, printerJob);
 2781       }
 2782   
 2783       /** Gets a <CODE>Graphics2D</CODE> to write on. The graphics
 2784        * are translated to PDF commands.
 2785        * @param width the width of the panel
 2786        * @param height the height of the panel
 2787        * @param convertImagesToJPEG
 2788        * @param quality
 2789        * @return a <CODE>Graphics2D</CODE>
 2790        */
 2791       public java.awt.Graphics2D createGraphics(float width, float height, boolean convertImagesToJPEG, float quality) {
 2792           return new PdfGraphics2D(this, width, height, null, false, convertImagesToJPEG, quality);
 2793       }
 2794   
 2795       /** Gets a <CODE>Graphics2D</CODE> to print on. The graphics
 2796        * are translated to PDF commands.
 2797        * @param width the width of the panel
 2798        * @param height the height of the panel
 2799        * @param convertImagesToJPEG
 2800        * @param quality
 2801        * @param printerJob
 2802        * @return a <CODE>Graphics2D</CODE>
 2803        */
 2804       public java.awt.Graphics2D createPrinterGraphics(float width, float height, boolean convertImagesToJPEG, float quality, PrinterJob printerJob) {
 2805           return new PdfPrinterGraphics2D(this, width, height, null, false, convertImagesToJPEG, quality, printerJob);
 2806       }
 2807   
 2808       /** Gets a <CODE>Graphics2D</CODE> to print on. The graphics
 2809        * are translated to PDF commands.
 2810        * @param width
 2811        * @param height
 2812        * @param convertImagesToJPEG
 2813        * @param quality
 2814        * @return A Graphics2D object
 2815        */
 2816       public java.awt.Graphics2D createGraphicsShapes(float width, float height, boolean convertImagesToJPEG, float quality) {
 2817           return new PdfGraphics2D(this, width, height, null, true, convertImagesToJPEG, quality);
 2818       }
 2819   
 2820       /** Gets a <CODE>Graphics2D</CODE> to print on. The graphics
 2821        * are translated to PDF commands.
 2822        * @param width
 2823        * @param height
 2824        * @param convertImagesToJPEG
 2825        * @param quality
 2826        * @param printerJob
 2827        * @return a Graphics2D object
 2828        */
 2829       public java.awt.Graphics2D createPrinterGraphicsShapes(float width, float height, boolean convertImagesToJPEG, float quality, PrinterJob printerJob) {
 2830           return new PdfPrinterGraphics2D(this, width, height, null, true, convertImagesToJPEG, quality, printerJob);
 2831       }
 2832       
 2833       /** Gets a <CODE>Graphics2D</CODE> to write on. The graphics
 2834        * are translated to PDF commands.
 2835        * @param width the width of the panel
 2836        * @param height the height of the panel
 2837        * @param fontMapper the mapping from awt fonts to <CODE>BaseFont</CODE>
 2838        * @return a <CODE>Graphics2D</CODE>
 2839        */
 2840       public java.awt.Graphics2D createGraphics(float width, float height, FontMapper fontMapper) {
 2841           return new PdfGraphics2D(this, width, height, fontMapper, false, false, 0);
 2842       }
 2843       
 2844       /** Gets a <CODE>Graphics2D</CODE> to print on. The graphics
 2845        * are translated to PDF commands.
 2846        * @param width the width of the panel
 2847        * @param height the height of the panel
 2848        * @param fontMapper the mapping from awt fonts to <CODE>BaseFont</CODE>
 2849        * @param printerJob a printer job
 2850        * @return a <CODE>Graphics2D</CODE>
 2851        */
 2852       public java.awt.Graphics2D createPrinterGraphics(float width, float height, FontMapper fontMapper, PrinterJob printerJob) {
 2853           return new PdfPrinterGraphics2D(this, width, height, fontMapper, false, false, 0, printerJob);
 2854       }
 2855       
 2856       /** Gets a <CODE>Graphics2D</CODE> to write on. The graphics
 2857        * are translated to PDF commands.
 2858        * @param width the width of the panel
 2859        * @param height the height of the panel
 2860        * @param fontMapper the mapping from awt fonts to <CODE>BaseFont</CODE>
 2861        * @param convertImagesToJPEG converts awt images to jpeg before inserting in pdf
 2862        * @param quality the quality of the jpeg
 2863        * @return a <CODE>Graphics2D</CODE>
 2864        */
 2865       public java.awt.Graphics2D createGraphics(float width, float height, FontMapper fontMapper, boolean convertImagesToJPEG, float quality) {
 2866           return new PdfGraphics2D(this, width, height, fontMapper, false, convertImagesToJPEG, quality);
 2867       }
 2868   
 2869       /** Gets a <CODE>Graphics2D</CODE> to print on. The graphics
 2870        * are translated to PDF commands.
 2871        * @param width the width of the panel
 2872        * @param height the height of the panel
 2873        * @param fontMapper the mapping from awt fonts to <CODE>BaseFont</CODE>
 2874        * @param convertImagesToJPEG converts awt images to jpeg before inserting in pdf
 2875        * @param quality the quality of the jpeg
 2876        * @param printerJob a printer job
 2877        * @return a <CODE>Graphics2D</CODE>
 2878        */
 2879       public java.awt.Graphics2D createPrinterGraphics(float width, float height, FontMapper fontMapper, boolean convertImagesToJPEG, float quality, PrinterJob printerJob) {
 2880           return new PdfPrinterGraphics2D(this, width, height, fontMapper, false, convertImagesToJPEG, quality, printerJob);
 2881       }
 2882   
 2883       PageResources getPageResources() {
 2884           return pdf.getPageResources();
 2885       }
 2886       
 2887       /** Sets the graphic state
 2888        * @param gstate the graphic state
 2889        */    
 2890       public void setGState(PdfGState gstate) {
 2891           PdfObject obj[] = writer.addSimpleExtGState(gstate);
 2892           PageResources prs = getPageResources();
 2893           PdfName name = prs.addExtGState((PdfName)obj[0], (PdfIndirectReference)obj[1]);
 2894           content.append(name.getBytes()).append(" gs").append_i(separator);
 2895       }
 2896       
 2897       /**
 2898        * Begins a graphic block whose visibility is controlled by the <CODE>layer</CODE>.
 2899        * Blocks can be nested. Each block must be terminated by an {@link #endLayer()}.<p>
 2900        * Note that nested layers with {@link PdfLayer#addChild(PdfLayer)} only require a single
 2901        * call to this method and a single call to {@link #endLayer()}; all the nesting control
 2902        * is built in.
 2903        * @param layer the layer
 2904        */    
 2905       public void beginLayer(PdfOCG layer) {
 2906           if ((layer instanceof PdfLayer) && ((PdfLayer)layer).getTitle() != null)
 2907               throw new IllegalArgumentException("A title is not a layer");
 2908           if (layerDepth == null)
 2909               layerDepth = new ArrayList();
 2910           if (layer instanceof PdfLayerMembership) {
 2911               layerDepth.add(new Integer(1));
 2912               beginLayer2(layer);
 2913               return;
 2914           }
 2915           int n = 0;
 2916           PdfLayer la = (PdfLayer)layer;
 2917           while (la != null) {
 2918               if (la.getTitle() == null) {
 2919                   beginLayer2(la);
 2920                   ++n;
 2921               }
 2922               la = la.getParent();
 2923           }
 2924           layerDepth.add(new Integer(n));
 2925       }
 2926       
 2927       private void beginLayer2(PdfOCG layer) {
 2928           PdfName name = (PdfName)writer.addSimpleProperty(layer, layer.getRef())[0];
 2929           PageResources prs = getPageResources();
 2930           name = prs.addProperty(name, layer.getRef());
 2931           content.append("/OC ").append(name.getBytes()).append(" BDC").append_i(separator);
 2932       }
 2933       
 2934       /**
 2935        * Ends a layer controlled graphic block. It will end the most recent open block.
 2936        */    
 2937       public void endLayer() {
 2938           int n = 1;
 2939           if (layerDepth != null && !layerDepth.isEmpty()) {
 2940               n = ((Integer)layerDepth.get(layerDepth.size() - 1)).intValue();
 2941               layerDepth.remove(layerDepth.size() - 1);
 2942           }
 2943           while (n-- > 0)
 2944               content.append("EMC").append_i(separator);
 2945       }
 2946       
 2947       /** Concatenates a transformation to the current transformation
 2948        * matrix.
 2949        * @param af the transformation
 2950        */    
 2951       public void transform(AffineTransform af) {
 2952           double arr[] = new double[6];
 2953           af.getMatrix(arr);
 2954           content.append(arr[0]).append(' ').append(arr[1]).append(' ').append(arr[2]).append(' ');
 2955           content.append(arr[3]).append(' ').append(arr[4]).append(' ').append(arr[5]).append(" cm").append_i(separator);
 2956       }
 2957       
 2958       void addAnnotation(PdfAnnotation annot) {
 2959           writer.addAnnotation(annot);
 2960       }
 2961       
 2962       /**
 2963        * Sets the default colorspace.
 2964        * @param name the name of the colorspace. It can be <CODE>PdfName.DEFAULTGRAY</CODE>, <CODE>PdfName.DEFAULTRGB</CODE>
 2965        * or <CODE>PdfName.DEFAULTCMYK</CODE>
 2966        * @param obj the colorspace. A <CODE>null</CODE> or <CODE>PdfNull</CODE> removes any colorspace with the same name
 2967        */    
 2968       public void setDefaultColorspace(PdfName name, PdfObject obj) {
 2969           PageResources prs = getPageResources();
 2970           prs.addDefaultColor(name, obj);
 2971       }
 2972       
 2973       /**
 2974        * Begins a marked content sequence. This sequence will be tagged with the structure <CODE>struc</CODE>.
 2975        * The same structure can be used several times to connect text that belongs to the same logical segment
 2976        * but is in a different location, like the same paragraph crossing to another page, for example.
 2977        * @param struc the tagging structure
 2978        */    
 2979       public void beginMarkedContentSequence(PdfStructureElement struc) {
 2980           PdfObject obj = struc.get(PdfName.K);
 2981           int mark = pdf.getMarkPoint();
 2982           if (obj != null) {
 2983               PdfArray ar = null;
 2984               if (obj.isNumber()) {
 2985                   ar = new PdfArray();
 2986                   ar.add(obj);
 2987                   struc.put(PdfName.K, ar);
 2988               }
 2989               else if (obj.isArray()) {
 2990                   ar = (PdfArray)obj;
 2991                   if (!((PdfObject)ar.getArrayList().get(0)).isNumber())
 2992                       throw new IllegalArgumentException("The structure has kids.");
 2993               }
 2994               else
 2995                   throw new IllegalArgumentException("Unknown object at /K " + obj.getClass().toString());
 2996               PdfDictionary dic = new PdfDictionary(PdfName.MCR);
 2997               dic.put(PdfName.PG, writer.getCurrentPage());
 2998               dic.put(PdfName.MCID, new PdfNumber(mark));
 2999               ar.add(dic);
 3000               struc.setPageMark(writer.getPageNumber() - 1, -1);
 3001           }
 3002           else {
 3003               struc.setPageMark(writer.getPageNumber() - 1, mark);
 3004               struc.put(PdfName.PG, writer.getCurrentPage());
 3005           }
 3006           pdf.incMarkPoint();
 3007           content.append(struc.get(PdfName.S).getBytes()).append(" <</MCID ").append(mark).append(">> BDC").append_i(separator);
 3008       }
 3009       
 3010       /**
 3011        * Ends a marked content sequence
 3012        */    
 3013       public void endMarkedContentSequence() {
 3014           content.append("EMC").append_i(separator);
 3015       }
 3016       
 3017       /**
 3018        * Begins a marked content sequence. If property is <CODE>null</CODE> the mark will be of the type
 3019        * <CODE>BMC</CODE> otherwise it will be <CODE>BDC</CODE>.
 3020        * @param tag the tag
 3021        * @param property the property
 3022        * @param inline <CODE>true</CODE> to include the property in the content or <CODE>false</CODE>
 3023        * to include the property in the resource dictionary with the possibility of reusing
 3024        */    
 3025       public void beginMarkedContentSequence(PdfName tag, PdfDictionary property, boolean inline) {
 3026           if (property == null) {
 3027               content.append(tag.getBytes()).append(" BMC").append_i(separator);
 3028               return;
 3029           }
 3030           content.append(tag.getBytes()).append(' ');
 3031           if (inline)
 3032               try {
 3033                   property.toPdf(writer, content);
 3034               }
 3035               catch (Exception e) {
 3036                   throw new ExceptionConverter(e);
 3037               }
 3038           else {
 3039               PdfObject[] objs;
 3040               if (writer.propertyExists(property))
 3041                   objs = writer.addSimpleProperty(property, null);
 3042               else
 3043                   objs = writer.addSimpleProperty(property, writer.getPdfIndirectReference());
 3044               PdfName name = (PdfName)objs[0];
 3045               PageResources prs = getPageResources();
 3046               name = prs.addProperty(name, (PdfIndirectReference)objs[1]);
 3047               content.append(name.getBytes());
 3048           }
 3049           content.append(" BDC").append_i(separator);
 3050       }
 3051       
 3052       /**
 3053        * This is just a shorthand to <CODE>beginMarkedContentSequence(tag, null, false)</CODE>.
 3054        * @param tag the tag
 3055        */    
 3056       public void beginMarkedContentSequence(PdfName tag) {
 3057           beginMarkedContentSequence(tag, null, false);
 3058       }
 3059   }

Save This Page
Home » iText-src-2.1.3 » com.lowagie » text » pdf » [javadoc | source]