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