KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > lowagie > text > pdf > PdfContentByte


1 /*
2  * $Id: PdfContentByte.java 2752 2007-05-15 14:58:33Z blowagie $
3  * $Name$
4  *
5  * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
6  *
7  * The contents of this file are subject to the Mozilla Public License Version 1.1
8  * (the "License"); you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at http://www.mozilla.org/MPL/
10  *
11  * Software distributed under the License is distributed on an "AS IS" basis,
12  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13  * for the specific language governing rights and limitations under the License.
14  *
15  * The Original Code is 'iText, a free JAVA-PDF library'.
16  *
17  * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
18  * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
19  * All Rights Reserved.
20  * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
21  * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
22  *
23  * Contributor(s): all the names of the contributors are added in the source code
24  * where applicable.
25  *
26  * Alternatively, the contents of this file may be used under the terms of the
27  * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
28  * provisions of LGPL are applicable instead of those above. If you wish to
29  * allow use of your version of this file only under the terms of the LGPL
30  * License and not to allow others to use your version of this file under
31  * the MPL, indicate your decision by deleting the provisions above and
32  * replace them with the notice and other provisions required by the LGPL.
33  * If you do not delete the provisions above, a recipient may use your version
34  * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
35  *
36  * This library is free software; you can redistribute it and/or modify it
37  * under the terms of the MPL as stated above or under the terms of the GNU
38  * Library General Public License as published by the Free Software Foundation;
39  * either version 2 of the License, or any later version.
40  *
41  * This library is distributed in the hope that it will be useful, but WITHOUT
42  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
43  * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
44  * details.
45  *
46  * If you didn't download this code from the following link, you should check if
47  * you aren't using an obsolete version:
48  * http://www.lowagie.com/iText/
49  */

50
51 package com.lowagie.text.pdf;
52 import java.awt.Color JavaDoc;
53 import java.awt.geom.AffineTransform JavaDoc;
54 import java.awt.print.PrinterJob JavaDoc;
55 import java.util.ArrayList JavaDoc;
56 import java.util.HashMap JavaDoc;
57 import java.util.Iterator JavaDoc;
58
59 import com.lowagie.text.Annotation;
60 import com.lowagie.text.DocumentException;
61 import com.lowagie.text.Element;
62 import com.lowagie.text.ExceptionConverter;
63 import com.lowagie.text.Image;
64 import com.lowagie.text.Rectangle;
65 import com.lowagie.text.pdf.internal.PdfAnnotationsImp;
66 import com.lowagie.text.pdf.internal.PdfXConformanceImp;
67
68 /**
69  * <CODE>PdfContentByte</CODE> is an object containing the user positioned
70  * text and graphic contents of a page. It knows how to apply the proper
71  * font encoding.
72  */

