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