1 31 package org.pdfbox.pdmodel.edit; 32 33 import java.awt.Color ; 34 import java.awt.color.ColorSpace ; 35 import java.io.ByteArrayOutputStream ; 36 import java.io.IOException ; 37 import java.io.OutputStream ; 38 39 import java.text.NumberFormat ; 40 41 import java.util.ArrayList ; 42 import java.util.List ; 43 import java.util.Locale ; 44 import java.util.Map ; 45 import java.util.HashMap ; 46 47 import org.pdfbox.pdmodel.PDDocument; 48 import org.pdfbox.pdmodel.PDPage; 49 import org.pdfbox.pdmodel.PDResources; 50 51 import org.pdfbox.pdmodel.common.COSStreamArray; 52 import org.pdfbox.pdmodel.common.PDStream; 53 54 import org.pdfbox.pdmodel.font.PDFont; 55 import org.pdfbox.pdmodel.graphics.color.PDColorSpace; 56 import org.pdfbox.pdmodel.graphics.color.PDDeviceCMYK; 57 import org.pdfbox.pdmodel.graphics.color.PDDeviceGray; 58 import org.pdfbox.pdmodel.graphics.color.PDDeviceN; 59 import org.pdfbox.pdmodel.graphics.color.PDDeviceRGB; 60 import org.pdfbox.pdmodel.graphics.color.PDICCBased; 61 import org.pdfbox.pdmodel.graphics.color.PDPattern; 62 import org.pdfbox.pdmodel.graphics.color.PDSeparation; 63 import org.pdfbox.pdmodel.graphics.xobject.PDXObjectImage; 64 import org.pdfbox.util.MapUtil; 65 66 import org.pdfbox.cos.COSArray; 67 import org.pdfbox.cos.COSDictionary; 68 import org.pdfbox.cos.COSName; 69 import org.pdfbox.cos.COSString; 70 71 72 79 public class PDPageContentStream 80 { 81 private PDPage page; 82 private OutputStream output; 83 private boolean inTextMode = false; 84 private Map fontMappings = new HashMap (); 85 private Map imageMappings = new HashMap (); 86 private PDResources resources; 87 private Map fonts; 88 private Map images; 89 90 private PDColorSpace currentStrokingColorSpace = new PDDeviceGray(); 91 private PDColorSpace currentNonStrokingColorSpace = new PDDeviceGray(); 92 93 private float[] colorComponents = new float[4]; 95 96 private NumberFormat formatDecimal = NumberFormat.getNumberInstance( Locale.US ); 97 98 private static final String BEGIN_TEXT = "BT\n"; 99 private static final String END_TEXT = "ET\n"; 100 private static final String SET_FONT = "Tf\n"; 101 private static final String MOVE_TEXT_POSITION = "Td\n"; 102 private static final String SHOW_TEXT = "Tj\n"; 103 104 private static final String SAVE_GRAPHICS_STATE = "q\n"; 105 private static final String RESTORE_GRAPHICS_STATE = "Q\n"; 106 private static final String CONCATENATE_MATRIX = "cm\n"; 107 private static final String XOBJECT_DO = "Do\n"; 108 private static final String RG_STROKING = "RG\n"; 109 private static final String RG_NON_STROKING = "rg\n"; 110 private static final String K_STROKING = "K\n"; 111 private static final String K_NON_STROKING = "k\n"; 112 private static final String G_STROKING = "G\n"; 113 private static final String G_NON_STROKING = "g\n"; 114 private static final String APPEND_RECTANGLE = "re\n"; 115 private static final String FILL = "f\n"; 116 117 private static final String SET_STROKING_COLORSPACE = "CS\n"; 118 private static final String SET_NON_STROKING_COLORSPACE = "cs\n"; 119 120 private static final String SET_STROKING_COLOR_SIMPLE="SC\n"; 121 private static final String SET_STROKING_COLOR_COMPLEX="SCN\n"; 122 private static final String SET_NON_STROKING_COLOR_SIMPLE="sc\n"; 123 private static final String SET_NON_STROKING_COLOR_COMPLEX="scn\n"; 124 125 126 127 private static final int SPACE = 32; 128 129 130 137 public PDPageContentStream( PDDocument document, PDPage sourcePage ) throws IOException 138 { 139 this(document,sourcePage,false,true); 140 } 141 142 151 public PDPageContentStream( PDDocument document, PDPage sourcePage, boolean appendContent, boolean compress ) 152 throws IOException 153 { 154 page = sourcePage; 155 resources = page.getResources(); 156 if( resources == null ) 157 { 158 resources = new PDResources(); 159 page.setResources( resources ); 160 } 161 fonts = resources.getFonts(); 162 images = resources.getImages(); 163 if(appendContent) 165 { 166 PDStream contents = sourcePage.getContents(); 168 169 PDStream contentsToAppend = new PDStream( document ); 171 172 COSStreamArray compoundStream = null; 174 175 if(contents.getStream() instanceof COSStreamArray) 177 { 178 compoundStream = (COSStreamArray)contents.getStream(); 179 compoundStream.appendStream( contentsToAppend.getStream()); 180 } 181 else 182 { 183 COSArray newArray = new COSArray(); 185 newArray.add(contents.getCOSObject()); 186 newArray.add(contentsToAppend.getCOSObject()); 187 compoundStream = new COSStreamArray(newArray); 188 } 189 190 if( compress ) 191 { 192 List filters = new ArrayList (); 193 filters.add( COSName.FLATE_DECODE ); 194 contentsToAppend.setFilters( filters ); 195 } 196 197 sourcePage.setContents( new PDStream(compoundStream) ); 199 output = contentsToAppend.createOutputStream(); 200 } 201 else 202 { 203 PDStream contents = new PDStream( document ); 204 if( compress ) 205 { 206 List filters = new ArrayList (); 207 filters.add( COSName.FLATE_DECODE ); 208 contents.setFilters( filters ); 209 } 210 sourcePage.setContents( contents ); 211 output = contents.createOutputStream(); 212 } 213 formatDecimal.setMaximumFractionDigits( 10 ); 214 formatDecimal.setGroupingUsed( false ); 215 } 216 217 223 public void beginText() throws IOException 224 { 225 if( inTextMode ) 226 { 227 throw new IOException ( "Error: Nested beginText() calls are not allowed." ); 228 } 229 appendRawCommands( BEGIN_TEXT ); 230 inTextMode = true; 231 } 232 233 239 public void endText() throws IOException 240 { 241 if( !inTextMode ) 242 { 243 throw new IOException ( "Error: You must call beginText() before calling endText." ); 244 } 245 appendRawCommands( END_TEXT ); 246 inTextMode = false; 247 } 248 249 256 public void setFont( PDFont font, float fontSize ) throws IOException 257 { 258 String fontMapping = (String )fontMappings.get( font ); 259 if( fontMapping == null ) 260 { 261 fontMapping = MapUtil.getNextUniqueKey( fonts, "F" ); 262 fontMappings.put( font, fontMapping ); 263 fonts.put( fontMapping, font ); 264 } 265 appendRawCommands( "/"); 266 appendRawCommands( fontMapping ); 267 appendRawCommands( SPACE ); 268 appendRawCommands( formatDecimal.format( fontSize ) ); 269 appendRawCommands( SPACE ); 270 appendRawCommands( SET_FONT ); 271 } 272 273 282 public void drawImage( PDXObjectImage image, float x, float y ) throws IOException 283 { 284 drawImage( image, x, y, image.getWidth(), image.getHeight() ); 285 } 286 287 298 public void drawImage( PDXObjectImage image, float x, float y, float width, float height ) throws IOException 299 { 300 String imageMapping = (String )imageMappings.get( image ); 301 if( imageMapping == null ) 302 { 303 imageMapping = MapUtil.getNextUniqueKey( images, "Im" ); 304 imageMappings.put( image, imageMapping ); 305 images.put( imageMapping, image ); 306 } 307 appendRawCommands( SAVE_GRAPHICS_STATE ); 308 appendRawCommands( formatDecimal.format( width ) ); 309 appendRawCommands( SPACE ); 310 appendRawCommands( formatDecimal.format( 0 ) ); 311 appendRawCommands( SPACE ); 312 appendRawCommands( formatDecimal.format( 0 ) ); 313 appendRawCommands( SPACE ); 314 appendRawCommands( formatDecimal.format( height ) ); 315 appendRawCommands( SPACE ); 316 appendRawCommands( formatDecimal.format( x ) ); 317 appendRawCommands( SPACE ); 318 appendRawCommands( formatDecimal.format( y ) ); 319 appendRawCommands( SPACE ); 320 appendRawCommands( CONCATENATE_MATRIX ); 321 appendRawCommands( SPACE ); 322 appendRawCommands( "/" ); 323 appendRawCommands( imageMapping ); 324 appendRawCommands( SPACE ); 325 appendRawCommands( XOBJECT_DO ); 326 appendRawCommands( SPACE ); 327 appendRawCommands( RESTORE_GRAPHICS_STATE ); 328 } 329 330 336 public void moveTextPositionByAmount( float x, float y ) throws IOException 337 { 338 if( !inTextMode ) 339 { 340 throw new IOException ( "Error: must call beginText() before moveTextPositionByAmount"); 341 } 342 appendRawCommands( formatDecimal.format( x ) ); 343 appendRawCommands( SPACE ); 344 appendRawCommands( formatDecimal.format( y ) ); 345 appendRawCommands( SPACE ); 346 appendRawCommands( MOVE_TEXT_POSITION ); 347 } 348 349 355 public void drawString( String text ) throws IOException 356 { 357 if( !inTextMode ) 358 { 359 throw new IOException ( "Error: must call beginText() before drawString"); 360 } 361 COSString string = new COSString( text ); 362 ByteArrayOutputStream buffer = new ByteArrayOutputStream (); 363 string.writePDF( buffer ); 364 appendRawCommands( new String ( buffer.toByteArray(), "ISO-8859-1")); 365 appendRawCommands( SPACE ); 366 appendRawCommands( SHOW_TEXT ); 367 } 368 369 376 public void setStrokingColorSpace( PDColorSpace colorSpace ) throws IOException 377 { 378 writeColorSpace( colorSpace ); 379 appendRawCommands( SET_STROKING_COLORSPACE ); 380 } 381 382 389 public void setNonStrokingColorSpace( PDColorSpace colorSpace ) throws IOException 390 { 391 writeColorSpace( colorSpace ); 392 appendRawCommands( SET_NON_STROKING_COLORSPACE ); 393 } 394 395 private void writeColorSpace( PDColorSpace colorSpace ) throws IOException 396 { 397 COSName key = null; 398 if( colorSpace instanceof PDDeviceGray || 399 colorSpace instanceof PDDeviceRGB || 400 colorSpace instanceof PDDeviceCMYK ) 401 { 402 key = COSName.getPDFName( colorSpace.getName() ); 403 } 404 else 405 { 406 COSDictionary colorSpaces = 407 (COSDictionary)resources.getCOSDictionary().getDictionaryObject(COSName.COLORSPACE); 408 if( colorSpaces == null ) 409 { 410 colorSpaces = new COSDictionary(); 411 resources.getCOSDictionary().setItem( COSName.COLORSPACE, colorSpaces ); 412 } 413 key = colorSpaces.getKeyForValue( colorSpace.getCOSObject() ); 414 415 if( key == null ) 416 { 417 int counter = 0; 418 String csName = "CS"; 419 while( colorSpaces.containsValue( csName + counter ) ) 420 { 421 counter++; 422 } 423 key = COSName.getPDFName( csName + counter ); 424 colorSpaces.setItem( key, colorSpace ); 425 } 426 } 427 key.writePDF( output ); 428 appendRawCommands( SPACE ); 429 } 430 431 437 public void setStrokingColor( float[] components ) throws IOException 438 { 439 for( int i=0; i< components.length; i++ ) 440 { 441 appendRawCommands( formatDecimal.format( components[i] ) ); 442 appendRawCommands( SPACE ); 443 } 444 if( currentStrokingColorSpace instanceof PDSeparation || 445 currentStrokingColorSpace instanceof PDPattern || 446 currentStrokingColorSpace instanceof PDDeviceN || 447 currentStrokingColorSpace instanceof PDICCBased ) 448 { 449 appendRawCommands( SET_STROKING_COLOR_COMPLEX ); 450 } 451 else 452 { 453 appendRawCommands( SET_STROKING_COLOR_SIMPLE ); 454 } 455 } 456 457 463 public void setStrokingColor( Color color ) throws IOException 464 { 465 ColorSpace colorSpace = color.getColorSpace(); 466 if( colorSpace.getType() == ColorSpace.TYPE_RGB ) 467 { 468 setStrokingColor( color.getRed(), color.getGreen(), color.getBlue() ); 469 } 470 else if( colorSpace.getType() == ColorSpace.TYPE_GRAY ) 471 { 472 color.getColorComponents( colorComponents ); 473 setStrokingColor( colorComponents[0] ); 474 } 475 else if( colorSpace.getType() == ColorSpace.TYPE_CMYK ) 476 { 477 color.getColorComponents( colorComponents ); 478 setStrokingColor( colorComponents[0], colorComponents[2], colorComponents[2], colorComponents[3] ); 479 } 480 else 481 { 482 throw new IOException ( "Error: unknown colorspace:" + colorSpace ); 483 } 484 } 485 486 492 public void setNonStrokingColor( Color color ) throws IOException 493 { 494 ColorSpace colorSpace = color.getColorSpace(); 495 if( colorSpace.getType() == ColorSpace.TYPE_RGB ) 496 { 497 setNonStrokingColor( color.getRed(), color.getGreen(), color.getBlue() ); 498 } 499 else if( colorSpace.getType() == ColorSpace.TYPE_GRAY ) 500 { 501 color.getColorComponents( colorComponents ); 502 setNonStrokingColor( colorComponents[0] ); 503 } 504 else if( colorSpace.getType() == ColorSpace.TYPE_CMYK ) 505 { 506 color.getColorComponents( colorComponents ); 507 setNonStrokingColor( colorComponents[0], colorComponents[2], colorComponents[2], colorComponents[3] ); 508 } 509 else 510 { 511 throw new IOException ( "Error: unknown colorspace:" + colorSpace ); 512 } 513 } 514 515 523 public void setStrokingColor( int r, int g, int b ) throws IOException 524 { 525 appendRawCommands( formatDecimal.format( r/255d ) ); 526 appendRawCommands( SPACE ); 527 appendRawCommands( formatDecimal.format( g/255d ) ); 528 appendRawCommands( SPACE ); 529 appendRawCommands( formatDecimal.format( b/255d ) ); 530 appendRawCommands( SPACE ); 531 appendRawCommands( RG_STROKING ); 532 } 533 534 543 public void setStrokingColor( int c, int m, int y, int k) throws IOException 544 { 545 appendRawCommands( formatDecimal.format( c/255d ) ); 546 appendRawCommands( SPACE ); 547 appendRawCommands( formatDecimal.format( m/255d ) ); 548 appendRawCommands( SPACE ); 549 appendRawCommands( formatDecimal.format( y/255d ) ); 550 appendRawCommands( SPACE ); 551 appendRawCommands( formatDecimal.format( k/255d ) ); 552 appendRawCommands( SPACE ); 553 appendRawCommands( K_STROKING ); 554 } 555 556 565 public void setStrokingColor( double c, double m, double y, double k) throws IOException 566 { 567 appendRawCommands( formatDecimal.format( c ) ); 568 appendRawCommands( SPACE ); 569 appendRawCommands( formatDecimal.format( m ) ); 570 appendRawCommands( SPACE ); 571 appendRawCommands( formatDecimal.format( y ) ); 572 appendRawCommands( SPACE ); 573 appendRawCommands( formatDecimal.format( k ) ); 574 appendRawCommands( SPACE ); 575 appendRawCommands( K_STROKING ); 576 } 577 578 584 public void setStrokingColor( int g ) throws IOException 585 { 586 appendRawCommands( formatDecimal.format( g/255d ) ); 587 appendRawCommands( SPACE ); 588 appendRawCommands( G_STROKING ); 589 } 590 591 597 public void setStrokingColor( double g ) throws IOException 598 { 599 appendRawCommands( formatDecimal.format( g ) ); 600 appendRawCommands( SPACE ); 601 appendRawCommands( G_STROKING ); 602 } 603 604 610 public void setNonStrokingColor( float[] components ) throws IOException 611 { 612 for( int i=0; i< components.length; i++ ) 613 { 614 appendRawCommands( formatDecimal.format( components[i] ) ); 615 appendRawCommands( SPACE ); 616 } 617 if( currentNonStrokingColorSpace instanceof PDSeparation || 618 currentNonStrokingColorSpace instanceof PDPattern || 619 currentNonStrokingColorSpace instanceof PDDeviceN || 620 currentNonStrokingColorSpace instanceof PDICCBased ) 621 { 622 appendRawCommands( SET_NON_STROKING_COLOR_COMPLEX ); 623 } 624 else 625 { 626 appendRawCommands( SET_NON_STROKING_COLOR_SIMPLE ); 627 } 628 } 629 630 638 public void setNonStrokingColor( int r, int g, int b ) throws IOException 639 { 640 appendRawCommands( formatDecimal.format( r/255d ) ); 641 appendRawCommands( SPACE ); 642 appendRawCommands( formatDecimal.format( g/255d ) ); 643 appendRawCommands( SPACE ); 644 appendRawCommands( formatDecimal.format( b/255d ) ); 645 appendRawCommands( SPACE ); 646 appendRawCommands( RG_NON_STROKING ); 647 } 648 649 658 public void setNonStrokingColor( int c, int m, int y, int k) throws IOException 659 { 660 appendRawCommands( formatDecimal.format( c/255d ) ); 661 appendRawCommands( SPACE ); 662 appendRawCommands( formatDecimal.format( m/255d ) ); 663 appendRawCommands( SPACE ); 664 appendRawCommands( formatDecimal.format( y/255d ) ); 665 appendRawCommands( SPACE ); 666 appendRawCommands( formatDecimal.format( k/255d ) ); 667 appendRawCommands( SPACE ); 668 appendRawCommands( K_NON_STROKING ); 669 } 670 671 680 public void setNonStrokingColor( double c, double m, double y, double k) throws IOException 681 { 682 appendRawCommands( formatDecimal.format( c ) ); 683 appendRawCommands( SPACE ); 684 appendRawCommands( formatDecimal.format( m ) ); 685 appendRawCommands( SPACE ); 686 appendRawCommands( formatDecimal.format( y ) ); 687 appendRawCommands( SPACE ); 688 appendRawCommands( formatDecimal.format( k ) ); 689 appendRawCommands( SPACE ); 690 appendRawCommands( K_NON_STROKING ); 691 } 692 693 699 public void setNonStrokingColor( int g ) throws IOException 700 { 701 appendRawCommands( formatDecimal.format( g/255d ) ); 702 appendRawCommands( SPACE ); 703 appendRawCommands( G_NON_STROKING ); 704 } 705 706 712 public void setNonStrokingColor( double g ) throws IOException 713 { 714 appendRawCommands( formatDecimal.format( g ) ); 715 appendRawCommands( SPACE ); 716 appendRawCommands( G_NON_STROKING ); 717 } 718 719 728 public void fillRect( float x, float y, float width, float height ) throws IOException 729 { 730 appendRawCommands( formatDecimal.format( x ) ); 731 appendRawCommands( SPACE ); 732 appendRawCommands( formatDecimal.format( y ) ); 733 appendRawCommands( SPACE ); 734 appendRawCommands( formatDecimal.format( width ) ); 735 appendRawCommands( SPACE ); 736 appendRawCommands( formatDecimal.format( height ) ); 737 appendRawCommands( SPACE ); 738 appendRawCommands( APPEND_RECTANGLE ); 739 appendRawCommands( FILL ); 740 } 741 742 743 749 public void appendRawCommands( String commands ) throws IOException 750 { 751 appendRawCommands( commands.getBytes( "ISO-8859-1" ) ); 752 } 753 754 760 public void appendRawCommands( byte[] commands ) throws IOException 761 { 762 output.write( commands ); 763 } 764 765 772 public void appendRawCommands( int data ) throws IOException 773 { 774 output.write( data ); 775 } 776 777 782 public void close() throws IOException 783 { 784 output.close(); 785 } 786 } | Popular Tags |