73
74 public class PdfContentByte {
75     
76     /**
77      * This class keeps the graphic state of the current page
78      */

79     
80     static class GraphicState {
81         
82         /** This is the font in use */
83         FontDetails fontDetails;
84         
85         /** This is the color in use */
86         ColorDetails colorDetails;
87         
88         /** This is the font size in use */
89         float size;
90         
91         /** The x position of the text line matrix. */
92         protected float xTLM = 0;
93         /** The y position of the text line matrix. */
94         protected float yTLM = 0;
95         
96         /** The current text leading. */
97         protected float leading = 0;
98
99         /** The current horizontal scaling */
100         protected float scale = 100;
101
102         /** The current character spacing */
103         protected float charSpace = 0;
104
105         /** The current word spacing */
106         protected float wordSpace = 0;
107         
108         GraphicState() {
109         }
110
111         GraphicState(GraphicState cp) {
112             fontDetails = cp.fontDetails;
113             colorDetails = cp.colorDetails;
114             size = cp.size;
115             xTLM = cp.xTLM;
116             yTLM = cp.yTLM;
117             leading = cp.leading;
118             scale = cp.scale;
119             charSpace = cp.charSpace;
120             wordSpace = cp.wordSpace;
121         }
122     }
123     
124     /** The alignement is center */
125     public static final int ALIGN_CENTER = Element.ALIGN_CENTER;
126     
127     /** The alignement is left */
128     public static final int ALIGN_LEFT = Element.ALIGN_LEFT;
129     
130     /** The alignement is right */
131     public static final int ALIGN_RIGHT = Element.ALIGN_RIGHT;
132
133     /** A possible line cap value */
134     public static final int LINE_CAP_BUTT = 0;
135     /** A possible line cap value */
136     public static final int LINE_CAP_ROUND = 1;
137     /** A possible line cap value */
138     public static final int LINE_CAP_PROJECTING_SQUARE = 2;
139
140     /** A possible line join value */
141     public static final int LINE_JOIN_MITER = 0;
142     /** A possible line join value */
143     public static final int LINE_JOIN_ROUND = 1;
144     /** A possible line join value */
145     public static final int LINE_JOIN_BEVEL = 2;
146
147     /** A possible text rendering value */
148     public static final int TEXT_RENDER_MODE_FILL = 0;
149     /** A possible text rendering value */
150     public static final int TEXT_RENDER_MODE_STROKE = 1;
151     /** A possible text rendering value */
152     public static final int TEXT_RENDER_MODE_FILL_STROKE = 2;
153     /** A possible text rendering value */
154     public static final int TEXT_RENDER_MODE_INVISIBLE = 3;
155     /** A possible text rendering value */
156     public static final int TEXT_RENDER_MODE_FILL_CLIP = 4;
157     /** A possible text rendering value */
158     public static final int TEXT_RENDER_MODE_STROKE_CLIP = 5;
159     /** A possible text rendering value */
160     public static final int TEXT_RENDER_MODE_FILL_STROKE_CLIP = 6;
161     /** A possible text rendering value */
162     public static final int TEXT_RENDER_MODE_CLIP = 7;
163     
164     private static final float[] unitRect = {0, 0, 0, 1, 1, 0, 1, 1};
165     // membervariables
166

167     /** This is the actual content */
168     protected ByteBuffer content = new ByteBuffer();
169     
170     /** This is the writer */
171     protected PdfWriter writer;
172     
173     /** This is the PdfDocument */
174     protected PdfDocument pdf;
175     
176     /** This is the GraphicState in use */
177     protected GraphicState state = new GraphicState();
178     
179     /** The list were we save/restore the state */
180     protected ArrayList JavaDoc stateList = new ArrayList JavaDoc();
181     
182     /** The list were we save/restore the layer depth */
183     protected ArrayList JavaDoc layerDepth;
184     
185     /** The separator between commands.
186      */

187     protected int separator = '\n';
188     
189     private static HashMap JavaDoc abrev = new HashMap JavaDoc();
190     
191     static {
192         abrev.put(PdfName.BITSPERCOMPONENT, "/BPC ");
193         abrev.put(PdfName.COLORSPACE, "/CS ");
194         abrev.put(PdfName.DECODE, "/D ");
195         abrev.put(PdfName.DECODEPARMS, "/DP ");
196         abrev.put(PdfName.FILTER, "/F ");
197         abrev.put(PdfName.HEIGHT, "/H ");
198         abrev.put(PdfName.IMAGEMASK, "/IM ");
199         abrev.put(PdfName.INTENT, "/Intent ");
200         abrev.put(PdfName.INTERPOLATE, "/I ");
201         abrev.put(PdfName.WIDTH, "/W ");
202     }
203     
204     // constructors
205

206     /**
207      * Constructs a new <CODE>PdfContentByte</CODE>-object.
208      *
209      * @param wr the writer associated to this content
210      */

211     
212     public PdfContentByte(PdfWriter wr) {
213         if (wr != null) {
214             writer = wr;
215             pdf = writer.getPdfDocument();
216         }
217     }
218     
219     // methods to get the content of this object
220

221     /**
222      * Returns the <CODE>String</CODE> representation of this <CODE>PdfContentByte</CODE>-object.
223      *
224      * @return a <CODE>String</CODE>
225      */

226     
227     public String JavaDoc toString() {
228         return content.toString();
229     }
230     
231     /**
232      * Gets the internal buffer.
233      * @return the internal buffer
234      */

235     public ByteBuffer getInternalBuffer() {
236         return content;
237     }
238     
239     /** Returns the PDF representation of this <CODE>PdfContentByte</CODE>-object.
240      *
241      * @param writer the <CODE>PdfWriter</CODE>
242      * @return a <CODE>byte</CODE> array with the representation
243      */

244     
245     public byte[] toPdf(PdfWriter writer) {
246         return content.toByteArray();
247     }
248     
249     // methods to add graphical content
250

251     /**
252      * Adds the content of another <CODE>PdfContent</CODE>-object to this object.
253      *
254      * @param other another <CODE>PdfByteContent</CODE>-object
255      */

256     
257     public void add(PdfContentByte other) {
258         if (other.writer != null && writer != other.writer)
259             throw new RuntimeException JavaDoc("Inconsistent writers. Are you mixing two documents?");
260         content.append(other.content);
261     }
262     
263     /**
264      * Gets the x position of the text line matrix.
265      *
266      * @return the x position of the text line matrix
267      */

268     public float getXTLM() {
269         return state.xTLM;
270     }
271     
272     /**
273      * Gets the y position of the text line matrix.
274      *
275      * @return the y position of the text line matrix
276      */

277     public float getYTLM() {
278         return state.yTLM;
279     }
280     
281     /**
282      * Gets the current text leading.
283      *
284      * @return the current text leading
285      */

286     public float getLeading() {
287         return state.leading;
288     }
289     
290     /**
291      * Gets the current character spacing.
292      *
293      * @return the current character spacing
294      */

295     public float getCharacterSpacing() {
296         return state.charSpace;
297     }
298
299     /**
300      * Gets the current word spacing.
301      *
302      * @return the current word spacing
303      */

304     public float getWordSpacing() {
305         return state.wordSpace;
306     }
307
308     /**
309      * Gets the current character spacing.
310      *
311      * @return the current character spacing
312      */

313     public float getHorizontalScaling() {
314         return state.scale;
315     }
316
317     /**
318      * Changes the <VAR>Flatness</VAR>.
319      * <P>
320      * <VAR>Flatness</VAR> sets the maximum permitted distance in device pixels between the
321      * mathematically correct path and an approximation constructed from straight line segments.<BR>
322      *
323      * @param flatness a value
324      */

325     
326     public void setFlatness(float flatness) {
327         if (flatness >= 0 && flatness <= 100) {
328             content.append(flatness).append(" i").append_i(separator);
329         }
330     }
331     
332     /**
333      * Changes the <VAR>Line cap style</VAR>.
334      * <P>
335      * The <VAR>line cap style</VAR> specifies the shape to be used at the end of open subpaths
336      * when they are stroked.<BR>
337      * Allowed values are LINE_CAP_BUTT, LINE_CAP_ROUND and LINE_CAP_PROJECTING_SQUARE.<BR>
338      *
339      * @param style a value
340      */

341     
342     public void setLineCap(int style) {
343         if (style >= 0 && style <= 2) {
344             content.append(style).append(" J").append_i(separator);
345         }
346     }
347     
348     /**
349      * Changes the value of the <VAR>line dash pattern</VAR>.
350      * <P>
351      * The line dash pattern controls the pattern of dashes and gaps used to stroke paths.
352      * It is specified by an <I>array</I> and a <I>phase</I>. The array specifies the length
353      * of the alternating dashes and gaps. The phase specifies the distance into the dash
354      * pattern to start the dash.<BR>
355      *
356      * @param phase the value of the phase
357      */

358     
359     public void setLineDash(float phase) {
360         content.append("[] ").append(phase).append(" d").append_i(separator);
361     }
362     
363     /**
364      * Changes the value of the <VAR>line dash pattern</VAR>.
365      * <P>
366      * The line dash pattern controls the pattern of dashes and gaps used to stroke paths.
367      * It is specified by an <I>array</I> and a <I>phase</I>. The array specifies the length
368      * of the alternating dashes and gaps. The phase specifies the distance into the dash
369      * pattern to start the dash.<BR>
370      *
371      * @param phase the value of the phase
372      * @param unitsOn the number of units that must be 'on' (equals the number of units that must be 'off').
373      */

374     
375     public void setLineDash(float unitsOn, float phase) {
376         content.append("[").append(unitsOn).append("] ").append(phase).append(" d").append_i(separator);
377     }
378     
379     /**
380      * Changes the value of the <VAR>line dash pattern</VAR>.
381      * <P>
382      * The line dash pattern controls the pattern of dashes and gaps used to stroke paths.
383      * It is specified by an <I>array</I> and a <I>phase</I>. The array specifies the length
384      * of the alternating dashes and gaps. The phase specifies the distance into the dash
385      * pattern to start the dash.<BR>
386      *
387      * @param phase the value of the phase
388      * @param unitsOn the number of units that must be 'on'
389      * @param unitsOff the number of units that must be 'off'
390      */

391     
392     public void setLineDash(float unitsOn, float unitsOff, float phase) {
393         content.append("[").append(unitsOn).append(' ').append(unitsOff).append("] ").append(phase).append(" d").append_i(separator);
394     }
395     
396     /**
397      * Changes the value of the <VAR>line dash pattern</VAR>.
398      * <P>
399      * The line dash pattern controls the pattern of dashes and gaps used to stroke paths.
400      * It is specified by an <I>array</I> and a <I>phase</I>. The array specifies the length
401      * of the alternating dashes and gaps. The phase specifies the distance into the dash
402      * pattern to start the dash.<BR>
403      *
404      * @param array length of the alternating dashes and gaps
405      * @param phase the value of the phase
406      */

407     
408     public final void setLineDash(float[] array, float phase) {
409         content.append("[");
410         for (int i = 0; i < array.length; i++) {
411             content.append(array[i]);
412             if (i < array.length - 1) content.append(' ');
413         }
414         content.append("] ").append(phase).append(" d").append_i(separator);
415     }
416
417     /**
418      * Changes the <VAR>Line join style</VAR>.
419      * <P>
420      * The <VAR>line join style</VAR> specifies the shape to be used at the corners of paths
421      * that are stroked.<BR>
422      * Allowed values are LINE_JOIN_MITER (Miter joins), LINE_JOIN_ROUND (Round joins) and LINE_JOIN_BEVEL (Bevel joins).<BR>
423      *
424      * @param style a value
425      */

426     
427     public void setLineJoin(int style) {
428         if (style >= 0 && style <= 2) {
429             content.append(style).append(" j").append_i(separator);
430         }
431     }
432     
433     /**
434      * Changes the <VAR>line width</VAR>.
435      * <P>
436      * The line width specifies the thickness of the line used to stroke a path and is measured
437      * in user space units.<BR>
438      *
439      * @param w a width
440      */

441     
442     public void setLineWidth(float w) {
443         content.append(w).append(" w").append_i(separator);
444     }
445     
446     /**
447      * Changes the <VAR>Miter limit</VAR>.
448      * <P>
449      * When two line segments meet at a sharp angle and mitered joins have been specified as the
450      * line join style, it is possible for the miter to extend far beyond the thickness of the line
451      * stroking path. The miter limit imposes a maximum on the ratio of the miter length to the line
452      * witdh. When the limit is exceeded, the join is converted from a miter to a bevel.<BR>
453      *
454      * @param miterLimit a miter limit
455      */

456     
457     public void setMiterLimit(float miterLimit) {
458         if (miterLimit > 1) {
459             content.append(miterLimit).append(" M").append_i(separator);
460         }
461     }
462     
463     /**
464      * Modify the current clipping path by intersecting it with the current path, using the
465      * nonzero winding number rule to determine which regions lie inside the clipping
466      * path.
467      */

468     
469     public void clip() {
470         content.append("W").append_i(separator);
471     }
472     
473     /**
474      * Modify the current clipping path by intersecting it with the current path, using the
475      * even-odd rule to determine which regions lie inside the clipping path.
476      */

477     
478     public void eoClip() {
479         content.append("W*").append_i(separator);
480     }
481     
482     /**
483      * Changes the currentgray tint for filling paths (device dependent colors!).
484      * <P>
485      * Sets the color space to <B>DeviceGray</B> (or the <B>DefaultGray</B> color space),
486      * and sets the gray tint to use for filling paths.</P>
487      *
488      * @param gray a value between 0 (black) and 1 (white)
489      */

490     
491     public void setGrayFill(float gray) {
492         content.append(gray).append(" g").append_i(separator);
493     }
494     
495     /**
496      * Changes the current gray tint for filling paths to black.
497      */

498     
499     public void resetGrayFill() {
500         content.append("0 g").append_i(separator);
501     }
502     
503     /**
504      * Changes the currentgray tint for stroking paths (device dependent colors!).
505      * <P>
506      * Sets the color space to <B>DeviceGray</B> (or the <B>DefaultGray</B> color space),
507      * and sets the gray tint to use for stroking paths.</P>
508      *
509      * @param gray a value between 0 (black) and 1 (white)
510      */

511     
512     public void setGrayStroke(float gray) {
513         content.append(gray).append(" G").append_i(separator);
514     }
515     
516     /**
517      * Changes the current gray tint for stroking paths to black.
518      */

519     
520     public void resetGrayStroke() {
521         content.append("0 G").append_i(separator);
522     }
523     
524     /**
525      * Helper to validate and write the RGB color components
526      * @param red the intensity of red. A value between 0 and 1
527      * @param green the intensity of green. A value between 0 and 1
528      * @param blue the intensity of blue. A value between 0 and 1
529      */

530     private void HelperRGB(float red, float green, float blue) {
531         PdfXConformanceImp.checkPDFXConformance(writer, PdfXConformanceImp.PDFXKEY_RGB, null);
532         if (red < 0)
533             red = 0.0f;
534         else if (red > 1.0f)
535             red = 1.0f;
536         if (green < 0)
537             green = 0.0f;
538         else if (green > 1.0f)
539             green = 1.0f;
540         if (blue < 0)
541             blue = 0.0f;
542         else if (blue > 1.0f)
543             blue = 1.0f;
544         content.append(red).append(' ').append(green).append(' ').append(blue);
545     }
546     
547     /**
548      * Changes the current color for filling paths (device dependent colors!).
549      * <P>
550      * Sets the color space to <B>DeviceRGB</B> (or the <B>DefaultRGB</B> color space),
551      * and sets the color to use for filling paths.</P>
552      * <P>
553      * Following the PDF manual, each operand must be a number between 0 (minimum intensity) and
554      * 1 (maximum intensity).</P>
555      *
556      * @param red the intensity of red. A value between 0 and 1
557      * @param green the intensity of green. A value between 0 and 1
558      * @param blue the intensity of blue. A value between 0 and 1
559      */

560     
561     public void setRGBColorFillF(float red, float green, float blue) {
562         HelperRGB(red, green, blue);
563         content.append(" rg").append_i(separator);
564     }
565     
566     /**
567      * Changes the current color for filling paths to black.
568      */

569     
570     public void resetRGBColorFill() {
571         content.append("0 g").append_i(separator);
572     }
573     
574     /**
575      * Changes the current color for stroking paths (device dependent colors!).
576      * <P>
577      * Sets the color space to <B>DeviceRGB</B> (or the <B>DefaultRGB</B> color space),
578      * and sets the color to use for stroking paths.</P>
579      * <P>
580      * Following the PDF manual, each operand must be a number between 0 (miniumum intensity) and
581      * 1 (maximum intensity).
582      *
583      * @param red the intensity of red. A value between 0 and 1
584      * @param green the intensity of green. A value between 0 and 1
585      * @param blue the intensity of blue. A value between 0 and 1
586      */

587     
588     public void setRGBColorStrokeF(float red, float green, float blue) {
589         HelperRGB(red, green, blue);
590         content.append(" RG").append_i(separator);
591     }
592     
593     /**
594      * Changes the current color for stroking paths to black.
595      *
596      */

597     
598     public void resetRGBColorStroke() {
599         content.append("0 G").append_i(separator);
600     }
601     
602     /**
603      * Helper to validate and write the CMYK color components.
604      *
605      * @param cyan the intensity of cyan. A value between 0 and 1
606      * @param magenta the intensity of magenta. A value between 0 and 1
607      * @param yellow the intensity of yellow. A value between 0 and 1
608      * @param black the intensity of black. A value between 0 and 1
609      */

610     private void HelperCMYK(float cyan, float magenta, float yellow, float black) {
611         if (cyan < 0)
612             cyan = 0.0f;
613         else if (cyan > 1.0f)
614             cyan = 1.0f;
615         if (magenta < 0)
616             magenta = 0.0f;
617         else if (magenta > 1.0f)
618             magenta = 1.0f;
619         if (yellow < 0)
620             yellow = 0.0f;
621         else if (yellow > 1.0f)
622             yellow = 1.0f;
623         if (black < 0)
624             black = 0.0f;
625         else if (black > 1.0f)
626             black = 1.0f;
627         content.append(cyan).append(' ').append(magenta).append(' ').append(yellow).append(' ').append(black);
628     }
629     
630     /**
631      * Changes the current color for filling paths (device dependent colors!).
632      * <P>
633      * Sets the color space to <B>DeviceCMYK</B> (or the <B>DefaultCMYK</B> color space),
634      * and sets the color to use for filling paths.</P>
635      * <P>
636      * Following the PDF manual, each operand must be a number between 0 (no ink) and
637      * 1 (maximum ink).</P>
638      *
639      * @param cyan the intensity of cyan. A value between 0 and 1
640      * @param magenta the intensity of magenta. A value between 0 and 1
641      * @param yellow the intensity of yellow. A value between 0 and 1
642      * @param black the intensity of black. A value between 0 and 1
643      */

644     
645     public void setCMYKColorFillF(float cyan, float magenta, float yellow, float black) {
646         HelperCMYK(cyan, magenta, yellow, black);
647         content.append(" k").append_i(separator);
648     }
649     
650     /**
651      * Changes the current color for filling paths to black.
652      *
653      */

654     
655     public void resetCMYKColorFill() {
656         content.append("0 0 0 1 k").append_i(separator);
657     }
658     
659     /**
660      * Changes the current color for stroking paths (device dependent colors!).
661      * <P>
662      * Sets the color space to <B>DeviceCMYK</B> (or the <B>DefaultCMYK</B> color space),
663      * and sets the color to use for stroking paths.</P>
664      * <P>
665      * Following the PDF manual, each operand must be a number between 0 (miniumum intensity) and
666      * 1 (maximum intensity).
667      *
668      * @param cyan the intensity of cyan. A value between 0 and 1
669      * @param magenta the intensity of magenta. A value between 0 and 1
670      * @param yellow the intensity of yellow. A value between 0 and 1
671      * @param black the intensity of black. A value between 0 and 1
672      */

673     
674     public void setCMYKColorStrokeF(float cyan, float magenta, float yellow, float black) {
675         HelperCMYK(cyan, magenta, yellow, black);
676         content.append(" K").append_i(separator);
677     }
678     
679     /**
680      * Changes the current color for stroking paths to black.
681      *
682      */

683     
684     public void resetCMYKColorStroke() {
685         content.append("0 0 0 1 K").append_i(separator);
686     }
687     
688     /**
689      * Move the current point <I>(x, y)</I>, omitting any connecting line segment.
690      *
691      * @param x new x-coordinate
692      * @param y new y-coordinate
693      */

694     
695     public void moveTo(float x, float y) {
696         content.append(x).append(' ').append(y).append(" m").append_i(separator);
697     }
698     
699     /**
700      * Appends a straight line segment from the current point <I>(x, y)</I>. The new current
701      * point is <I>(x, y)</I>.
702      *
703      * @param x new x-coordinate
704      * @param y new y-coordinate
705      */

706     
707     public void lineTo(float x, float y) {
708         content.append(x).append(' ').append(y).append(" l").append_i(separator);
709     }
710     
711     /**
712      * Appends a B&#xea;zier curve to the path, starting from the current point.
713      *
714      * @param x1 x-coordinate of the first control point
715      * @param y1 y-coordinate of the first control point
716      * @param x2 x-coordinate of the second control point
717      * @param y2 y-coordinate of the second control point
718      * @param x3 x-coordinaat of the ending point (= new current point)
719      * @param y3 y-coordinaat of the ending point (= new current point)
720      */

721     
722     public void curveTo(float x1, float y1, float x2, float y2, float x3, float y3) {
723         content.append(x1).append(' ').append(y1).append(' ').append(x2).append(' ').append(y2).append(' ').append(x3).append(' ').append(y3).append(" c").append_i(separator);
724     }
725     
726     /**
727      * Appends a B&#xea;zier curve to the path, starting from the current point.
728      *
729      * @param x2 x-coordinate of the second control point
730      * @param y2 y-coordinate of the second control point
731      * @param x3 x-coordinaat of the ending point (= new current point)
732      * @param y3 y-coordinaat of the ending point (= new current point)
733      */

734     
735     public void curveTo(float x2, float y2, float x3, float y3) {
736         content.append(x2).append(' ').append(y2).append(' ').append(x3).append(' ').append(y3).append(" v").append_i(separator);
737     }
738     
739     /**
740      * Appends a B&#xea;zier curve to the path, starting from the current point.
741      *
742      * @param x1 x-coordinate of the first control point
743      * @param y1 y-coordinate of the first control point
744      * @param x3 x-coordinaat of the ending point (= new current point)
745      * @param y3 y-coordinaat of the ending point (= new current point)
746      */

747     
748     public void curveFromTo(float x1, float y1, float x3, float y3) {
749         content.append(x1).append(' ').append(y1).append(' ').append(x3).append(' ').append(y3).append(" y").append_i(separator);
750     }
751     
752     /** Draws a circle. The endpoint will (x+r, y).
753      *
754      * @param x x center of circle
755      * @param y y center of circle
756      * @param r radius of circle
757      */

758     public void circle(float x, float y, float r) {
759         float b = 0.5523f;
760         moveTo(x + r, y);
761         curveTo(x + r, y + r * b, x + r * b, y + r, x, y + r);
762         curveTo(x - r * b, y + r, x - r, y + r * b, x - r, y);
763         curveTo(x - r, y - r * b, x - r * b, y - r, x, y - r);
764         curveTo(x + r * b, y - r, x + r, y - r * b, x + r, y);
765     }
766     
767     
768     
769     /**
770      * Adds a rectangle to the current path.
771      *
772      * @param x x-coordinate of the starting point
773      * @param y y-coordinate of the starting point
774      * @param w width
775      * @param h height
776      */

777     
778     public void rectangle(float x, float y, float w, float h) {
779         content.append(x).append(' ').append(y).append(' ').append(w).append(' ').append(h).append(" re").append_i(separator);
780     }
781     
782     private boolean compareColors(Color JavaDoc c1, Color JavaDoc c2) {
783         if (c1 == null && c2 == null)
784             return true;
785         if (c1 == null || c2 == null)
786             return false;
787         if (c1 instanceof ExtendedColor)
788             return c1.equals(c2);
789         return c2.equals(c1);
790     }
791     
792     /**
793      * Adds a variable width border to the current path.
794      * Only use if {@link com.lowagie.text.Rectangle#isUseVariableBorders() Rectangle.isUseVariableBorders}
795      * = true.
796      * @param rect a <CODE>Rectangle</CODE>
797      */

798     public void variableRectangle(Rectangle rect) {
799         float t = rect.getTop();
800         float b = rect.getBottom();
801         float r = rect.getRight();
802         float l = rect.getLeft();
803         float wt = rect.getBorderWidthTop();
804         float wb = rect.getBorderWidthBottom();
805         float wr = rect.getBorderWidthRight();
806         float wl = rect.getBorderWidthLeft();
807         Color JavaDoc ct = rect.getBorderColorTop();
808         Color JavaDoc cb = rect.getBorderColorBottom();
809         Color JavaDoc cr = rect.getBorderColorRight();
810         Color JavaDoc cl = rect.getBorderColorLeft();
811         saveState();
812         setLineCap(PdfContentByte.LINE_CAP_BUTT);
813         setLineJoin(PdfContentByte.LINE_JOIN_MITER);
814         float clw = 0;
815         boolean cdef = false;
816         Color JavaDoc ccol = null;
817         boolean cdefi = false;
818         Color JavaDoc cfil = null;
819         // draw top
820
if (wt > 0) {
821             setLineWidth(clw = wt);
822             cdef = true;
823             if (ct == null)
824                 resetRGBColorStroke();
825             else
826                 setColorStroke(ct);
827             ccol = ct;
828             moveTo(l, t - wt / 2f);
829             lineTo(r, t - wt / 2f);
830             stroke();
831         }
832
833         // Draw bottom
834
if (wb > 0) {
835             if (wb != clw)
836                 setLineWidth(clw = wb);
837             if (!cdef || !compareColors(ccol, cb)) {
838                 cdef = true;
839                 if (cb == null)
840                     resetRGBColorStroke();
841                 else
842                     setColorStroke(cb);
843                 ccol = cb;
844             }
845             moveTo(r, b + wb / 2f);
846             lineTo(l, b + wb / 2f);
847             stroke();
848         }
849
850         // Draw right
851
if (wr > 0) {
852             if (wr != clw)
853                 setLineWidth(clw = wr);
854             if (!cdef || !compareColors(ccol, cr)) {
855                 cdef = true;
856                 if (cr == null)
857                     resetRGBColorStroke();
858                 else
859                     setColorStroke(cr);
860                 ccol = cr;
861             }
862             boolean bt = compareColors(ct, cr);
863             boolean bb = compareColors(cb, cr);
864             moveTo(r - wr / 2f, bt ? t : t - wt);
865             lineTo(r - wr / 2f, bb ? b : b + wb);
866             stroke();
867             if (!bt || !bb) {
868                 cdefi = true;
869                 if (cr == null)
870                     resetRGBColorFill();
871                 else
872                     setColorFill(cr);
873                 cfil = cr;
874                 if (!bt) {
875                     moveTo(r, t);
876                     lineTo(r, t - wt);
877                     lineTo(r - wr, t - wt);
878                     fill();
879                 }
880                 if (!bb) {
881                     moveTo(r, b);
882                     lineTo(r, b + wb);
883                     lineTo(r - wr, b + wb);
884                     fill();
885                 }
886             }
887         }
888         
889         // Draw Left
890
if (wl > 0) {
891             if (wl != clw)
892                 setLineWidth(wl);
893             if (!cdef || !compareColors(ccol, cl)) {
894                 if (cl == null)
895                     resetRGBColorStroke();
896                 else
897                     setColorStroke(cl);
898             }
899             boolean bt = compareColors(ct, cl);
900             boolean bb = compareColors(cb, cl);
901             moveTo(l + wl / 2f, bt ? t : t - wt);
902             lineTo(l + wl / 2f, bb ? b : b + wb);
903             stroke();
904             if (!bt || !bb) {
905                 if (!cdefi || !compareColors(cfil, cl)) {
906                     if (cl == null)
907                         resetRGBColorFill();
908                     else
909                         setColorFill(cl);
910                 }
911                 if (!bt) {
912                     moveTo(l, t);
913                     lineTo(l, t - wt);
914                     lineTo(l + wl, t - wt);
915                     fill();
916                 }
917                 if (!bb) {
918                     moveTo(l, b);
919                     lineTo(l, b + wb);
920                     lineTo(l + wl, b + wb);
921                     fill();
922                 }
923             }
924         }
925         restoreState();
926     }
927
928     /**
929      * Adds a border (complete or partially) to the current path..
930      *
931      * @param rectangle a <CODE>Rectangle</CODE>
932      */

933     
934     public void rectangle(Rectangle rectangle) {
935         // the coordinates of the border are retrieved
936
float x1 = rectangle.getLeft();
937         float y1 = rectangle.getBottom();
938         float x2 = rectangle.getRight();
939         float y2 = rectangle.getTop();
940
941         // the backgroundcolor is set
942
Color JavaDoc background = rectangle.getBackgroundColor();
943         if (background != null) {
944             setColorFill(background);
945             rectangle(x1, y1, x2 - x1, y2 - y1);
946             fill();
947             resetRGBColorFill();
948         }
949
950         // if the element hasn't got any borders, nothing is added
951
if (! rectangle.hasBorders()) {
952             return;
953         }
954
955         // if any of the individual border colors are set
956
// we draw the borders all around using the
957
// different colors
958
if (rectangle.isUseVariableBorders()) {
959             variableRectangle(rectangle);
960         }
961         else {
962             // the width is set to the width of the element
963
if (rectangle.getBorderWidth() != Rectangle.UNDEFINED) {
964                 setLineWidth(rectangle.getBorderWidth());
965             }
966
967             // the color is set to the color of the element
968
Color JavaDoc color = rectangle.getBorderColor();
969             if (color != null) {
970                 setColorStroke(color);
971             }
972
973             // if the box is a rectangle, it is added as a rectangle
974
if (rectangle.hasBorder(Rectangle.BOX)) {
975                rectangle(x1, y1, x2 - x1, y2 - y1);
976             }
977             // if the border isn't a rectangle, the different sides are added apart
978
else {
979                 if (rectangle.hasBorder(Rectangle.RIGHT)) {
980                     moveTo(x2, y1);
981                     lineTo(x2, y2);
982                 }
983                 if (rectangle.hasBorder(Rectangle.LEFT)) {
984                     moveTo(x1, y1);
985                     lineTo(x1, y2);
986                 }
987                 if (rectangle.hasBorder(Rectangle.BOTTOM)) {
988                     moveTo(x1, y1);
989                     lineTo(x2, y1);
990                 }
991                 if (rectangle.hasBorder(Rectangle.TOP)) {
992                     moveTo(x1, y2);
993                     lineTo(x2, y2);
994                 }
995             }
996
997             stroke();
998
999             if (color != null) {
1000                resetRGBColorStroke();
1001            }
1002        }
1003    }
1004
1005    /**
1006     * Closes the current subpath by appending a straight line segment from the current point
1007     * to the starting point of the subpath.
1008     */

1009    
1010    public void closePath() {
1011        content.append("h").append_i(separator);
1012    }
1013    
1014    /**
1015     * Ends the path without filling or stroking it.
1016     */

1017    
1018    public void newPath() {
1019        content.append("n").append_i(separator);
1020    }
1021    
1022    /**
1023     * Strokes the path.
1024     */

1025    
1026    public void stroke() {
1027        content.append("S").append_i(separator);
1028    }
1029    
1030    /**
1031     * Closes the path and strokes it.
1032     */

1033    
1034    public void closePathStroke() {
1035        content.append("s").append_i(separator);
1036    }
1037    
1038    /**
1039     * Fills the path, using the non-zero winding number rule to determine the region to fill.
1040     */

1041    
1042    public void fill() {
1043        content.append("f").append_i(separator);
1044    }
1045    
1046    /**
1047     * Fills the path, using the even-odd rule to determine the region to fill.
1048     */

1049    
1050    public void eoFill() {
1051        content.append("f*").append_i(separator);
1052    }
1053    
1054    /**
1055     * Fills the path using the non-zero winding number rule to determine the region to fill and strokes it.
1056     */

1057    
1058    public void fillStroke() {
1059        content.append("B").append_i(separator);
1060    }
1061    
1062    /**
1063     * Closes the path, fills it using the non-zero winding number rule to determine the region to fill and strokes it.
1064     */

1065    
1066    public void closePathFillStroke() {
1067        content.append("b").append_i(separator);
1068    }
1069    
1070    /**
1071     * Fills the path, using the even-odd rule to determine the region to fill and strokes it.
1072     */

1073    
1074    public void eoFillStroke() {
1075        content.append("B*").append_i(separator);
1076    }
1077    
1078    /**
1079     * Closes the path, fills it using the even-odd rule to determine the region to fill and strokes it.
1080     */

1081    
1082    public void closePathEoFillStroke() {
1083        content.append("b*").append_i(separator);
1084    }
1085    
1086    /**
1087     * Adds an <CODE>Image</CODE> to the page. The <CODE>Image</CODE> must have
1088     * absolute positioning.
1089     * @param image the <CODE>Image</CODE> object
1090     * @throws DocumentException if the <CODE>Image</CODE> does not have absolute positioning
1091     */

1092    public void addImage(Image image) throws DocumentException {
1093        addImage(image, false);
1094    }
1095    
1096    /**
1097     * Adds an <CODE>Image</CODE> to the page. The <CODE>Image</CODE> must have
1098     * absolute positioning. The image can be placed inline.
1099     * @param image the <CODE>Image</CODE> object
1100     * @param inlineImage <CODE>true</CODE> to place this image inline, <CODE>false</CODE> otherwise
1101     * @throws DocumentException if the <CODE>Image</CODE> does not have absolute positioning
1102     */

1103    public void addImage(Image image, boolean inlineImage) throws DocumentException {
1104        if (!image.hasAbsoluteY())
1105            throw new DocumentException("The image must have absolute positioning.");
1106        float matrix[] = image.matrix();
1107        matrix[Image.CX] = image.getAbsoluteX() - matrix[Image.CX];
1108        matrix[Image.CY] = image.getAbsoluteY() - matrix[Image.CY];
1109        addImage(image, matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5], inlineImage);
1110    }
1111    
1112    /**
1113     * Adds an <CODE>Image</CODE> to the page. The positioning of the <CODE>Image</CODE>
1114     * is done with the transformation matrix. To position an <CODE>image</CODE> at (x,y)
1115     * use addImage(image, image_width, 0, 0, image_height, x, y).
1116     * @param image the <CODE>Image</CODE> object
1117     * @param a an element of the transformation matrix
1118     * @param b an element of the transformation matrix
1119     * @param c an element of the transformation matrix
1120     * @param d an element of the transformation matrix
1121     * @param e an element of the transformation matrix
1122     * @param f an element of the transformation matrix
1123     * @throws DocumentException on error
1124     */

