1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.pdfbox.pdmodel.edit; 18 19 import java.awt.Color; 20 import java.awt.color.ColorSpace; 21 import java.io.ByteArrayOutputStream; 22 import java.io.IOException; 23 import java.io.OutputStream; 24 25 import java.text.NumberFormat; 26 27 import java.util.ArrayList; 28 import java.util.List; 29 import java.util.Locale; 30 import java.util.Map; 31 import java.util.HashMap; 32 33 import org.apache.pdfbox.pdmodel.PDDocument; 34 import org.apache.pdfbox.pdmodel.PDPage; 35 import org.apache.pdfbox.pdmodel.PDResources; 36 37 import org.apache.pdfbox.pdmodel.common.COSStreamArray; 38 import org.apache.pdfbox.pdmodel.common.PDStream; 39 40 import org.apache.pdfbox.pdmodel.font.PDFont; 41 import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpace; 42 import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceCMYK; 43 import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceGray; 44 import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceN; 45 import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceRGB; 46 import org.apache.pdfbox.pdmodel.graphics.color.PDICCBased; 47 import org.apache.pdfbox.pdmodel.graphics.color.PDPattern; 48 import org.apache.pdfbox.pdmodel.graphics.color.PDSeparation; 49 import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObject; 50 import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObjectImage; 51 import org.apache.pdfbox.util.MapUtil; 52 53 import org.apache.pdfbox.cos.COSArray; 54 import org.apache.pdfbox.cos.COSDictionary; 55 import org.apache.pdfbox.cos.COSName; 56 import org.apache.pdfbox.cos.COSString; 57 58 59 /** 60 * This class will is a convenience for creating page content streams. You MUST 61 * call close() when you are finished with this object. 62 * 63 * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a> 64 * @version $Revision: 1.19 $ 65 */ 66 public class PDPageContentStream 67 { 68 private PDPage page; 69 private OutputStream output; 70 private boolean inTextMode = false; 71 private Map<PDFont,String> fontMappings = new HashMap<PDFont,String>(); 72 private Map<PDXObject,String> xobjectMappings = new HashMap<PDXObject,String>(); 73 private PDResources resources; 74 private Map fonts; 75 private Map xobjects; 76 77 private PDColorSpace currentStrokingColorSpace = new PDDeviceGray(); 78 private PDColorSpace currentNonStrokingColorSpace = new PDDeviceGray(); 79 80 //cached storage component for getting color values 81 private float[] colorComponents = new float[4]; 82 83 private NumberFormat formatDecimal = NumberFormat.getNumberInstance( Locale.US ); 84 85 private static final String BEGIN_TEXT = "BT\n"; 86 private static final String END_TEXT = "ET\n"; 87 private static final String SET_FONT = "Tf\n"; 88 private static final String MOVE_TEXT_POSITION = "Td\n"; 89 private static final String SET_TEXT_MATRIX = "Tm\n"; 90 private static final String SHOW_TEXT = "Tj\n"; 91 92 private static final String SAVE_GRAPHICS_STATE = "q\n"; 93 private static final String RESTORE_GRAPHICS_STATE = "Q\n"; 94 private static final String CONCATENATE_MATRIX = "cm\n"; 95 private static final String XOBJECT_DO = "Do\n"; 96 private static final String RG_STROKING = "RG\n"; 97 private static final String RG_NON_STROKING = "rg\n"; 98 private static final String K_STROKING = "K\n"; 99 private static final String K_NON_STROKING = "k\n"; 100 private static final String G_STROKING = "G\n"; 101 private static final String G_NON_STROKING = "g\n"; 102 private static final String APPEND_RECTANGLE = "re\n"; 103 private static final String FILL = "f\n"; 104 private static final String LINE_TO = "l\n"; 105 private static final String MOVE_TO = "m\n"; 106 private static final String STROKE = "S\n"; 107 private static final String LINE_WIDTH = "w\n"; 108 109 110 private static final String SET_STROKING_COLORSPACE = "CS\n"; 111 private static final String SET_NON_STROKING_COLORSPACE = "cs\n"; 112 113 private static final String SET_STROKING_COLOR_SIMPLE="SC\n"; 114 private static final String SET_STROKING_COLOR_COMPLEX="SCN\n"; 115 private static final String SET_NON_STROKING_COLOR_SIMPLE="sc\n"; 116 private static final String SET_NON_STROKING_COLOR_COMPLEX="scn\n"; 117 118 119 120 private static final int SPACE = 32; 121 122 123 /** 124 * Create a new PDPage content stream. 125 * 126 * @param document The document the page is part of. 127 * @param sourcePage The page to write the contents to. 128 * @throws IOException If there is an error writing to the page contents. 129 */ 130 public PDPageContentStream( PDDocument document, PDPage sourcePage ) throws IOException 131 { 132 this(document,sourcePage,false,true); 133 } 134 135 /** 136 * Create a new PDPage content stream. 137 * 138 * @param document The document the page is part of. 139 * @param sourcePage The page to write the contents to. 140 * @param appendContent Indicates whether content will be overwritten. If false all previous content is deleted. 141 * @param compress Tell if the content stream should compress the page contents. 142 * @throws IOException If there is an error writing to the page contents. 143 */ 144 public PDPageContentStream( PDDocument document, PDPage sourcePage, boolean appendContent, boolean compress ) 145 throws IOException 146 { 147 page = sourcePage; 148 resources = page.getResources(); 149 if( resources == null ) 150 { 151 resources = new PDResources(); 152 page.setResources( resources ); 153 } 154 fonts = resources.getFonts(); 155 xobjects = resources.getImages(); 156 // If request specifies the need to append to the document 157 if(appendContent) 158 { 159 // Get the pdstream from the source page instead of creating a new one 160 PDStream contents = sourcePage.getContents(); 161 162 // Create a pdstream to append new content 163 PDStream contentsToAppend = new PDStream( document ); 164 165 // This will be the resulting COSStreamArray after existing and new streams are merged 166 COSStreamArray compoundStream = null; 167 168 // If contents is already an array, a new stream is simply appended to it 169 if(contents.getStream() instanceof COSStreamArray) 170 { 171 compoundStream = (COSStreamArray)contents.getStream(); 172 compoundStream.appendStream( contentsToAppend.getStream()); 173 } 174 else 175 { 176 // Creates the COSStreamArray and adds the current stream plus a new one to it 177 COSArray newArray = new COSArray(); 178 newArray.add(contents.getCOSObject()); 179 newArray.add(contentsToAppend.getCOSObject()); 180 compoundStream = new COSStreamArray(newArray); 181 } 182 183 if( compress ) 184 { 185 List filters = new ArrayList(); 186 filters.add( COSName.FLATE_DECODE ); 187 contentsToAppend.setFilters( filters ); 188 } 189 190 // Sets the compoundStream as page contents 191 sourcePage.setContents( new PDStream(compoundStream) ); 192 output = contentsToAppend.createOutputStream(); 193 } 194 else 195 { 196 PDStream contents = new PDStream( document ); 197 if( compress ) 198 { 199 List filters = new ArrayList(); 200 filters.add( COSName.FLATE_DECODE ); 201 contents.setFilters( filters ); 202 } 203 sourcePage.setContents( contents ); 204 output = contents.createOutputStream(); 205 } 206 formatDecimal.setMaximumFractionDigits( 10 ); 207 formatDecimal.setGroupingUsed( false ); 208 } 209 210 /** 211 * Begin some text operations. 212 * 213 * @throws IOException If there is an error writing to the stream or if you attempt to 214 * nest beginText calls. 215 */ 216 public void beginText() throws IOException 217 { 218 if( inTextMode ) 219 { 220 throw new IOException( "Error: Nested beginText() calls are not allowed." ); 221 } 222 appendRawCommands( BEGIN_TEXT ); 223 inTextMode = true; 224 } 225 226 /** 227 * End some text operations. 228 * 229 * @throws IOException If there is an error writing to the stream or if you attempt to 230 * nest endText calls. 231 */ 232 public void endText() throws IOException 233 { 234 if( !inTextMode ) 235 { 236 throw new IOException( "Error: You must call beginText() before calling endText." ); 237 } 238 appendRawCommands( END_TEXT ); 239 inTextMode = false; 240 } 241 242 /** 243 * Set the font to draw text with. 244 * 245 * @param font The font to use. 246 * @param fontSize The font size to draw the text. 247 * @throws IOException If there is an error writing the font information. 248 */ 249 public void setFont( PDFont font, float fontSize ) throws IOException 250 { 251 String fontMapping = fontMappings.get( font ); 252 if( fontMapping == null ) 253 { 254 fontMapping = MapUtil.getNextUniqueKey( fonts, "F" ); 255 fontMappings.put( font, fontMapping ); 256 fonts.put( fontMapping, font ); 257 } 258 appendRawCommands( "/"); 259 appendRawCommands( fontMapping ); 260 appendRawCommands( SPACE ); 261 appendRawCommands( formatDecimal.format( fontSize ) ); 262 appendRawCommands( SPACE ); 263 appendRawCommands( SET_FONT ); 264 } 265 266 /** 267 * Draw an image at the x,y coordinates, with the default size of the image. 268 * 269 * @param image The image to draw. 270 * @param x The x-coordinate to draw the image. 271 * @param y The y-coordinate to draw the image. 272 * 273 * @throws IOException If there is an error writing to the stream. 274 */ 275 public void drawImage( PDXObjectImage image, float x, float y ) throws IOException 276 { 277 drawXObject( image, x, y, image.getWidth(), image.getHeight() ); 278 } 279 280 /** 281 * Draw an xobject(form or image) at the x,y coordinates and a certain width and height. 282 * 283 * @param xobject The xobject to draw. 284 * @param x The x-coordinate to draw the image. 285 * @param y The y-coordinate to draw the image. 286 * @param width The width of the image to draw. 287 * @param height The height of the image to draw. 288 * 289 * @throws IOException If there is an error writing to the stream. 290 */ 291 public void drawXObject( PDXObject xobject, float x, float y, float width, float height ) throws IOException 292 { 293 String xObjectPrefix = null; 294 if( xobject instanceof PDXObjectImage ) 295 { 296 xObjectPrefix = "Im"; 297 } 298 else 299 { 300 xObjectPrefix = "Form"; 301 } 302 303 String objMapping = xobjectMappings.get( xobject ); 304 if( objMapping == null ) 305 { 306 objMapping = MapUtil.getNextUniqueKey( xobjects, xObjectPrefix ); 307 xobjectMappings.put( xobject, objMapping ); 308 xobjects.put( objMapping, xobject ); 309 } 310 appendRawCommands( SAVE_GRAPHICS_STATE ); 311 appendRawCommands( formatDecimal.format( width ) ); 312 appendRawCommands( SPACE ); 313 appendRawCommands( formatDecimal.format( 0 ) ); 314 appendRawCommands( SPACE ); 315 appendRawCommands( formatDecimal.format( 0 ) ); 316 appendRawCommands( SPACE ); 317 appendRawCommands( formatDecimal.format( height ) ); 318 appendRawCommands( SPACE ); 319 appendRawCommands( formatDecimal.format( x ) ); 320 appendRawCommands( SPACE ); 321 appendRawCommands( formatDecimal.format( y ) ); 322 appendRawCommands( SPACE ); 323 appendRawCommands( CONCATENATE_MATRIX ); 324 appendRawCommands( SPACE ); 325 appendRawCommands( "/" ); 326 appendRawCommands( objMapping ); 327 appendRawCommands( SPACE ); 328 appendRawCommands( XOBJECT_DO ); 329 appendRawCommands( SPACE ); 330 appendRawCommands( RESTORE_GRAPHICS_STATE ); 331 } 332 333 /** 334 * The Td operator. 335 * A current text matrix will be replaced with a new one (1 0 0 1 x y). 336 * @param x The x coordinate. 337 * @param y The y coordinate. 338 * @throws IOException If there is an error writing to the stream. 339 */ 340 public void moveTextPositionByAmount( float x, float y ) throws IOException 341 { 342 if( !inTextMode ) 343 { 344 throw new IOException( "Error: must call beginText() before moveTextPositionByAmount"); 345 } 346 appendRawCommands( formatDecimal.format( x ) ); 347 appendRawCommands( SPACE ); 348 appendRawCommands( formatDecimal.format( y ) ); 349 appendRawCommands( SPACE ); 350 appendRawCommands( MOVE_TEXT_POSITION ); 351 } 352 353 /** 354 * The Tm operator. Sets the text matrix to the given values. 355 * A current text matrix will be replaced with the new one. 356 * @param a The a value of the matrix. 357 * @param b The b value of the matrix. 358 * @param c The c value of the matrix. 359 * @param d The d value of the matrix. 360 * @param e The e value of the matrix. 361 * @param f The f value of the matrix. 362 * @throws IOException If there is an error writing to the stream. 363 */ 364 public void setTextMatrix( double a, double b, double c, double d, double e, double f ) throws IOException 365 { 366 if( !inTextMode ) 367 { 368 throw new IOException( "Error: must call beginText() before setTextMatrix"); 369 } 370 appendRawCommands( formatDecimal.format( a ) ); 371 appendRawCommands( SPACE ); 372 appendRawCommands( formatDecimal.format( b ) ); 373 appendRawCommands( SPACE ); 374 appendRawCommands( formatDecimal.format( c ) ); 375 appendRawCommands( SPACE ); 376 appendRawCommands( formatDecimal.format( d ) ); 377 appendRawCommands( SPACE ); 378 appendRawCommands( formatDecimal.format( e ) ); 379 appendRawCommands( SPACE ); 380 appendRawCommands( formatDecimal.format( f ) ); 381 appendRawCommands( SPACE ); 382 appendRawCommands( SET_TEXT_MATRIX ); 383 } 384 385 /** 386 * The Tm operator. Sets the text matrix to the given scaling and translation values. 387 * A current text matrix will be replaced with the new one. 388 * @param sx The scaling factor in x-direction. 389 * @param sy The scaling factor in y-direction. 390 * @param tx The translation value in x-direction. 391 * @param ty The translation value in y-direction. 392 * @throws IOException If there is an error writing to the stream. 393 */ 394 public void setTextScaling( double sx, double sy, double tx, double ty ) throws IOException 395 { 396 setTextMatrix(sx, 0, 0, sy, tx, ty); 397 } 398 399 /** 400 * The Tm operator. Sets the text matrix to the given translation values. 401 * A current text matrix will be replaced with the new one. 402 * @param tx The translation value in x-direction. 403 * @param ty The translation value in y-direction. 404 * @throws IOException If there is an error writing to the stream. 405 */ 406 public void setTextTranslation( double tx, double ty ) throws IOException 407 { 408 setTextMatrix(1, 0, 0, 1, tx, ty); 409 } 410 411 /** 412 * The Tm operator. Sets the text matrix to the given rotation and translation values. 413 * A current text matrix will be replaced with the new one. 414 * @param angle The angle used for the counterclockwise rotation in radians. 415 * @param tx The translation value in x-direction. 416 * @param ty The translation value in y-direction. 417 * @throws IOException If there is an error writing to the stream. 418 */ 419 public void setTextRotation( double angle, double tx, double ty ) throws IOException 420 { 421 double angleCos = Math.cos(angle); 422 double angleSin = Math.sin(angle); 423 setTextMatrix( angleCos, angleSin, -angleSin, angleCos, tx, ty); 424 } 425 426 /** 427 * This will draw a string at the current location on the screen. 428 * 429 * @param text The text to draw. 430 * @throws IOException If an io exception occurs. 431 */ 432 public void drawString( String text ) throws IOException 433 { 434 if( !inTextMode ) 435 { 436 throw new IOException( "Error: must call beginText() before drawString"); 437 } 438 COSString string = new COSString( text ); 439 ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 440 string.writePDF( buffer ); 441 appendRawCommands( new String( buffer.toByteArray(), "ISO-8859-1")); 442 appendRawCommands( SPACE ); 443 appendRawCommands( SHOW_TEXT ); 444 } 445 446 /** 447 * Set the stroking color space. This will add the colorspace to the PDResources 448 * if necessary. 449 * 450 * @param colorSpace The colorspace to write. 451 * @throws IOException If there is an error writing the colorspace. 452 */ 453 public void setStrokingColorSpace( PDColorSpace colorSpace ) throws IOException 454 { 455 currentStrokingColorSpace = colorSpace; 456 writeColorSpace( colorSpace ); 457 appendRawCommands( SET_STROKING_COLORSPACE ); 458 } 459 460 /** 461 * Set the stroking color space. This will add the colorspace to the PDResources 462 * if necessary. 463 * 464 * @param colorSpace The colorspace to write. 465 * @throws IOException If there is an error writing the colorspace. 466 */ 467 public void setNonStrokingColorSpace( PDColorSpace colorSpace ) throws IOException 468 { 469 currentNonStrokingColorSpace = colorSpace; 470 writeColorSpace( colorSpace ); 471 appendRawCommands( SET_NON_STROKING_COLORSPACE ); 472 } 473 474 private void writeColorSpace( PDColorSpace colorSpace ) throws IOException 475 { 476 COSName key = null; 477 if( colorSpace instanceof PDDeviceGray || 478 colorSpace instanceof PDDeviceRGB || 479 colorSpace instanceof PDDeviceCMYK ) 480 { 481 key = COSName.getPDFName( colorSpace.getName() ); 482 } 483 else 484 { 485 COSDictionary colorSpaces = 486 (COSDictionary)resources.getCOSDictionary().getDictionaryObject(COSName.COLORSPACE); 487 if( colorSpaces == null ) 488 { 489 colorSpaces = new COSDictionary(); 490 resources.getCOSDictionary().setItem( COSName.COLORSPACE, colorSpaces ); 491 } 492 key = colorSpaces.getKeyForValue( colorSpace.getCOSObject() ); 493 494 if( key == null ) 495 { 496 int counter = 0; 497 String csName = "CS"; 498 while( colorSpaces.containsValue( csName + counter ) ) 499 { 500 counter++; 501 } 502 key = COSName.getPDFName( csName + counter ); 503 colorSpaces.setItem( key, colorSpace ); 504 } 505 } 506 key.writePDF( output ); 507 appendRawCommands( SPACE ); 508 } 509 510 /** 511 * Set the color components of current stroking colorspace. 512 * 513 * @param components The components to set for the current color. 514 * @throws IOException If there is an error while writing to the stream. 515 */ 516 public void setStrokingColor( float[] components ) throws IOException 517 { 518 for( int i=0; i< components.length; i++ ) 519 { 520 appendRawCommands( formatDecimal.format( components[i] ) ); 521 appendRawCommands( SPACE ); 522 } 523 if( currentStrokingColorSpace instanceof PDSeparation || 524 currentStrokingColorSpace instanceof PDPattern || 525 currentStrokingColorSpace instanceof PDDeviceN || 526 currentStrokingColorSpace instanceof PDICCBased ) 527 { 528 appendRawCommands( SET_STROKING_COLOR_COMPLEX ); 529 } 530 else 531 { 532 appendRawCommands( SET_STROKING_COLOR_SIMPLE ); 533 } 534 } 535 536 /** 537 * Set the stroking color, specified as RGB. 538 * 539 * @param color The color to set. 540 * @throws IOException If an IO error occurs while writing to the stream. 541 */ 542 public void setStrokingColor( Color color ) throws IOException 543 { 544 ColorSpace colorSpace = color.getColorSpace(); 545 if( colorSpace.getType() == ColorSpace.TYPE_RGB ) 546 { 547 setStrokingColor( color.getRed(), color.getGreen(), color.getBlue() ); 548 } 549 else if( colorSpace.getType() == ColorSpace.TYPE_GRAY ) 550 { 551 color.getColorComponents( colorComponents ); 552 setStrokingColor( colorComponents[0] ); 553 } 554 else if( colorSpace.getType() == ColorSpace.TYPE_CMYK ) 555 { 556 color.getColorComponents( colorComponents ); 557 setStrokingColor( colorComponents[0], colorComponents[2], colorComponents[2], colorComponents[3] ); 558 } 559 else 560 { 561 throw new IOException( "Error: unknown colorspace:" + colorSpace ); 562 } 563 } 564 565 /** 566 * Set the non stroking color, specified as RGB. 567 * 568 * @param color The color to set. 569 * @throws IOException If an IO error occurs while writing to the stream. 570 */ 571 public void setNonStrokingColor( Color color ) throws IOException 572 { 573 ColorSpace colorSpace = color.getColorSpace(); 574 if( colorSpace.getType() == ColorSpace.TYPE_RGB ) 575 { 576 setNonStrokingColor( color.getRed(), color.getGreen(), color.getBlue() ); 577 } 578 else if( colorSpace.getType() == ColorSpace.TYPE_GRAY ) 579 { 580 color.getColorComponents( colorComponents ); 581 setNonStrokingColor( colorComponents[0] ); 582 } 583 else if( colorSpace.getType() == ColorSpace.TYPE_CMYK ) 584 { 585 color.getColorComponents( colorComponents ); 586 setNonStrokingColor( colorComponents[0], colorComponents[2], colorComponents[2], colorComponents[3] ); 587 } 588 else 589 { 590 throw new IOException( "Error: unknown colorspace:" + colorSpace ); 591 } 592 } 593 594 /** 595 * Set the stroking color, specified as RGB, 0-255. 596 * 597 * @param r The red value. 598 * @param g The green value. 599 * @param b The blue value. 600 * @throws IOException If an IO error occurs while writing to the stream. 601 */ 602 public void setStrokingColor( int r, int g, int b ) throws IOException 603 { 604 appendRawCommands( formatDecimal.format( r/255d ) ); 605 appendRawCommands( SPACE ); 606 appendRawCommands( formatDecimal.format( g/255d ) ); 607 appendRawCommands( SPACE ); 608 appendRawCommands( formatDecimal.format( b/255d ) ); 609 appendRawCommands( SPACE ); 610 appendRawCommands( RG_STROKING ); 611 } 612 613 /** 614 * Set the stroking color, specified as CMYK, 0-255. 615 * 616 * @param c The cyan value. 617 * @param m The magenta value. 618 * @param y The yellow value. 619 * @param k The black value. 620 * @throws IOException If an IO error occurs while writing to the stream. 621 */ 622 public void setStrokingColor( int c, int m, int y, int k) throws IOException 623 { 624 appendRawCommands( formatDecimal.format( c/255d ) ); 625 appendRawCommands( SPACE ); 626 appendRawCommands( formatDecimal.format( m/255d ) ); 627 appendRawCommands( SPACE ); 628 appendRawCommands( formatDecimal.format( y/255d ) ); 629 appendRawCommands( SPACE ); 630 appendRawCommands( formatDecimal.format( k/255d ) ); 631 appendRawCommands( SPACE ); 632 appendRawCommands( K_STROKING ); 633 } 634 635 /** 636 * Set the stroking color, specified as CMYK, 0.0-1.0. 637 * 638 * @param c The cyan value. 639 * @param m The magenta value. 640 * @param y The yellow value. 641 * @param k The black value. 642 * @throws IOException If an IO error occurs while writing to the stream. 643 */ 644 public void setStrokingColor( double c, double m, double y, double k) throws IOException 645 { 646 appendRawCommands( formatDecimal.format( c ) ); 647 appendRawCommands( SPACE ); 648 appendRawCommands( formatDecimal.format( m ) ); 649 appendRawCommands( SPACE ); 650 appendRawCommands( formatDecimal.format( y ) ); 651 appendRawCommands( SPACE ); 652 appendRawCommands( formatDecimal.format( k ) ); 653 appendRawCommands( SPACE ); 654 appendRawCommands( K_STROKING ); 655 } 656 657 /** 658 * Set the stroking color, specified as grayscale, 0-255. 659 * 660 * @param g The gray value. 661 * @throws IOException If an IO error occurs while writing to the stream. 662 */ 663 public void setStrokingColor( int g ) throws IOException 664 { 665 appendRawCommands( formatDecimal.format( g/255d ) ); 666 appendRawCommands( SPACE ); 667 appendRawCommands( G_STROKING ); 668 } 669 670 /** 671 * Set the stroking color, specified as Grayscale 0.0-1.0. 672 * 673 * @param g The gray value. 674 * @throws IOException If an IO error occurs while writing to the stream. 675 */ 676 public void setStrokingColor( double g ) throws IOException 677 { 678 appendRawCommands( formatDecimal.format( g ) ); 679 appendRawCommands( SPACE ); 680 appendRawCommands( G_STROKING ); 681 } 682 683 /** 684 * Set the color components of current non stroking colorspace. 685 * 686 * @param components The components to set for the current color. 687 * @throws IOException If there is an error while writing to the stream. 688 */ 689 public void setNonStrokingColor( float[] components ) throws IOException 690 { 691 for( int i=0; i< components.length; i++ ) 692 { 693 appendRawCommands( formatDecimal.format( components[i] ) ); 694 appendRawCommands( SPACE ); 695 } 696 if( currentNonStrokingColorSpace instanceof PDSeparation || 697 currentNonStrokingColorSpace instanceof PDPattern || 698 currentNonStrokingColorSpace instanceof PDDeviceN || 699 currentNonStrokingColorSpace instanceof PDICCBased ) 700 { 701 appendRawCommands( SET_NON_STROKING_COLOR_COMPLEX ); 702 } 703 else 704 { 705 appendRawCommands( SET_NON_STROKING_COLOR_SIMPLE ); 706 } 707 } 708 709 /** 710 * Set the non stroking color, specified as RGB, 0-255. 711 * 712 * @param r The red value. 713 * @param g The green value. 714 * @param b The blue value. 715 * @throws IOException If an IO error occurs while writing to the stream. 716 */ 717 public void setNonStrokingColor( int r, int g, int b ) throws IOException 718 { 719 appendRawCommands( formatDecimal.format( r/255d ) ); 720 appendRawCommands( SPACE ); 721 appendRawCommands( formatDecimal.format( g/255d ) ); 722 appendRawCommands( SPACE ); 723 appendRawCommands( formatDecimal.format( b/255d ) ); 724 appendRawCommands( SPACE ); 725 appendRawCommands( RG_NON_STROKING ); 726 } 727 728 /** 729 * Set the non stroking color, specified as CMYK, 0-255. 730 * 731 * @param c The cyan value. 732 * @param m The magenta value. 733 * @param y The yellow value. 734 * @param k The black value. 735 * @throws IOException If an IO error occurs while writing to the stream. 736 */ 737 public void setNonStrokingColor( int c, int m, int y, int k) throws IOException 738 { 739 appendRawCommands( formatDecimal.format( c/255d ) ); 740 appendRawCommands( SPACE ); 741 appendRawCommands( formatDecimal.format( m/255d ) ); 742 appendRawCommands( SPACE ); 743 appendRawCommands( formatDecimal.format( y/255d ) ); 744 appendRawCommands( SPACE ); 745 appendRawCommands( formatDecimal.format( k/255d ) ); 746 appendRawCommands( SPACE ); 747 appendRawCommands( K_NON_STROKING ); 748 } 749 750 /** 751 * Set the non stroking color, specified as CMYK, 0.0-1.0. 752 * 753 * @param c The cyan value. 754 * @param m The magenta value. 755 * @param y The yellow value. 756 * @param k The black value. 757 * @throws IOException If an IO error occurs while writing to the stream. 758 */ 759 public void setNonStrokingColor( double c, double m, double y, double k) throws IOException 760 { 761 appendRawCommands( formatDecimal.format( c ) ); 762 appendRawCommands( SPACE ); 763 appendRawCommands( formatDecimal.format( m ) ); 764 appendRawCommands( SPACE ); 765 appendRawCommands( formatDecimal.format( y ) ); 766 appendRawCommands( SPACE ); 767 appendRawCommands( formatDecimal.format( k ) ); 768 appendRawCommands( SPACE ); 769 appendRawCommands( K_NON_STROKING ); 770 } 771 772 /** 773 * Set the non stroking color, specified as grayscale, 0-255. 774 * 775 * @param g The gray value. 776 * @throws IOException If an IO error occurs while writing to the stream. 777 */ 778 public void setNonStrokingColor( int g ) throws IOException 779 { 780 appendRawCommands( formatDecimal.format( g/255d ) ); 781 appendRawCommands( SPACE ); 782 appendRawCommands( G_NON_STROKING ); 783 } 784 785 /** 786 * Set the non stroking color, specified as Grayscale 0.0-1.0. 787 * 788 * @param g The gray value. 789 * @throws IOException If an IO error occurs while writing to the stream. 790 */ 791 public void setNonStrokingColor( double g ) throws IOException 792 { 793 appendRawCommands( formatDecimal.format( g ) ); 794 appendRawCommands( SPACE ); 795 appendRawCommands( G_NON_STROKING ); 796 } 797 798 /** 799 * Draw a rectangle on the page using the current non stroking color. 800 * 801 * @param x The lower left x coordinate. 802 * @param y The lower left y coordinate. 803 * @param width The width of the rectangle. 804 * @param height The height of the rectangle. 805 * @throws IOException If there is an error while drawing on the screen. 806 */ 807 public void fillRect( float x, float y, float width, float height ) throws IOException 808 { 809 appendRawCommands( formatDecimal.format( x ) ); 810 appendRawCommands( SPACE ); 811 appendRawCommands( formatDecimal.format( y ) ); 812 appendRawCommands( SPACE ); 813 appendRawCommands( formatDecimal.format( width ) ); 814 appendRawCommands( SPACE ); 815 appendRawCommands( formatDecimal.format( height ) ); 816 appendRawCommands( SPACE ); 817 appendRawCommands( APPEND_RECTANGLE ); 818 appendRawCommands( FILL ); 819 } 820 821 /** 822 * Draw a line on the page using the current non stroking color and the current line width. 823 * 824 * @param xStart The start x coordinate. 825 * @param yStart The start y coordinate. 826 * @param xEnd The end x coordinate. 827 * @param yEnd The end y coordinate. 828 * @throws IOException If there is an error while drawing on the screen. 829 */ 830 public void drawLine( float xStart, float yStart, float xEnd, float yEnd ) throws IOException 831 { 832 // moveTo 833 appendRawCommands( formatDecimal.format( xStart) ); 834 appendRawCommands( SPACE ); 835 appendRawCommands( formatDecimal.format( yStart) ); 836 appendRawCommands( SPACE ); 837 appendRawCommands( MOVE_TO ); 838 // lineTo 839 appendRawCommands( formatDecimal.format( xEnd ) ); 840 appendRawCommands( SPACE ); 841 appendRawCommands( formatDecimal.format( yEnd ) ); 842 appendRawCommands( SPACE ); 843 appendRawCommands( LINE_TO ); 844 // stroke 845 appendRawCommands( STROKE ); 846 847 } 848 849 /** 850 * Set linewidth to the given value. 851 * 852 * @param lineWidth The width which is used for drwaing. 853 * @throws IOException If there is an error while drawing on the screen. 854 */ 855 public void setLineWidth(float lineWidth) throws IOException 856 { 857 appendRawCommands( formatDecimal.format( lineWidth ) ); 858 appendRawCommands( SPACE ); 859 appendRawCommands( LINE_WIDTH ); 860 } 861 /** 862 * This will append raw commands to the content stream. 863 * 864 * @param commands The commands to append to the stream. 865 * @throws IOException If an error occurs while writing to the stream. 866 */ 867 public void appendRawCommands( String commands ) throws IOException 868 { 869 appendRawCommands( commands.getBytes( "ISO-8859-1" ) ); 870 } 871 872 /** 873 * This will append raw commands to the content stream. 874 * 875 * @param commands The commands to append to the stream. 876 * @throws IOException If an error occurs while writing to the stream. 877 */ 878 public void appendRawCommands( byte[] commands ) throws IOException 879 { 880 output.write( commands ); 881 } 882 883 /** 884 * This will append raw commands to the content stream. 885 * 886 * @param data Append a raw byte to the stream. 887 * 888 * @throws IOException If an error occurs while writing to the stream. 889 */ 890 public void appendRawCommands( int data ) throws IOException 891 { 892 output.write( data ); 893 } 894 895 /** 896 * Close the content stream. This must be called when you are done with this 897 * object. 898 * @throws IOException If the underlying stream has a problem being written to. 899 */ 900 public void close() throws IOException 901 { 902 output.close(); 903 } 904 }