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();
9