1125    public void addImage(Image image, float a, float b, float c, float d, float e, float f) throws DocumentException {
1126        addImage(image, a, b, c, d, e, f, false);
1127    }
1128    
1129    /**
1130     * Adds an <CODE>Image</CODE> to the page. The positioning of the <CODE>Image</CODE>
1131     * is done with the transformation matrix. To position an <CODE>image</CODE> at (x,y)
1132     * use addImage(image, image_width, 0, 0, image_height, x, y). The image can be placed inline.
1133     * @param image the <CODE>Image</CODE> object
1134     * @param a an element of the transformation matrix
1135     * @param b an element of the transformation matrix
1136     * @param c an element of the transformation matrix
1137     * @param d an element of the transformation matrix
1138     * @param e an element of the transformation matrix
1139     * @param f an element of the transformation matrix
1140     * @param inlineImage <CODE>true</CODE> to place this image inline, <CODE>false</CODE> otherwise
1141     * @throws DocumentException on error
1142     */

1143    public void addImage(Image image, float a, float b, float c, float d, float e, float f, boolean inlineImage) throws DocumentException {
1144        try {
1145            if (image.getLayer() != null)
1146                beginLayer(image.getLayer());
1147            if (image.isImgTemplate()) {
1148                writer.addDirectImageSimple(image);
1149                PdfTemplate template = image.getTemplateData();
1150                float w = template.getWidth();
1151                float h = template.getHeight();
1152                addTemplate(template, a / w, b / w, c / h, d / h, e, f);
1153            }
1154            else {
1155                content.append("q ");
1156                content.append(a).append(' ');
1157                content.append(b).append(' ');
1158                content.append(c).append(' ');
1159                content.append(d).append(' ');
1160                content.append(e).append(' ');
1161                content.append(f).append(" cm");
1162                if (inlineImage) {
1163                    content.append("\nBI\n");
1164                    PdfImage pimage = new PdfImage(image, "", null);
1165                    for (Iterator JavaDoc it = pimage.getKeys().iterator(); it.hasNext();) {
1166                        PdfName key = (PdfName)it.next();
1167                        PdfObject value = pimage.get(key);
1168                        String JavaDoc s = (String JavaDoc)abrev.get(key);
1169                        if (s == null)
1170                            continue;
1171                        content.append(s);
1172                        boolean check = true;
1173                        if (key.equals(PdfName.COLORSPACE) && value.isArray()) {
1174                            ArrayList JavaDoc ar = ((PdfArray)value).getArrayList();
1175                            if (ar.size() == 4
1176                                && PdfName.INDEXED.equals(ar.get(0))
1177                                && ((PdfObject)ar.get(1)).isName()
1178                                && ((PdfObject)ar.get(2)).isNumber()
1179                                && ((PdfObject)ar.get(3)).isString()
1180                            ) {
1181                                check = false;
1182                            }
1183                            
1184                        }
1185                        if (check && key.equals(PdfName.COLORSPACE) && !value.isName()) {
1186                            PdfName cs = writer.getColorspaceName();
1187                            PageResources prs = getPageResources();
1188                            prs.addColor(cs, writer.addToBody(value).getIndirectReference());
1189                            value = cs;
1190                        }
1191                        value.toPdf(null, content);
1192                        content.append('\n');
1193                    }
1194                    content.append("ID\n");
1195                    pimage.writeContent(content);
1196                    content.append("\nEI\nQ").append_i(separator);
1197                }
1198                else {
1199                    PdfName name;
1200                    PageResources prs = getPageResources();
1201                    Image maskImage = image.getImageMask();
1202                    if (maskImage != null) {
1203                        name = writer.addDirectImageSimple(maskImage);
1204                        prs.addXObject(name, writer.getImageReference(name));
1205                    }
1206                    name = writer.addDirectImageSimple(image);
1207                    name = prs.addXObject(name, writer.getImageReference(name));
1208                    content.append(' ').append(name.getBytes()).append(" Do Q").append_i(separator);
1209                }
1210            }
1211            if (image.hasBorders()) {
1212                saveState();
1213                float w = image.getWidth();
1214                float h = image.getHeight();
1215                concatCTM(a / w, b / w, c / h, d / h, e, f);
1216                rectangle(image);
1217                restoreState();
1218            }
1219            if (image.getLayer() != null)
1220                endLayer();
1221            Annotation annot = image.getAnnotation();
1222            if (annot == null)
1223                return;
1224            float[] r = new float[unitRect.length];
1225            for (int k = 0; k < unitRect.length; k += 2) {
1226                r[k] = a * unitRect[k] + c * unitRect[k + 1] + e;
1227                r[k + 1] = b * unitRect[k] + d * unitRect[k + 1] + f;
1228            }
1229            float llx = r[0];
1230            float lly = r[1];
1231            float urx = llx;
1232            float ury = lly;
1233            for (int k = 2; k < r.length; k += 2) {
1234                llx = Math.min(llx, r[k]);
1235                lly = Math.min(lly, r[k + 1]);
1236                urx = Math.max(urx, r[k]);
1237                ury = Math.max(ury, r[k + 1]);
1238            }
1239            annot = new Annotation(annot);
1240            annot.setDimensions(llx, lly, urx, ury);
1241            PdfAnnotation an = PdfAnnotationsImp.convertAnnotation(writer, annot, new Rectangle(llx, lly, urx, ury));
1242            if (an == null)
1243                return;
1244            addAnnotation(an);
1245        }
1246        catch (Exception JavaDoc ee) {
1247            throw new DocumentException(ee);
1248        }
1249    }
1250    
1251    /**
1252     * Makes this <CODE>PdfContentByte</CODE> empty.
1253     */

1254    public void reset() {
1255        content.reset();
1256        stateList.clear();
1257        state = new GraphicState();
1258    }
1259    
1260    /**
1261     * Starts the writing of text.
1262     */

1263    public void beginText() {
1264        state.xTLM = 0;
1265        state.yTLM = 0;
1266        content.append("BT").append_i(separator);
1267    }
1268    
1269    /**
1270     * Ends the writing of text and makes the current font invalid.
1271     */

1272    public void endText() {
1273        content.append("ET").append_i(separator);
1274    }
1275    
1276    /**
1277     * Saves the graphic state. <CODE>saveState</CODE> and
1278     * <CODE>restoreState</CODE> must be balanced.
1279     */

1280    public void saveState() {
1281        content.append("q").append_i(separator);
1282        stateList.add(new GraphicState(state));
1283    }
1284    
1285    /**
1286     * Restores the graphic state. <CODE>saveState</CODE> and
1287     * <CODE>restoreState</CODE> must be balanced.
1288     */

1289    public void restoreState() {
1290        content.append("Q").append_i(separator);
1291        int idx = stateList.size() - 1;
1292        if (idx < 0)
1293            throw new RuntimeException JavaDoc("Unbalanced save/restore state operators.");
1294        state = (GraphicState)stateList.get(idx);
1295        stateList.remove(idx);
1296    }
1297    
1298    /**
1299     * Sets the character spacing parameter.
1300     *
1301     * @param charSpace a parameter
1302     */

1303    public void setCharacterSpacing(float charSpace) {
1304        state.charSpace = charSpace;
1305        content.append(charSpace).append(" Tc").append_i(separator);
1306    }
1307    
1308    /**
1309     * Sets the word spacing parameter.
1310     *
1311     * @param wordSpace a parameter
1312     */

1313    public void setWordSpacing(float wordSpace) {
1314        state.wordSpace = wordSpace;
1315        content.append(wordSpace).append(" Tw").append_i(separator);
1316    }
1317    
1318    /**
1319     * Sets the horizontal scaling parameter.
1320     *
1321     * @param scale a parameter
1322     */

1323    public void setHorizontalScaling(float scale) {
1324        state.scale = scale;
1325        content.append(scale).append(" Tz").append_i(separator);
1326    }
1327    
1328    /**
1329     * Sets the text leading parameter.
1330     * <P>
1331     * The leading parameter is measured in text space units. It specifies the vertical distance
1332     * between the baselines of adjacent lines of text.</P>
1333     *
1334     * @param leading the new leading
1335     */

1336    public void setLeading(float leading) {
1337        state.leading = leading;
1338        content.append(leading).append(" TL").append_i(separator);
1339    }
1340    
1341    /**
1342     * Set the font and the size for the subsequent text writing.
1343     *
1344     * @param bf the font
1345     * @param size the font size in points
1346     */

1347    public void setFontAndSize(BaseFont bf, float size) {
1348        checkWriter();
1349        state.size = size;
1350        state.fontDetails = writer.addSimple(bf);
1351        PageResources prs = getPageResources();
1352        PdfName name = state.fontDetails.getFontName();
1353        name = prs.addFont(name, state.fontDetails.getIndirectReference());
1354        content.append(name.getBytes()).append(' ').append(size).append(" Tf").append_i(separator);
1355    }
1356    
1357    /**
1358     * Sets the text rendering parameter.
1359     *
1360     * @param rendering a parameter
1361     */

1362    public void setTextRenderingMode(int rendering) {
1363        content.append(rendering).append(" Tr").append_i(separator);
1364    }
1365    
1366    /**
1367     * Sets the text rise parameter.
1368     * <P>
1369     * This allows to write text in subscript or superscript mode.</P>
1370     *
1371     * @param rise a parameter
1372     */

1373    public void setTextRise(float rise) {
1374        content.append(rise).append(" Ts").append_i(separator);
1375    }
1376    
1377    /**
1378     * A helper to insert into the content stream the <CODE>text</CODE>
1379     * converted to bytes according to the font's encoding.
1380     *
1381     * @param text the text to write
1382     */

1383    private void showText2(String JavaDoc text) {
1384        if (state.fontDetails == null)
1385            throw new NullPointerException JavaDoc("Font and size must be set before writing any text");
1386        byte b[] = state.fontDetails.convertToBytes(text);
1387        escapeString(b, content);
1388    }
1389    
1390    /**
1391     * Shows the <CODE>text</CODE>.
1392     *
1393     * @param text the text to write
1394     */

1395    public void showText(String JavaDoc text) {
1396        showText2(text);
1397        content.append("Tj").append_i(separator);
1398    }
1399    
1400    /**
1401     * Constructs a kern array for a text in a certain font
1402     * @param text the text
1403     * @param font the font
1404     * @return a PdfTextArray
1405     */

1406    public static PdfTextArray getKernArray(String JavaDoc text, BaseFont font) {
1407        PdfTextArray pa = new PdfTextArray();
1408        StringBuffer JavaDoc acc = new StringBuffer JavaDoc();
1409        int len = text.length() - 1;
1410        char c[] = text.toCharArray();
1411        if (len >= 0)
1412            acc.append(c, 0, 1);
1413        for (int k = 0; k < len; ++k) {
1414            char c2 = c[k + 1];
1415            int kern = font.getKerning(c[k], c2);
1416            if (kern == 0) {
1417                acc.append(c2);
1418            }
1419            else {
1420                pa.add(acc.toString());
1421                acc.setLength(0);
1422                acc.append(c, k + 1, 1);
1423                pa.add(-kern);
1424            }
1425        }
1426        pa.add(acc.toString());
1427        return pa;
1428    }
1429    
1430    /**
1431     * Shows the <CODE>text</CODE> kerned.
1432     *
1433     * @param text the text to write
1434     */

1435    public void showTextKerned(String JavaDoc text) {
1436        if (state.fontDetails == null)
1437            throw new NullPointerException JavaDoc("Font and size must be set before writing any text");
1438        BaseFont bf = state.fontDetails.getBaseFont();
1439        if (bf.hasKernPairs())
1440            showText(getKernArray(text, bf));
1441        else
1442            showText(text);
1443    }
1444    
1445    /**
1446     * Moves to the next line and shows <CODE>text</CODE>.
1447     *
1448     * @param text the text to write
1449     */

1450    public void newlineShowText(String JavaDoc text) {
1451        state.yTLM -= state.leading;
1452        showText2(text);
1453        content.append("'").append_i(separator);
1454    }
1455    
1456    /**
1457     * Moves to the next line and shows text string, using the given values of the character and word spacing parameters.
1458     *
1459     * @param wordSpacing a parameter
1460     * @param charSpacing a parameter
1461     * @param text the text to write
1462     */

1463    public void newlineShowText(float wordSpacing, float charSpacing, String JavaDoc text) {
1464        state.yTLM -= state.leading;
1465        content.append(wordSpacing).append(' ').append(charSpacing);
1466        showText2(text);
1467        content.append("\"").append_i(separator);
1468        
1469        // The " operator sets charSpace and wordSpace into graphics state
1470
// (cfr PDF reference v1.6, table 5.6)
1471
state.charSpace = charSpacing;
1472        state.wordSpace = wordSpacing;
1473    }
1474    
1475    /**
1476     * Changes the text matrix.
1477     * <P>
1478     * Remark: this operation also initializes the current point position.</P>
1479     *
1480     * @param a operand 1,1 in the matrix
1481     * @param b operand 1,2 in the matrix
1482     * @param c operand 2,1 in the matrix
1483     * @param d operand 2,2 in the matrix
1484     * @param x operand 3,1 in the matrix
1485     * @param y operand 3,2 in the matrix
1486     */

1487    public void setTextMatrix(float a, float b, float c, float d, float x, float y) {
1488        state.xTLM = x;
1489        state.yTLM = y;
1490        content.append(a).append(' ').append(b).append_i(' ')
1491        .append(c).append_i(' ').append(d).append_i(' ')
1492        .append(x).append_i(' ').append(y).append(" Tm").append_i(separator);
1493    }
1494    
1495    /**
1496     * Changes the text matrix. The first four parameters are {1,0,0,1}.
1497     * <P>
1498     * Remark: this operation also initializes the current point position.</P>
1499     *
1500     * @param x operand 3,1 in the matrix
1501     * @param y operand 3,2 in the matrix
1502     */

1503    public void setTextMatrix(float x, float y) {
1504        setTextMatrix(1, 0, 0, 1, x, y);
1505    }
1506    
1507    /**
1508     * Moves to the start of the next line, offset from the start of the current line.
1509     *
1510     * @param x x-coordinate of the new current point
1511     * @param y y-coordinate of the new current point
1512     */

1513    public void moveText(float x, float y) {
1514        state.xTLM += x;
1515        state.yTLM += y;
1516        content.append(x).append(' ').append(y).append(" Td").append_i(separator);
1517    }
1518    
1519    /**
1520     * Moves to the start of the next line, offset from the start of the current line.
1521     * <P>
1522     * As a side effect, this sets the leading parameter in the text state.</P>
1523     *
1524     * @param x offset of the new current point
1525     * @param y y-coordinate of the new current point
1526     */

1527    public void moveTextWithLeading(float x, float y) {
1528        state.xTLM += x;
1529        state.yTLM += y;
1530        state.leading = -y;
1531        content.append(x).append(' ').append(y).append(" TD").append_i(separator);
1532    }
1533    
1534    /**
1535     * Moves to the start of the next line.
1536     */

1537    public void newlineText() {
1538        state.yTLM -= state.leading;
1539        content.append("T*").append_i(separator);
1540    }
1541    
1542    /**
1543     * Gets the size of this content.
1544     *
1545     * @return the size of the content
1546     */

1547    int size() {
1548        return content.size();
1549    }
1550    
1551    /**
1552     * Escapes a <CODE>byte</CODE> array according to the PDF conventions.
1553     *
1554     * @param b the <CODE>byte</CODE> array to escape
1555     * @return an escaped <CODE>byte</CODE> array
1556     */

1557    static byte[] escapeString(byte b[]) {
1558        ByteBuffer content = new ByteBuffer();
1559        escapeString(b, content);
1560        return content.toByteArray();
1561    }
1562    
1563    /**
1564     * Escapes a <CODE>byte</CODE> array according to the PDF conventions.
1565     *
1566     * @param b the <CODE>byte</CODE> array to escape
1567     * @param content the content
1568     */

1569    static void escapeString(byte b[], ByteBuffer content) {
1570        content.append_i('(');
1571        for (int k = 0; k < b.length; ++k) {
1572            byte c = b[k];
1573            switch ((int)c) {
1574                case '\r':
1575                    content.append("\\r");
1576                    break;
1577                case '\n':
1578                    content.append("\\n");
1579                    break;
1580                case '\t':
1581                    content.append("\\t");
1582                    break;
1583                case '\b':
1584                    content.append("\\b");
1585                    break;
1586                case '\f':
1587                    content.append("\\f");
1588                    break;
1589                case '(':
1590                case ')':
1591                case '\\':
1592                    content.append_i('\\').append_i(c);
1593                    break;
1594                default:
1595                    content.append_i(c);
1596            }
1597        }
1598        content.append(")");
1599    }
1600    
1601    /**
1602     * Adds an outline to the document.
1603     *
1604     * @param outline the outline
1605     * @deprecated not needed anymore. The outlines are extracted
1606     * from the root outline
1607     */

1608    public void addOutline(PdfOutline outline) {
1609        // for compatibility
1610
}
1611    /**
1612     * Adds a named outline to the document.
1613     *
1614     * @param outline the outline
1615     * @param name the name for the local destination
1616     */

1617    public void addOutline(PdfOutline outline, String JavaDoc name) {
1618        checkWriter();
1619        pdf.addOutline(outline, name);
1620    }
1621    /**
1622     * Gets the root outline.
1623     *
1624     * @return the root outline
1625     */

1626    public PdfOutline getRootOutline() {
1627        checkWriter();
1628        return pdf.getRootOutline();
1629    }
1630    
1631    /**
1632     * Computes the width of the given string taking in account
1633     * the current values of "Character spacing", "Word Spacing"
1634     * and "Horizontal Scaling".
1635     * The additional spacing is not computed for the last character
1636     * of the string.
1637     * @param text the string to get width of
1638     * @param kerned the kerning option
1639     * @return the width
1640     */

1641
1642    public float getEffectiveStringWidth(String JavaDoc text, boolean kerned) {
1643        BaseFont bf = state.fontDetails.getBaseFont();
1644        
1645        float w;
1646        if (kerned)
1647            w = bf.getWidthPointKerned(text, state.size);
1648        else
1649            w = bf.getWidthPoint(text, state.size);
1650        
1651        if (state.charSpace != 0.0f && text.length() > 1) {
1652            w += state.charSpace * (text.length() -1);
1653        }
1654        
1655        int ft = bf.getFontType();
1656        if (state.wordSpace != 0.0f && (ft == BaseFont.FONT_TYPE_T1 || ft == BaseFont.FONT_TYPE_TT || ft == BaseFont.FONT_TYPE_T3)) {
1657            for (int i = 0; i < (text.length() -1); i++) {
1658                if (text.charAt(i) == ' ')
1659                    w += state.wordSpace;
1660            }
1661        }
1662        if (state.scale != 100.0)
1663            w = (w * state.scale) / 100.0f;
1664        
1665        //System.out.println("String width = " + Float.toString(w));
1666
return w;
1667    }
1668    
1669    /**
1670     * Shows text right, left or center aligned with rotation.
1671     * @param alignment the alignment can be ALIGN_CENTER, ALIGN_RIGHT or ALIGN_LEFT
1672     * @param text the text to show
1673     * @param x the x pivot position
1674     * @param y the y pivot position
1675     * @param rotation the rotation to be applied in degrees counterclockwise
1676     */

1677    public void showTextAligned(int alignment, String JavaDoc text, float x, float y, float rotation) {
1678        showTextAligned(alignment, text, x, y, rotation, false);
1679    }
1680    
1681    private void showTextAligned(int alignment, String JavaDoc text, float x, float y, float rotation, boolean kerned) {
1682        if (state.fontDetails == null)
1683            throw new NullPointerException JavaDoc("Font and size must be set before writing any text");
1684        if (rotation == 0) {
1685            switch (alignment) {
1686                case ALIGN_CENTER:
1687                    x -= getEffectiveStringWidth(text, kerned) / 2;
1688                    break;
1689                case ALIGN_RIGHT:
1690                    x -= getEffectiveStringWidth(text, kerned);
1691                    break;
1692            }
1693            setTextMatrix(x, y);
1694            if (kerned)
1695                showTextKerned(text);
1696            else
1697                showText(text);
1698        }
1699        else {
1700            double alpha = rotation * Math.PI / 180.0;
1701            float cos = (float)Math.cos(alpha);
1702            float sin = (float)Math.sin(alpha);
1703            float len;
1704            switch (alignment) {
1705                case ALIGN_CENTER:
1706                    len = getEffectiveStringWidth(text, kerned) / 2;
1707                    x -= len * cos;
1708                    y -= len * sin;
1709                    break;
1710                case ALIGN_RIGHT:
1711                    len = getEffectiveStringWidth(text, kerned);
1712                    x -= len * cos;
1713                    y -= len * sin;
1714                    break;
1715            }
1716            setTextMatrix(cos, sin, -sin, cos, x, y);
1717            if (kerned)
1718                showTextKerned(text);
1719            else
1720                showText(text);
1721            setTextMatrix(0f, 0f);
1722        }
1723    }
1724    
1725    /**
1726     * Shows text kerned right, left or center aligned with rotation.
1727     * @param alignment the alignment can be ALIGN_CENTER, ALIGN_RIGHT or ALIGN_LEFT
1728     * @param text the text to show
1729     * @param x the x pivot position
1730     * @param y the y pivot position
1731     * @param rotation the rotation to be applied in degrees counterclockwise
1732     */

1733    public void showTextAlignedKerned(int alignment, String JavaDoc text, float x, float y, float rotation) {
1734        showTextAligned(alignment, text, x, y, rotation, true);
1735    }
1736    
1737    /**
1738     * Concatenate a matrix to the current transformation matrix.
1739     * @param a an element of the transformation matrix
1740     * @param b an element of the transformation matrix
1741     * @param c an element of the transformation matrix
1742     * @param d an element of the transformation matrix
1743     * @param e an element of the transformation matrix
1744     * @param f an element of the transformation matrix
1745     **/

1746    public void concatCTM(float a, float b, float c, float d, float e, float f) {
1747        content.append(a).append(' ').append(b).append(' ').append(c).append(' ');
1748        content.append(d).append(' ').append(e).append(' ').append(f).append(" cm").append_i(separator);
1749    }
1750    
1751    /**
1752     * Generates an array of bezier curves to draw an arc.
1753     * <P>
1754     * (x1, y1) and (x2, y2) are the corners of the enclosing rectangle.
1755     * Angles, measured in degrees, start with 0 to the right (the positive X
1756     * axis) and increase counter-clockwise. The arc extends from startAng
1757     * to startAng+extent. I.e. startAng=0 and extent=180 yields an openside-down
1758     * semi-circle.
1759     * <P>
1760     * The resulting coordinates are of the form float[]{x1,y1,x2,y2,x3,y3, x4,y4}
1761     * such that the curve goes from (x1, y1) to (x4, y4) with (x2, y2) and
1762     * (x3, y3) as their respective Bezier control points.
1763     * <P>
1764     * Note: this code was taken from ReportLab (www.reportlab.org), an excelent
1765     * PDF generator for Python (BSD license: http://www.reportlab.org/devfaq.html#1.3 ).
1766     *
1767     * @param x1 a corner of the enclosing rectangle
1768     * @param y1 a corner of the enclosing rectangle
1769     * @param x2 a corner of the enclosing rectangle
1770     * @param y2 a corner of the enclosing rectangle
1771     * @param startAng starting angle in degrees
1772     * @param extent angle extent in degrees
1773     * @return a list of float[] with the bezier curves
1774     */

1775    public static ArrayList JavaDoc bezierArc(float x1, float y1, float x2, float y2, float startAng, float extent) {
1776        float tmp;
1777        if (x1 > x2) {
1778            tmp = x1;
1779            x1 = x2;
1780            x2 = tmp;
1781        }
1782        if (y2 > y1) {
1783            tmp = y1;
1784            y1 = y2;
1785            y2 = tmp;
1786        }
1787        
1788        float fragAngle;
1789        int Nfrag;
1790        if (Math.abs(extent) <= 90f) {
1791            fragAngle = extent;
1792            Nfrag = 1;
1793        }
1794        else {
1795            Nfrag = (int)(Math.ceil(Math.abs(extent)/90f));
1796            fragAngle = extent / Nfrag;
1797        }
1798        float x_cen = (x1+x2)/2f;
1799        float y_cen = (y1+y2)/2f;
1800        float rx = (x2-x1)/2f;
1801        float ry = (y2-y1)/2f;
1802        float halfAng = (float)(fragAngle * Math.PI / 360.);
1803        float kappa = (float)(Math.abs(4. / 3. * (1. - Math.cos(halfAng)) / Math.sin(halfAng)));
1804        ArrayList JavaDoc pointList = new ArrayList JavaDoc();
1805        for (int i = 0; i < Nfrag; ++i) {
1806            float theta0 = (float)((startAng + i*fragAngle) * Math.PI / 180.);
1807            float theta1 = (float)((startAng + (i+1)*fragAngle) * Math.PI / 180.);
1808            float cos0 = (float)Math.cos(theta0);
1809            float cos1 = (float)Math.cos(theta1);
1810            float sin0 = (float)Math.sin(theta0);
1811            float sin1 = (float)Math.sin(theta1);
1812            if (fragAngle > 0f) {
1813                pointList.add(new float[]{x_cen + rx * cos0,
1814                y_cen - ry * sin0,
1815                x_cen + rx * (cos0 - kappa * sin0),
1816                y_cen - ry * (sin0 + kappa * cos0),
1817                x_cen + rx * (cos1 + kappa * sin1),
1818                y_cen - ry * (sin1 - kappa * cos1),
1819                x_cen + rx * cos1,
1820                y_cen - ry * sin1});
1821            }
1822            else {
1823                pointList.add(new float[]{x_cen + rx * cos0,
1824                y_cen - ry * sin0,
1825                x_cen + rx * (cos0 + kappa * sin0),
1826                y_cen - ry * (sin0 - kappa * cos0),
1827                x_cen + rx * (cos1 - kappa * sin1),
1828                y_cen - ry * (sin1 + kappa * cos1),
1829                x_cen + rx * cos1,
1830                y_cen - ry * sin1});
1831            }
1832        }
1833        return pointList;
1834    }
1835    
1836    /**
1837     * Draws a partial ellipse inscribed within the rectangle x1,y1,x2,y2,
1838     * starting at startAng degrees and covering extent degrees. Angles
1839     * start with 0 to the right (+x) and increase counter-clockwise.
1840     *
1841     * @param x1 a corner of the enclosing rectangle
1842     * @param y1 a corner of the enclosing rectangle
1843     * @param x2 a corner of the enclosing rectangle
1844     * @param y2 a corner of the enclosing rectangle
1845     * @param startAng starting angle in degrees
1846     * @param extent angle extent in degrees
1847     */

1848    public void arc(float x1, float y1, float x2, float y2, float startAng, float extent) {
1849        ArrayList JavaDoc ar = bezierArc(x1, y1, x2, y2, startAng, extent);
1850        if (ar.isEmpty())
1851            return;
1852        float pt[] = (float [])ar.get(0);
1853        moveTo(pt[0], pt[1]);
1854        for (int k = 0; k < ar.size(); ++k) {
1855            pt = (float [])ar.get(k);
1856            curveTo(pt[2], pt[3], pt[4], pt[5], pt[6], pt[7]);
1857        }
1858    }
1859    
1860    /**
1861     * Draws an ellipse inscribed within the rectangle x1,y1,x2,y2.
1862     *
1863     * @param x1 a corner of the enclosing rectangle
1864     * @param y1 a corner of the enclosing rectangle
1865     * @param x2 a corner of the enclosing rectangle
1866     * @param y2 a corner of the enclosing rectangle
1867     */

1868    public void ellipse(float x1, float y1, float x2, float y2) {
1869        arc(x1, y1, x2, y2, 0f, 360f);
1870    }
1871    
1872    /**
1873     * Create a new colored tiling pattern.
1874     *
1875     * @param width the width of the pattern
1876     * @param height the height of the pattern
1877     * @param xstep the desired horizontal spacing between pattern cells.
1878     * May be either positive or negative, but not zero.
1879     * @param ystep the desired vertical spacing between pattern cells.
1880     * May be either positive or negative, but not zero.
1881     * @return the <CODE>PdfPatternPainter</CODE> where the pattern will be created
1882     */

1883    public PdfPatternPainter createPattern(float width, float height, float xstep, float ystep) {
1884        checkWriter();
1885        if ( xstep == 0.0f || ystep == 0.0f )
1886            throw new RuntimeException JavaDoc("XStep or YStep can not be ZERO.");
1887        PdfPatternPainter painter = new PdfPatternPainter(writer);
1888        painter.setWidth(width);
1889        painter.setHeight(height);
1890        painter.setXStep(xstep);
1891        painter.setYStep(ystep);
1892        writer.addSimplePattern(painter);
1893        return painter;
1894    }
1895    
1896    /**
1897     * Create a new colored tiling pattern. Variables xstep and ystep are set to the same values
1898     * of width and height.
1899     * @param width the width of the pattern
1900     * @param height the height of the pattern
1901     * @return the <CODE>PdfPatternPainter</CODE> where the pattern will be created
1902     */

1903    public PdfPatternPainter createPattern(float width, float height) {
1904        return createPattern(width, height, width, height);
1905    }
1906    
1907    /**
1908     * Create a new uncolored tiling pattern.
1909     *
1910     * @param width the width of the pattern
1911     * @param height the height of the pattern
1912     * @param xstep the desired horizontal spacing between pattern cells.
1913     * May be either positive or negative, but not zero.
1914     * @param ystep the desired vertical spacing between pattern cells.
1915     * May be either positive or negative, but not zero.
1916     * @param color the default color. Can be <CODE>null</CODE>
1917     * @return the <CODE>PdfPatternPainter</CODE> where the pattern will be created
1918     */

1919    public PdfPatternPainter createPattern(float width, float height, float xstep, float ystep, Color JavaDoc color) {
1920        checkWriter();
1921        if ( xstep == 0.0f || ystep == 0.0f )
1922            throw new RuntimeException JavaDoc("XStep or YStep can not be ZERO.");
1923        PdfPatternPainter painter = new PdfPatternPainter(writer, color);
1924        painter.setWidth(width);
1925        painter.setHeight(height);
1926        painter.setXStep(xstep);
1927        painter.setYStep(ystep);
1928        writer.addSimplePattern(painter);
1929        return painter;
1930    }
1931    
1932    /**
1933     * Create a new uncolored tiling pattern.
1934     * Variables xstep and ystep are set to the same values
1935     * of width and height.
1936     * @param width the width of the pattern
1937     * @param height the height of the pattern
1938     * @param color the default color. Can be <CODE>null</CODE>
1939     * @return the <CODE>PdfPatternPainter</CODE> where the pattern will be created
1940     */

1941    public PdfPatternPainter createPattern(float width, float height, Color JavaDoc color) {
1942        return createPattern(width, height, width, height, color);
1943    }
1944    
1945    /**
1946     * Creates a new template.
1947     * <P>
1948     * Creates a new template that is nothing more than a form XObject. This template can be included
1949     * in this <CODE>PdfContentByte</CODE> or in another template. Templates are only written
1950     * to the output when the document is closed permitting things like showing text in the first page
1951     * that is only defined in the last page.
1952     *
1953     * @param width the bounding box width
1954     * @param height the bounding box height
1955     * @return the templated created
1956     */

1957    public PdfTemplate createTemplate(float width, float height) {
1958        return createTemplate(width, height, null);
1959    }
1960    
1961    PdfTemplate createTemplate(float width, float height, PdfName forcedName) {
1962        checkWriter();
1963        PdfTemplate template = new PdfTemplate(writer);
1964        template.setWidth(width);
1965        template.setHeight(height);
1966        writer.addDirectTemplateSimple(template, forcedName);
1967        return template;
1968    }
1969    
1970    /**
1971     * Creates a new appearance to be used with form fields.
1972     *
1973     * @param width the bounding box width
1974     * @param height the bounding box height
1975     * @return the appearance created
1976     */

1977    public PdfAppearance createAppearance(float width, float height) {
1978        return createAppearance(width, height, null);
1979    }
1980    
1981    PdfAppearance createAppearance(float width, float height, PdfName forcedName) {
1982        checkWriter();
1983        PdfAppearance template = new PdfAppearance(writer);
1984        template.setWidth(width);
1985        template.setHeight(height);
1986        writer.addDirectTemplateSimple(template, forcedName);
1987        return template;
1988    }
1989    
1990    /**
1991     * Adds a PostScript XObject to this content.
1992     *
1993     * @param psobject the object
1994     */

1995    public void addPSXObject(PdfPSXObject psobject) {
1996        checkWriter();
1997        PdfName name = writer.addDirectTemplateSimple(psobject, null);
1998        PageResources prs = getPageResources();
1999        name = prs.addXObject(name, psobject.getIndirectReference());
2000        content.append(name.getBytes()).append(" Do").append_i(separator);
2001    }
2002    
2003    /**
2004     * Adds a template to this content.
2005     *
2006     * @param template the template
2007     * @param a an element of the transformation matrix
2008     * @param b an element of the transformation matrix
2009     * @param c an element of the transformation matrix
2010     * @param d an element of the transformation matrix
2011     * @param e an element of the transformation matrix
2012     * @param f an element of the transformation matrix
2013     */

2014    public void addTemplate(PdfTemplate template, float a, float b, float c, float d, float e, float f) {
2015        checkWriter();
2016        checkNoPattern(template);
2017        PdfName name = writer.addDirectTemplateSimple(template, null);
2018        PageResources prs = getPageResources();
2019        name = prs.addXObject(name, template.getIndirectReference());
2020        content.append("q ");
2021        content.append(a).append(' ');
2022        content.append(b).append(' ');
2023        content.append(c).append(' ');
2024        content.append(d).append(' ');
2025        content.append(e).append(' ');
2026        content.append(f).append(" cm ");
2027        content.append(name.getBytes()).append(" Do Q").append_i(separator);
2028    }
2029    
2030    void addTemplateReference(PdfIndirectReference template, PdfName name, float a, float b, float c, float d, float e, float f) {
2031        checkWriter();
2032        PageResources prs = getPageResources();
2033        name = prs.addXObject(name, template);
2034        content.append("q ");
2035        content.append(a).append(' ');
2036        content.append(b).append(' ');
2037        content.append(c).append(' ');
2038        content.append(d).append(' ');
2039        content.append(e).append(' ');
2040        content.append(f).append(" cm ");
2041        content.append(name.getBytes()).append(" Do Q").append_i(separator);
2042    }
2043    
2044    /**
2045     * Adds a template to this content.
2046     *
2047     * @param template the template
2048     * @param x the x location of this template
2049     * @param y the y location of this template
2050     */

2051    public void addTemplate(PdfTemplate template, float x, float y) {
2052        addTemplate(template, 1, 0, 0, 1, x, y);
2053    }
2054    
2055    /**
2056     * Changes the current color for filling paths (device dependent colors!).
2057     * <P>
2058     * Sets the color space to <B>DeviceCMYK</B> (or the <B>DefaultCMYK</B> color space),
2059     * and sets the color to use for filling paths.</P>
2060     * <P>
2061     * This method is described in the 'Portable Document Format Reference Manual version 1.3'
2062     * section 8.5.2.1 (page 331).</P>
2063     * <P>
2064     * Following the PDF manual, each operand must be a number between 0 (no ink) and
2065     * 1 (maximum ink). This method however accepts only integers between 0x00 and 0xFF.</P>
2066     *
2067     * @param cyan the intensity of cyan
2068     * @param magenta the intensity of magenta
2069     * @param yellow the intensity of yellow
2070     * @param black the intensity of black
2071     */

2072    
2073    public void setCMYKColorFill(int cyan, int magenta, int yellow, int black) {
2074        content.append((float)(cyan & 0xFF) / 0xFF);
2075        content.append(' ');
2076        content.append((float)(magenta & 0xFF) / 0xFF);
2077        content.append(' ');
2078        content.append((float)(yellow & 0xFF) / 0xFF);
2079        content.append(' ');
2080        content.append((float)(black & 0xFF) / 0xFF);
2081        content.append(" k").append_i(separator);
2082    }
2083    /**
2084     * Changes the current color for stroking paths (device dependent colors!).
2085     * <P>
2086     * Sets the color space to <B>DeviceCMYK</B> (or the <B>DefaultCMYK</B> color space),
2087     * and sets the color to use for stroking paths.</P>
2088     * <P>
2089     * This method is described in the 'Portable Document Format Reference Manual version 1.3'
2090     * section 8.5.2.1 (page 331).</P>
2091     * Following the PDF manual, each operand must be a number between 0 (miniumum intensity) and
2092     * 1 (maximum intensity). This method however accepts only integers between 0x00 and 0xFF.
2093     *
2094     * @param cyan the intensity of red
2095     * @param magenta the intensity of green
2096     * @param yellow the intensity of blue
2097     * @param black the intensity of black
2098     */

2099    
2100    public void setCMYKColorStroke(int cyan, int magenta, int yellow, int black) {
2101        content.append((float)(cyan & 0xFF) / 0xFF);
2102        content.append(' ');
2103        content.append((float)(magenta & 0xFF) / 0xFF);
2104        content.append(' ');
2105        content.append((float)(yellow & 0xFF) / 0xFF);
2106        content.append(' ');
2107        content.append((float)(black & 0xFF) / 0xFF);
2108        content.append(" K").append_i(separator);
2109    }
2110    
2111    /**
2112     * Changes the current color for filling paths (device dependent colors!).
2113     * <P>
2114     * Sets the color space to <B>DeviceRGB</B> (or the <B>DefaultRGB</B> color space),
2115     * and sets the color to use for filling paths.</P>
2116     * <P>
2117     * This method is described in the 'Portable Document Format Reference Manual version 1.3'
2118     * section 8.5.2.1 (page 331).</P>
2119     * <P>
2120     * Following the PDF manual, each operand must be a number between 0 (miniumum intensity) and
2121     * 1 (maximum intensity). This method however accepts only integers between 0x00 and 0xFF.</P>
2122     *
2123     * @param red the intensity of red
2124     * @param green the intensity of green
2125     * @param blue the intensity of blue
2126     */

2127    
2128    public void setRGBColorFill(int red, int green, int blue) {
2129        HelperRGB((float)(red & 0xFF) / 0xFF, (float)(green & 0xFF) / 0xFF, (float)(blue & 0xFF) / 0xFF);
2130        content.append(" rg").append_i(separator);
2131    }
2132    
2133    /**
2134     * Changes the current color for stroking paths (device dependent colors!).
2135     * <P>
2136     * Sets the color space to <B>DeviceRGB</B> (or the <B>DefaultRGB</B> color space),
2137     * and sets the color to use for stroking paths.</P>
2138     * <P>
2139     * This method is described in the 'Portable Document Format Reference Manual version 1.3'
2140     * section 8.5.2.1 (page 331).</P>
2141     * Following the PDF manual, each operand must be a number between 0 (miniumum intensity) and
2142     * 1 (maximum intensity). This method however accepts only integers between 0x00 and 0xFF.
2143     *
2144     * @param red the intensity of red
2145     * @param green the intensity of green
2146     * @param blue the intensity of blue
2147     */

2148    
2149    public void setRGBColorStroke(int red, int green, int blue) {
2150        HelperRGB((float)(red & 0xFF) / 0xFF, (float)(green & 0xFF) / 0xFF, (float)(blue & 0xFF) / 0xFF);
2151        content.append(" RG").append_i(separator);
2152    }
2153    
2154    /** Sets the stroke color. <CODE>color</CODE> can be an
2155     * <CODE>ExtendedColor</CODE>.
2156     * @param color the color
2157     */

2158    public void setColorStroke(Color JavaDoc color) {
2159        PdfXConformanceImp.checkPDFXConformance(writer, PdfXConformanceImp.PDFXKEY_COLOR, color);
2160        int type = ExtendedColor.getType(color);
2161        switch (type) {
2162            case ExtendedColor.TYPE_GRAY: {
2163                setGrayStroke(((GrayColor)color).getGray());
2164                break;
2165            }
2166            case ExtendedColor.TYPE_CMYK: {
2167                CMYKColor cmyk = (CMYKColor)color;
2168                setCMYKColorStrokeF(cmyk.getCyan(), cmyk.getMagenta(), cmyk.getYellow(), cmyk.getBlack());
2169                break;
2170            }
2171            case ExtendedColor.TYPE_SEPARATION: {
2172                SpotColor spot = (SpotColor)color;
2173                setColorStroke(spot.getPdfSpotColor(), spot.getTint());
2174                break;
2175            }
2176            case ExtendedColor.TYPE_PATTERN: {
2177                PatternColor pat = (PatternColor) color;
2178                setPatternStroke(pat.getPainter());
2179                break;
2180            }
2181            case ExtendedColor.TYPE_SHADING: {
2182                ShadingColor shading = (ShadingColor) color;
2183                setShadingStroke(shading.getPdfShadingPattern());
2184                break;
2185            }
2186            default:
2187                setRGBColorStroke(color.getRed(), color.getGreen(), color.getBlue());
2188        }
2189    }
2190    
2191    /** Sets the fill color. <CODE>color</CODE> can be an
2192     * <CODE>ExtendedColor</CODE>.
2193     * @param color the color
2194     */

2195    public void setColorFill(Color JavaDoc color) {
2196        PdfXConformanceImp.checkPDFXConformance(writer, PdfXConformanceImp.PDFXKEY_COLOR, color);
2197        int type = ExtendedColor.getType(color);
2198        switch (type) {
2199            case ExtendedColor.TYPE_GRAY: {
2200                setGrayFill(((GrayColor)color).getGray());
2201                break;
2202            }
2203            case ExtendedColor.TYPE_CMYK: {
2204                CMYKColor cmyk = (CMYKColor)color;
2205                setCMYKColorFillF(cmyk.getCyan(), cmyk.getMagenta(), cmyk.getYellow(), cmyk.getBlack());
2206                break;
2207            }
2208            case ExtendedColor.TYPE_SEPARATION: {
2209                SpotColor spot = (SpotColor)color;
2210                setColorFill(spot.getPdfSpotColor(), spot.getTint());
2211                break;
2212            }
2213            case ExtendedColor.TYPE_PATTERN: {
2214                PatternColor pat = (PatternColor) color;
2215                setPatternFill(pat.getPainter());
2216                break;
2217            }
2218            case ExtendedColor.TYPE_SHADING: {
2219                ShadingColor shading = (ShadingColor) color;
2220                setShadingFill(shading.getPdfShadingPattern());
2221                break;
2222            }
2223            default:
2224                setRGBColorFill(color.getRed(), color.getGreen(), color.getBlue());
2225        }
2226    }
2227    
2228    /** Sets the fill color to a spot color.
2229     * @param sp the spot color
2230     * @param tint the tint for the spot color. 0 is no color and 1
2231     * is 100% color
2232     */

2233    public void setColorFill(PdfSpotColor sp, float tint) {
2234        checkWriter();
2235        state.colorDetails = writer.addSimple(sp);
2236        PageResources prs = getPageResources();
2237        PdfName name = state.colorDetails.getColorName();
2238        name = prs.addColor(name, state.colorDetails.getIndirectReference());
2239        content.append(name.getBytes()).append(" cs ").append(tint).append(" scn").append_i(separator);
2240    }
2241    
2242    /** Sets the stroke color to a spot color.
2243     * @param sp the spot color
2244     * @param tint the tint for the spot color. 0 is no color and 1
2245     * is 100% color
2246     */

2247    public void setColorStroke(PdfSpotColor sp, float tint) {
2248        checkWriter();
2249        state.colorDetails = writer.addSimple(sp);
2250        PageResources prs = getPageResources();
2251        PdfName name = state.colorDetails.getColorName();
2252        name = prs.addColor(name, state.colorDetails.getIndirectReference());
2253        content.append(name.getBytes()).append(" CS ").append(tint).append(" SCN").append_i(separator);
2254    }
2255    
2256    /** Sets the fill color to a pattern. The pattern can be
2257     * colored or uncolored.
2258     * @param p the pattern
2259     */

2260    public void setPatternFill(PdfPatternPainter p) {
2261        if (p.isStencil()) {
2262            setPatternFill(p, p.getDefaultColor());
2263            return;
2264        }
2265        checkWriter();
2266        PageResources prs = getPageResources();
2267        PdfName name = writer.addSimplePattern(p);
2268        name = prs.addPattern(name, p.getIndirectReference());
2269        content.append(PdfName.PATTERN.getBytes()).append(" cs ").append(name.getBytes()).append(" scn").append_i(separator);
2270    }
2271    
2272    /** Outputs the color values to the content.
2273     * @param color The color
2274     * @param tint the tint if it is a spot color, ignored otherwise
2275     */

2276    void outputColorNumbers(Color JavaDoc color, float tint) {
2277        PdfXConformanceImp.checkPDFXConformance(writer, PdfXConformanceImp.PDFXKEY_COLOR, color);
2278        int type = ExtendedColor.getType(color);
2279        switch (type) {
2280            case ExtendedColor.TYPE_RGB:
2281                content.append((float)(color.getRed()) / 0xFF);
2282                content.append(' ');
2283                content.append((float)(color.getGreen()) / 0xFF);
2284                content.append(' ');
2285                content.append((float)(color.getBlue()) / 0xFF);
2286                break;
2287            case ExtendedColor.TYPE_GRAY:
2288                content.append(((GrayColor)color).getGray());
2289                break;
2290            case ExtendedColor.TYPE_CMYK: {
2291                CMYKColor cmyk = (CMYKColor)color;
2292                content.append(cmyk.getCyan()).append(' ').append(cmyk.getMagenta());
2293                content.append(' ').append(cmyk.getYellow()).append(' ').append(cmyk.getBlack());
2294                break;
2295            }
2296            case ExtendedColor.TYPE_SEPARATION:
2297                content.append(tint);
2298                break;
2299            default:
2300                throw new RuntimeException JavaDoc("Invalid color type.");
2301        }
2302    }
2303    
2304    /** Sets the fill color to an uncolored pattern.
2305     * @param p the pattern
2306     * @param color the color of the pattern
2307     */

2308    public void setPatternFill(PdfPatternPainter p, Color JavaDoc color) {
2309        if (ExtendedColor.getType(color) == ExtendedColor.TYPE_SEPARATION)
2310            setPatternFill(p, color, ((SpotColor)color).getTint());
2311        else
2312            setPatternFill(p, color, 0);
2313    }
2314    
2315    /** Sets the fill color to an uncolored pattern.
2316     * @param p the pattern
2317     * @param color the color of the pattern
2318     * @param tint the tint if the color is a spot color, ignored otherwise
2319     */

2320    public void setPatternFill(PdfPatternPainter p, Color JavaDoc color, float tint) {
2321        checkWriter();
2322        if (!p.isStencil())
2323            throw new RuntimeException JavaDoc("An uncolored pattern was expected.");
2324        PageResources prs = getPageResources();
2325        PdfName name = writer.addSimplePattern(p);
2326        name = prs.addPattern(name, p.getIndirectReference());
2327        ColorDetails csDetail = writer.addSimplePatternColorspace(color);
2328        PdfName cName = prs.addColor(csDetail.getColorName(), csDetail.getIndirectReference());
2329        content.append(cName.getBytes()).append(" cs").append_i(separator);
2330        outputColorNumbers(color, tint);
2331        content.append(' ').append(name.getBytes()).append(" scn").append_i(separator);
2332    }
2333    
2334    /** Sets the stroke color to an uncolored pattern.
2335     * @param p the pattern
2336     * @param color the color of the pattern
2337     */

2338    public void setPatternStroke(PdfPatternPainter p, Color JavaDoc color) {
2339        if (ExtendedColor.getType(color) == ExtendedColor.TYPE_SEPARATION)
2340            setPatternStroke(p, color, ((SpotColor)color).getTint());
2341        else
2342            setPatternStroke(p, color, 0);
2343    }
2344    
2345    /** Sets the stroke color to an uncolored pattern.
2346     * @param p the pattern
2347     * @param color the color of the pattern
2348     * @param tint the tint if the color is a spot color, ignored otherwise
2349     */

2350    public void setPatternStroke(PdfPatternPainter p, Color JavaDoc color, float tint) {
2351        checkWriter();
2352        if (!p.isStencil())
2353            throw new RuntimeException JavaDoc("An uncolored pattern was expected.");
2354        PageResources prs = getPageResources();
2355        PdfName name = writer.addSimplePattern(p);
2356        name = prs.addPattern(name, p.getIndirectReference());
2357        ColorDetails csDetail = writer.addSimplePatternColorspace(color);
2358        PdfName cName = prs.addColor(csDetail.getColorName(), csDetail.getIndirectReference());
2359        content.append(cName.getBytes()).append(" CS").append_i(separator);
2360        outputColorNumbers(color, tint);
2361        content.append(' ').append(name.getBytes()).append(" SCN").append_i(separator);
2362    }
2363    
2364    /** Sets the stroke color to a pattern. The pattern can be
2365     * colored or uncolored.
2366     * @param p the pattern
2367     */

2368    public void setPatternStroke(PdfPatternPainter p) {
2369        if (p.isStencil()) {
2370            setPatternStroke(p, p.getDefaultColor());
2371            return;
2372        }
2373        checkWriter();
2374        PageResources prs = getPageResources();
2375        PdfName name = writer.addSimplePattern(p);
2376        name = prs.addPattern(name, p.getIndirectReference());
2377        content.append(PdfName.PATTERN.getBytes()).append(" CS ").append(name.getBytes()).append(" SCN").append_i(separator);
2378    }
2379    
2380    /**
2381     * Paints using a shading object.
2382     * @param shading the shading object
2383     */

2384    public void paintShading(PdfShading shading) {
2385        writer.addSimpleShading(shading);
2386        PageResources prs = getPageResources();
2387        PdfName name = prs.addShading(shading.getShadingName(), shading.getShadingReference());
2388        content.append(name.getBytes()).append(" sh").append_i(separator);
2389        ColorDetails details = shading.getColorDetails();
2390        if (details != null)
2391            prs.addColor(details.getColorName(), details.getIndirectReference());
2392    }
2393    
2394    /**
2395     * Paints using a shading pattern.
2396     * @param shading the shading pattern
2397     */

2398    public void paintShading(PdfShadingPattern shading) {
2399        paintShading(shading.getShading());
2400    }
2401    
2402    /**
2403     * Sets the shading fill pattern.
2404     * @param shading the shading pattern
2405     */

2406    public void setShadingFill(PdfShadingPattern shading) {
2407        writer.addSimpleShadingPattern(shading);
2408        PageResources prs = getPageResources();
2409        PdfName name = prs.addPattern(shading.getPatternName(), shading.getPatternReference());
2410        content.append(PdfName.PATTERN.getBytes()).append(" cs ").append(name.getBytes()).append(" scn").append_i(separator);
2411        ColorDetails details = shading.getColorDetails();
2412        if (details != null)
2413            prs.addColor(details.getColorName(), details.getIndirectReference());
2414    }
2415    
2416    /**
2417     * Sets the shading stroke pattern
2418     * @param shading the shading pattern
2419     */

2420    public void setShadingStroke(PdfShadingPattern shading) {
2421        writer.addSimpleShadingPattern(shading);
2422        PageResources prs = getPageResources();
2423        PdfName name = prs.addPattern(shading.getPatternName(), shading.getPatternReference());
2424        content.append(PdfName.PATTERN.getBytes()).append(" CS ").append(name.getBytes()).append(" SCN").append_i(separator);
2425        ColorDetails details = shading.getColorDetails();
2426        if (details != null)
2427            prs.addColor(details.getColorName(), details.getIndirectReference());
2428    }
2429    
2430    /** Check if we have a valid PdfWriter.
2431     *
2432     */

2433    protected void checkWriter() {
2434        if (writer == null)
2435            throw new NullPointerException JavaDoc("The writer in PdfContentByte is null.");
2436    }
2437    
2438    /**
2439     * Show an array of text.
2440     * @param text array of text
2441     */

2442    public void showText(PdfTextArray text) {
2443        if (state.fontDetails == null)
2444            throw new NullPointerException JavaDoc("Font and size must be set before writing any text");
2445        content.append("[");
2446        ArrayList JavaDoc arrayList = text.getArrayList();
2447        boolean lastWasNumber = false;
2448        for (int k = 0; k < arrayList.size(); ++k) {
2449            Object JavaDoc obj = arrayList.get(k);
2450            if (obj instanceof String JavaDoc) {
2451                showText2((String JavaDoc)obj);
2452                lastWasNumber = false;
2453            }
2454            else {
2455                if (lastWasNumber)
2456                    content.append(' ');
2457                else
2458                    lastWasNumber = true;
2459                content.append(((Float JavaDoc)obj).floatValue());
2460            }
2461        }
2462        content.append("]TJ").append_i(separator);
2463    }
2464    
2465    /**
2466     * Gets the <CODE>PdfWriter</CODE> in use by this object.
2467     * @return the <CODE>PdfWriter</CODE> in use by this object
2468     */

2469    public PdfWriter getPdfWriter() {
2470        return writer;
2471    }
2472    
2473    /**
2474     * Gets the <CODE>PdfDocument</CODE> in use by this object.
2475     * @return the <CODE>PdfDocument</CODE> in use by this object
2476     */

2477    public PdfDocument getPdfDocument() {
2478        return pdf;
2479    }
2480    
2481    /**
2482     * Implements a link to other part of the document. The jump will
2483     * be made to a local destination with the same name, that must exist.
2484     * @param name the name for this link
2485     * @param llx the lower left x corner of the activation area
2486     * @param lly the lower left y corner of the activation area
2487     * @param urx the upper right x corner of the activation area
2488     * @param ury the upper right y corner of the activation area
2489     */

2490    public void localGoto(String JavaDoc name, float llx, float lly, float urx, float ury) {
2491        pdf.localGoto(name, llx, lly, urx, ury);
2492    }
2493    
2494    /**
2495     * The local destination to where a local goto with the same
2496     * name will jump.
2497     * @param name the name of this local destination
2498     * @param destination the <CODE>PdfDestination</CODE> with the jump coordinates
2499     * @return <CODE>true</CODE> if the local destination was added,
2500     * <CODE>false</CODE> if a local destination with the same name
2501     * already exists
2502     */

2503    public boolean localDestination(String JavaDoc name, PdfDestination destination) {
2504        return pdf.localDestination(name, destination);
2505    }
2506    
2507    /**
2508     * Gets a duplicate of this <CODE>PdfContentByte</CODE>. All
2509     * the members are copied by reference but the buffer stays different.
2510     *
2511     * @return a copy of this <CODE>PdfContentByte</CODE>
2512     */

2513    public PdfContentByte getDuplicate() {
2514        return new PdfContentByte(writer);
2515    }
2516    
2517    /**
2518     * Implements a link to another document.
2519     * @param filename the filename for the remote document
2520     * @param name the name to jump to
2521     * @param llx the lower left x corner of the activation area
2522     * @param lly the lower left y corner of the activation area
2523     * @param urx the upper right x corner of the activation area
2524     * @param ury the upper right y corner of the activation area
2525     */

2526    public void remoteGoto(String JavaDoc filename, String JavaDoc name, float llx, float lly, float urx, float ury) {
2527        pdf.remoteGoto(filename, name, llx, lly, urx, ury);
2528    }
2529    
2530    /**
2531     * Implements a link to another document.
2532     * @param filename the filename for the remote document
2533     * @param page the page to jump to
2534     * @param llx the lower left x corner of the activation area
2535     * @param lly the lower left y corner of the activation area
2536     * @param urx the upper right x corner of the activation area
2537     * @param ury the upper right y corner of the activation area
2538     */

2539    public void remoteGoto(String JavaDoc filename, int page, float llx, float lly, float urx, float ury) {
2540        pdf.remoteGoto(filename, page, llx, lly, urx, ury);
2541    }
2542    /**
2543     * Adds a round rectangle to the current path.
2544     *
2545     * @param x x-coordinate of the starting point
2546     * @param y y-coordinate of the starting point
2547     * @param w width
2548     * @param h height
2549     * @param r radius of the arc corner
2550     */

2551    public void roundRectangle(float x, float y, float w, float h, float r) {
2552        if (w < 0) {
2553            x += w;
2554            w = -w;
2555        }
2556        if (h < 0) {
2557            y += h;
2558            h = -h;
2559        }
2560        if (r < 0)
2561            r = -r;
2562        float b = 0.4477f;
2563        moveTo(x + r, y);
2564        lineTo(x + w - r, y);
2565        curveTo(x + w - r * b, y, x + w, y + r * b, x + w, y + r);
2566        lineTo(x + w, y + h - r);
2567        curveTo(x + w, y + h - r * b, x + w - r * b, y + h, x + w - r, y + h);
2568        lineTo(x + r, y + h);
2569        curveTo(x + r * b, y + h, x, y + h - r * b, x, y + h - r);
2570        lineTo(x, y + r);
2571        curveTo(x, y + r * b, x + r * b, y, x + r, y);
2572    }
2573    
2574    /** Implements an action in an area.
2575     * @param action the <CODE>PdfAction</CODE>
2576     * @param llx the lower left x corner of the activation area
2577     * @param lly the lower left y corner of the activation area
2578     * @param urx the upper right x corner of the activation area
2579     * @param ury the upper right y corner of the activation area
2580     */

2581    public void setAction(PdfAction action, float llx, float lly, float urx, float ury) {
2582        pdf.setAction(action, llx, lly, urx, ury);
2583    }
2584    
2585    /** Outputs a <CODE>String</CODE> directly to the content.
2586     * @param s the <CODE>String</CODE>
2587     */

2588    public void setLiteral(String JavaDoc s) {
2589        content.append(s);
2590    }
2591    
2592    /** Outputs a <CODE>char</CODE> directly to the content.
2593     * @param c the <CODE>char</CODE>
2594     */

2595    public void setLiteral(char c) {
2596        content.append(c);
2597    }
2598    
2599    /** Outputs a <CODE>float</CODE> directly to the content.
2600     * @param n the <CODE>float</CODE>
2601     */

2602    public void setLiteral(float n) {
2603        content.append(n);
2604    }
2605    
2606    /** Throws an error if it is a pattern.
2607     * @param t the object to check
2608     */

2609    void checkNoPattern(PdfTemplate t) {
2610        if (t.getType() == PdfTemplate.TYPE_PATTERN)
2611            throw new RuntimeException JavaDoc("Invalid use of a pattern. A template was expected.");
2612    }
2613    
2614    /**
2615     * Draws a TextField.
2616     * @param llx
2617     * @param lly
2618     * @param urx
2619     * @param ury
2620     * @param on
2621     */

2622    public void drawRadioField(float llx, float lly, float urx, float ury, boolean on) {
2623        if (llx > urx) { float x = llx; llx = urx; urx = x; }
2624        if (lly > ury) { float y = lly; lly = ury; ury = y; }
2625        // silver circle
2626
setLineWidth(1);
2627        setLineCap(1);
2628        setColorStroke(new Color JavaDoc(0xC0, 0xC0, 0xC0));
2629        arc(llx + 1f, lly + 1f, urx - 1f, ury - 1f, 0f, 360f);
2630        stroke();
2631        // gray circle-segment
2632
setLineWidth(1);
2633        setLineCap(1);
2634        setColorStroke(new Color JavaDoc(0xA0, 0xA0, 0xA0));
2635        arc(llx + 0.5f, lly + 0.5f, urx - 0.5f, ury - 0.5f, 45, 180);
2636        stroke();
2637        // black circle-segment
2638
setLineWidth(1);
2639        setLineCap(1);
2640        setColorStroke(new Color JavaDoc(0x00, 0x00, 0x00));
2641        arc(llx + 1.5f, lly + 1.5f, urx - 1.5f, ury - 1.5f, 45, 180);
2642        stroke();
2643        if (on) {
2644            // gray circle
2645
setLineWidth(1);
2646            setLineCap(1);
2647            setColorFill(new Color JavaDoc(0x00, 0x00, 0x00));
2648            arc(llx + 4f, lly + 4f, urx - 4f, ury - 4f, 0, 360);
2649            fill();
2650        }
2651    }
2652    
2653    /**
2654     * Draws a TextField.
2655     * @param llx
2656     * @param lly
2657     * @param urx
2658     * @param ury
2659     */

2660    public void drawTextField(float llx, float lly, float urx, float ury) {
2661        if (llx > urx) { float x = llx; llx = urx; urx = x; }
2662        if (lly > ury) { float y = lly; lly = ury; ury = y; }
2663        // silver rectangle not filled
2664
setColorStroke(new Color JavaDoc(0xC0, 0xC0, 0xC0));
2665        setLineWidth(1);
2666        setLineCap(0);
2667        rectangle(llx, lly, urx - llx, ury - lly);
2668        stroke();
2669        // white rectangle filled
2670
setLineWidth(1);
2671        setLineCap(0);
2672        setColorFill(new Color JavaDoc(0xFF, 0xFF, 0xFF));
2673        rectangle(llx + 0.5f, lly + 0.5f, urx - llx - 1f, ury -lly - 1f);
2674        fill();
2675        // silver lines
2676
setColorStroke(new Color JavaDoc(0xC0, 0xC0, 0xC0));
2677        setLineWidth(1);
2678        setLineCap(0);
2679        moveTo(llx + 1f, lly + 1.5f);
2680        lineTo(urx - 1.5f, lly + 1.5f);
2681        lineTo(urx - 1.5f, ury - 1f);
2682        stroke();
2683        // gray lines
2684
setColorStroke(new Color JavaDoc(0xA0, 0xA0, 0xA0));
2685        setLineWidth(1);
2686        setLineCap(0);
2687        moveTo(llx + 1f, lly + 1);
2688        lineTo(llx + 1f, ury - 1f);
2689        lineTo(urx - 1f, ury - 1f);
2690        stroke();
2691        // black lines
2692
setColorStroke(new Color JavaDoc(0x00, 0x00, 0x00));
2693        setLineWidth(1);
2694        setLineCap(0);
2695        moveTo(llx + 2f, lly + 2f);
2696        lineTo(llx + 2f, ury - 2f);
2697        lineTo(urx - 2f, ury - 2f);
2698        stroke();
2699    }
2700    
2701    /**
2702     * Draws a button.
2703     * @param llx
2704     * @param lly
2705     * @param urx
2706     * @param ury
2707     * @param text
2708     * @param bf
2709     * @param size
2710     */

2711    public void drawButton(float llx, float lly, float urx, float ury, String JavaDoc text, BaseFont bf, float size) {
2712        if (llx > urx) { float x = llx; llx = urx; urx = x; }
2713        if (lly > ury) { float y = lly; lly = ury; ury = y; }
2714        // black rectangle not filled
2715
setColorStroke(new Color JavaDoc(0x00, 0x00, 0x00));
2716        setLineWidth(1);
2717        setLineCap(0);
2718        rectangle(llx, lly, urx - llx, ury - lly);
2719        stroke();
2720        // silver rectangle filled
2721
setLineWidth(1);
2722        setLineCap(0);
2723        setColorFill(new Color JavaDoc(0xC0, 0xC0, 0xC0));
2724        rectangle(llx + 0.5f, lly + 0.5f, urx - llx - 1f, ury -lly - 1f);
2725        fill();
2726        // white lines
2727
setColorStroke(new Color JavaDoc(0xFF, 0xFF, 0xFF));
2728        setLineWidth(1);
2729        setLineCap(0);
2730        moveTo(llx + 1f, lly + 1f);
2731        lineTo(llx + 1f, ury - 1f);
2732        lineTo(urx - 1f, ury - 1f);
2733        stroke();
2734        // dark grey lines
2735
setColorStroke(new Color JavaDoc(0xA0, 0xA0, 0xA0));
2736        setLineWidth(1);
2737        setLineCap(0);
2738        moveTo(llx + 1f, lly + 1f);
2739        lineTo(urx - 1f, lly + 1f);
2740        lineTo(urx - 1f, ury - 1f);
2741        stroke();
2742        // text
2743
resetRGBColorFill();
2744        beginText();
2745        setFontAndSize(bf, size);
2746        showTextAligned(PdfContentByte.ALIGN_CENTER, text, llx + (urx - llx) / 2, lly + (ury - lly - size) / 2, 0);
2747        endText();
2748    }
2749    
2750    /** Gets a <CODE>Graphics2D</CODE> to write on. The graphics
2751     * are translated to PDF commands as shapes. No PDF fonts will appear.
2752     * @param width the width of the panel
2753     * @param height the height of the panel
2754     * @return a <CODE>Graphics2D</CODE>
2755     */

2756    public java.awt.Graphics2D JavaDoc createGraphicsShapes(float width, float height) {
2757        return new PdfGraphics2D(this, width, height, null, true, false, 0);
2758    }
2759    
2760    /** Gets a <CODE>Graphics2D</CODE> to print on. The graphics
2761     * are translated to PDF commands as shapes. No PDF fonts will appear.
2762     * @param width the width of the panel
2763     * @param height the height of the panel
2764     * @param printerJob a printer job
2765     * @return a <CODE>Graphics2D</CODE>
2766     */

2767    public java.awt.Graphics2D JavaDoc createPrinterGraphicsShapes(float width, float height, PrinterJob JavaDoc printerJob) {
2768        return new PdfPrinterGraphics2D(this, width, height, null, true, false, 0, printerJob);
2769    }
2770
2771    /** Gets a <CODE>Graphics2D</CODE> to write on. The graphics
2772     * are translated to PDF commands.
2773     * @param width the width of the panel
2774     * @param height the height of the panel
2775     * @return a <CODE>Graphics2D</CODE>
2776     */

2777    public java.awt.Graphics2D JavaDoc createGraphics(float width, float height) {
2778        return new PdfGraphics2D(this, width, height, null, false, false, 0);
2779    }
2780    
2781    /** Gets a <CODE>Graphics2D</CODE> to print on. The graphics
2782     * are translated to PDF commands.
2783     * @param width the width of the panel
2784     * @param height the height of the panel
2785     * @param printerJob
2786     * @return a <CODE>Graphics2D</CODE>
2787     */

2788    public java.awt.Graphics2D JavaDoc createPrinterGraphics(float width, float height, PrinterJob JavaDoc printerJob) {
2789        return new PdfPrinterGraphics2D(this, width, height, null, false, false, 0, printerJob);
2790    }
2791
2792    /** Gets a <CODE>Graphics2D</CODE> to write on. The graphics
2793     * are translated to PDF commands.
2794     * @param width the width of the panel
2795     * @param height the height of the panel
2796     * @param convertImagesToJPEG
2797     * @param quality
2798     * @return a <CODE>Graphics2D</CODE>
2799     */

2800    public java.awt.Graphics2D JavaDoc createGraphics(float width, float height, boolean convertImagesToJPEG, float quality) {
2801        return new PdfGraphics2D(this, width, height, null, false, convertImagesToJPEG, quality);
2802    }
2803
2804    /** Gets a <CODE>Graphics2D</CODE> to print on. The graphics
2805     * are translated to PDF commands.
2806     * @param width the width of the panel
2807     * @param height the height of the panel
2808     * @param convertImagesToJPEG
2809     * @param quality
2810     * @param printerJob
2811     * @return a <CODE>Graphics2D</CODE>
2812     */

2813    public java.awt.Graphics2D JavaDoc createPrinterGraphics(float width, float height, boolean convertImagesToJPEG, float quality, PrinterJob JavaDoc printerJob) {
2814        return new PdfPrinterGraphics2D(this, width, height, null, false, convertImagesToJPEG, quality, printerJob);
2815    }
2816
2817    /** Gets a <CODE>Graphics2D</CODE> to print on. The graphics
2818     * are translated to PDF commands.
2819     * @param width
2820     * @param height
2821     * @param convertImagesToJPEG
2822     * @param quality
2823     * @return A Graphics2D object
2824     */

2825    public java.awt.Graphics2D JavaDoc createGraphicsShapes(float width, float height, boolean convertImagesToJPEG, float quality) {
2826        return new PdfGraphics2D(this, width, height, null, true, convertImagesToJPEG, quality);
2827    }
2828
2829    /** Gets a <CODE>Graphics2D</CODE> to print on. The graphics
2830     * are translated to PDF commands.
2831     * @param width
2832     * @param height
2833     * @param convertImagesToJPEG
2834     * @param quality
2835     * @param printerJob
2836     * @return a Graphics2D object
2837     */

2838    public java.awt.Graphics2D JavaDoc createPrinterGraphicsShapes(float width, float height, boolean convertImagesToJPEG, float quality, PrinterJob JavaDoc printerJob) {
2839        return new PdfPrinterGraphics2D(this, width, height, null, true, convertImagesToJPEG, quality, printerJob);
2840    }
2841    
2842    /** Gets a <CODE>Graphics2D</CODE> to write on. The graphics
2843     * are translated to PDF commands.
2844     * @param width the width of the panel
2845     * @param height the height of the panel
2846     * @param fontMapper the mapping from awt fonts to <CODE>BaseFont</CODE>
2847     * @return a <CODE>Graphics2D</CODE>
2848     */

2849    public java.awt.Graphics2D JavaDoc createGraphics(float width, float height, FontMapper fontMapper) {
2850        return new PdfGraphics2D(this, width, height, fontMapper, false, false, 0);
2851    }
2852    
2853    /** Gets a <CODE>Graphics2D</CODE> to print on. The graphics
2854     * are translated to PDF commands.
2855     * @param width the width of the panel
2856     * @param height the height of the panel
2857     * @param fontMapper the mapping from awt fonts to <CODE>BaseFont</CODE>
2858     * @param printerJob a printer job
2859     * @return a <CODE>Graphics2D</CODE>
2860     */

2861    public java.awt.Graphics2D JavaDoc createPrinterGraphics(float width, float height, FontMapper fontMapper, PrinterJob JavaDoc printerJob) {
2862        return new PdfPrinterGraphics2D(this, width, height, fontMapper, false, false, 0, printerJob);
2863    }
2864    
2865    /** Gets a <CODE>Graphics2D</CODE> to write on. The graphics
2866     * are translated to PDF commands.
2867     * @param width the width of the panel
2868     * @param height the height of the panel
2869     * @param fontMapper the mapping from awt fonts to <CODE>BaseFont</CODE>
2870     * @param convertImagesToJPEG converts awt images to jpeg before inserting in pdf
2871     * @param quality the quality of the jpeg
2872     * @return a <CODE>Graphics2D</CODE>
2873     */

2874    public java.awt.Graphics2D JavaDoc createGraphics(float width, float height, FontMapper fontMapper, boolean convertImagesToJPEG, float quality) {
2875        return new PdfGraphics2D(this, width, height, fontMapper, false, convertImagesToJPEG, quality);
2876    }
2877
2878    /** Gets a <CODE>Graphics2D</CODE> to print on. The graphics
2879     * are translated to PDF commands.
2880     * @param width the width of the panel
2881     * @param height the height of the panel
2882     * @param fontMapper the mapping from awt fonts to <CODE>BaseFont</CODE>
2883     * @param convertImagesToJPEG converts awt images to jpeg before inserting in pdf
2884     * @param quality the quality of the jpeg
2885     * @param printerJob a printer job
2886     * @return a <CODE>Graphics2D</CODE>
2887     */

2888    public java.awt.Graphics2D JavaDoc createPrinterGraphics(float width, float height, FontMapper fontMapper, boolean convertImagesToJPEG, float quality, PrinterJob JavaDoc printerJob) {
2889        return new PdfPrinterGraphics2D(this, width, height, fontMapper, false, convertImagesToJPEG, quality, printerJob);
2890    }
2891
2892    PageResources getPageResources() {
2893        return pdf.getPageResources();
2894    }
2895    
2896    /** Sets the graphic state
2897     * @param gstate the graphic state
2898     */

2899    public void setGState(PdfGState gstate) {
2900        PdfObject obj[] = writer.addSimpleExtGState(gstate);
2901        PageResources prs = getPageResources();
2902        PdfName name = prs.addExtGState((PdfName)obj[0], (PdfIndirectReference)obj[1]);
2903        content.append(name.getBytes()).append(" gs").append_i(separator);
2904    }
2905    
2906    /**
2907     * Begins a graphic block whose visibility is controled by the <CODE>layer</CODE>.
2908     * Blocks can be nested. Each block must be terminated by an {@link #endLayer()}.<p>
2909     * Note that nested layers with {@link PdfLayer#addChild(PdfLayer)} only require a single
2910     * call to this method and a single call to {@link #endLayer()}; all the nesting control
2911     * is built in.
2912     * @param layer the layer
2913     */

2914    public void beginLayer(PdfOCG layer) {
2915        if ((layer instanceof PdfLayer) && ((PdfLayer)layer).getTitle() != null)
2916            throw new IllegalArgumentException JavaDoc("A title is not a layer");
2917        if (layerDepth == null)
2918            layerDepth = new ArrayList JavaDoc();
2919        if (layer instanceof PdfLayerMembership) {
2920            layerDepth.add(new Integer JavaDoc(1));
2921            beginLayer2(layer);
2922            return;
2923        }
2924        int n = 0;
2925        PdfLayer la = (PdfLayer)layer;
2926        while (la != null) {
2927            if (la.getTitle() == null) {
2928                beginLayer2(la);
2929                ++n;
2930            }
2931            la = la.getParent();
2932        }
2933        layerDepth.add(new Integer JavaDoc(n));
2934    }
2935    
2936    private void beginLayer2(PdfOCG layer) {
2937        PdfName name = (PdfName)writer.addSimpleProperty(layer, layer.getRef())[0];
2938        PageResources prs = getPageResources();
2939        name = prs.addProperty(name, layer.getRef());
2940        content.append("/OC ").append(name.getBytes()).append(" BDC").append_i(separator);
2941    }
2942    
2943    /**
2944     * Ends a layer controled graphic block. It will end the most recent open block.
2945     */

2946    public void endLayer() {
2947        int n = 1;
2948        if (layerDepth != null && !layerDepth.isEmpty()) {
2949            n = ((Integer JavaDoc)layerDepth.get(layerDepth.size() - 1)).intValue();
2950            layerDepth.remove(layerDepth.size() - 1);
2951        }
2952        while (n-- > 0)
2953            content.append("EMC").append_i(separator);
2954    }
2955    
2956    /** Concatenates a transformation to the current transformation
2957     * matrix.
2958     * @param af the transformation
2959     */

2960    public void transform(AffineTransform JavaDoc af) {
2961        double arr[] = new double[6];
2962        af.getMatrix(arr);
2963        content.append(arr[0]).append(' ').append(arr[1]).append(' ').append(arr[2]).append(' ');
2964        content.append(arr[3]).append(' ').append(arr[4]).append(' ').append(arr[5]).append(" cm").append_i(separator);
2965    }
2966    
2967    void addAnnotation(PdfAnnotation annot) {
2968        writer.addAnnotation(annot);
2969    }
2970    
2971    /**
2972     * Sets the default colorspace.
2973     * @param name the name of the colorspace. It can be <CODE>PdfName.DEFAULTGRAY</CODE>, <CODE>PdfName.DEFAULTRGB</CODE>
2974     * or <CODE>PdfName.DEFAULTCMYK</CODE>
2975     * @param obj the colorspace. A <CODE>null</CODE> or <CODE>PdfNull</CODE> removes any colorspace with the same name
2976     */

2977    public void setDefaultColorspace(PdfName name, PdfObject obj) {
2978        PageResources prs = getPageResources();
2979        prs.addDefaultColor(name, obj);
2980    }
2981    
2982    /**
2983     * Begins a marked content sequence. This sequence will be tagged with the structure <CODE>struc</CODE>.
2984     * The same structure can be used several times to connect text that belongs to the same logical segment
2985     * but is in a different location, like the same paragraph crossing to another page, for example.
2986     * @param struc the tagging structure
2987     */

2988    public void beginMarkedContentSequence(PdfStructureElement struc) {
2989        PdfObject obj = struc.get(PdfName.K);
2990        int mark = pdf.getMarkPoint();
2991        if (obj != null) {
2992            PdfArray ar = null;
2993            if (obj.isNumber()) {
2994                ar = new PdfArray();
2995                ar.add(obj);
2996                struc.put(PdfName.K, ar);
2997            }
2998            else if (obj.isArray()) {
2999                ar = (PdfArray)obj;
3000                if (!((PdfObject)ar.getArrayList().get(0)).isNumber())
3001                    throw new IllegalArgumentException JavaDoc("The structure has kids.");
3002            }
3003            else
3004                throw new IllegalArgumentException JavaDoc("Unknown object at /K " + obj.getClass().toString());
3005            PdfDictionary dic = new PdfDictionary(PdfName.MCR);
3006            dic.put(PdfName.PG, writer.getCurrentPage());
3007            dic.put(PdfName.MCID, new PdfNumber(mark));
3008            ar.add(dic);
3009            struc.setPageMark(writer.getPageNumber() - 1, -1);
3010        }
3011        else {
3012            struc.setPageMark(writer.getPageNumber() - 1, mark);
3013            struc.put(PdfName.PG, writer.getCurrentPage());
3014        }
3015        pdf.incMarkPoint();
3016        content.append(struc.get(PdfName.S).getBytes()).append(" <</MCID ").append(mark).append(">> BDC").append_i(separator);
3017    }
3018    
3019    /**
3020     * Ends a marked content sequence
3021     */

3022    public void endMarkedContentSequence() {
3023        content.append("EMC").append_i(separator);
3024    }
3025    
3026    /**
3027     * Begins a marked content sequence. If property is <CODE>null</CODE> the mark will be of the type
3028     * <CODE>BMC</CODE> otherwise it will be <CODE>BDC</CODE>.
3029     * @param tag the tag
3030     * @param property the property
3031     * @param inline <CODE>true</CODE> to include the property in the content or <CODE>false</CODE>
3032     * to include the property in the resource dictionary with the possibility of reusing
3033     */

3034    public void beginMarkedContentSequence(PdfName tag, PdfDictionary property, boolean inline) {
3035        if (property == null) {
3036            content.append(tag.getBytes()).append(" BMC").append_i(separator);
3037            return;
3038        }
3039        content.append(tag.getBytes()).append(' ');
3040        if (inline)
3041            try {
3042                property.toPdf(writer, content);
3043            }
3044            catch (Exception JavaDoc e) {
3045                throw new ExceptionConverter(e);
3046            }
3047        else {
3048            PdfObject[] objs;
3049            if (writer.propertyExists(property))
3050                objs = writer.addSimpleProperty(property, null);
3051            else
3052                objs = writer.addSimpleProperty(property, writer.getPdfIndirectReference());
3053            PdfName name = (PdfName)objs[0];
3054            PageResources prs = getPageResources();
3055            name = prs.addProperty(name, (PdfIndirectReference)objs[1]);
3056            content.append(name.getBytes());
3057        }
3058        content.append(" BDC").append_i(separator);
3059    }
3060    
3061    /**
3062     * This is just a shorthand to <CODE>beginMarkedContentSequence(tag, null, false)</CODE>.
3063     * @param tag the tag
3064     */

3065    public void beginMarkedContentSequence(PdfName tag) {
3066        beginMarkedContentSequence(tag, null, false);
3067    }
3068}
3069
Popular Tags