1 31 package org.pdfbox; 32 33 import java.io.IOException ; 34 import java.util.ArrayList ; 35 import java.util.Hashtable ; 36 import java.util.List ; 37 import java.util.regex.Matcher ; 38 import java.util.regex.Pattern ; 39 40 import org.pdfbox.cos.COSFloat; 41 import org.pdfbox.cos.COSNumber; 42 import org.pdfbox.exceptions.InvalidPasswordException; 43 import org.pdfbox.pdfparser.PDFStreamParser; 44 import org.pdfbox.pdfwriter.ContentStreamWriter; 45 import org.pdfbox.pdmodel.PDDocument; 46 import org.pdfbox.pdmodel.PDPage; 47 import org.pdfbox.pdmodel.common.PDStream; 48 import org.pdfbox.util.PDFOperator; 49 50 58 public class ConvertColorspace 59 { 60 61 private static final String PASSWORD = "-password"; 62 private static final String CONVERSION = "-equiv"; 63 private static final String DEST_COLORSPACE = "-toColorspace"; 64 65 68 private ConvertColorspace() 69 { 70 } 72 73 82 private void replaceColors( PDDocument inputFile, 83 Hashtable colorEquivalents, 84 String destColorspace ) throws IOException 85 { 86 if( !destColorspace.equals( "CMYK" ) ) 87 { 88 throw new IOException ( "Error: Unknown colorspace " + destColorspace ); 89 } 90 List pagesList = inputFile.getDocumentCatalog().getAllPages(); 91 92 PDPage currentPage = null; 93 PDFStreamParser parser = null; 94 List pageTokens = null; 95 List editedPageTokens = null; 96 97 for(int pageCounter = 0; pageCounter < pagesList.size(); pageCounter++) { 99 currentPage = (PDPage)pagesList.get( pageCounter ); 100 101 parser = new PDFStreamParser(currentPage.getContents().getStream()); 102 parser.parse(); 103 pageTokens = parser.getTokens(); 104 editedPageTokens = new ArrayList (); 105 106 for( int counter = 0; counter < pageTokens.size(); counter++) { 108 Object token = pageTokens.get( counter ); 109 if( token instanceof PDFOperator ) { 111 PDFOperator tokenOperator = (PDFOperator)token; 112 113 if(tokenOperator.getOperation().equals("rg")) { 115 if( destColorspace.equals( "CMYK" ) ) 116 { 117 replaceRGBTokensWithCMYKTokens( editedPageTokens, pageTokens, counter, colorEquivalents ); 118 editedPageTokens.add( PDFOperator.getOperator( "k" )); 119 } 120 } 121 else if(tokenOperator.getOperation().equals("RG")) { 123 if( destColorspace.equals( "CMYK" ) ) 124 { 125 replaceRGBTokensWithCMYKTokens( editedPageTokens, pageTokens, counter, colorEquivalents ); 126 editedPageTokens.add( PDFOperator.getOperator( "K" )); 127 } 128 } 129 else if(tokenOperator.getOperation().equals("g")) { 131 if( destColorspace.equals( "CMYK" ) ) 132 { 133 replaceGrayTokensWithCMYKTokens( editedPageTokens, pageTokens, counter, colorEquivalents ); 134 editedPageTokens.add( PDFOperator.getOperator( "k" )); 135 } 136 } 137 else if(tokenOperator.getOperation().equals("G")) { 139 if( destColorspace.equals( "CMYK" ) ) 140 { 141 replaceGrayTokensWithCMYKTokens( editedPageTokens, pageTokens, counter, colorEquivalents ); 142 editedPageTokens.add( PDFOperator.getOperator( "K" )); 143 } 144 } 145 else 146 { 147 editedPageTokens.add( token ); 148 } 149 } 150 else { 152 editedPageTokens.add( token ); 153 } 154 } 156 PDStream updatedPageContents = new PDStream(inputFile); 158 ContentStreamWriter contentWriter = new ContentStreamWriter( updatedPageContents.createOutputStream() ); 159 contentWriter.writeTokens( editedPageTokens ); 160 currentPage.setContents( updatedPageContents ); 161 162 } } 164 165 private void replaceRGBTokensWithCMYKTokens( List editedPageTokens, 166 List pageTokens, 167 int counter, 168 Hashtable colorEquivalents ) 169 { 170 float red = ((COSNumber)pageTokens.get( counter - 3 )).floatValue(); 172 float green = ((COSNumber)pageTokens.get( counter - 2 )).floatValue(); 173 float blue = ((COSNumber)pageTokens.get( counter - 1 )).floatValue(); 174 175 int intRed = Math.round(red * 255.0f); 176 int intGreen = Math.round(green * 255.0f); 177 int intBlue = Math.round(blue * 255.0f); 178 179 ColorSpaceInstance rgbColor = new ColorSpaceInstance(); 180 rgbColor.colorspace = "RGB"; 181 rgbColor.colorspaceValues = new int[] { intRed, intGreen, intBlue }; 182 ColorSpaceInstance cmykColor = (ColorSpaceInstance)colorEquivalents.get(rgbColor); 183 float[] cmyk = null; 184 185 if( cmykColor != null ) 186 { 187 cmyk = new float[] { 188 cmykColor.colorspaceValues[0] / 100.0f, 189 cmykColor.colorspaceValues[1] / 100.0f, 190 cmykColor.colorspaceValues[2] / 100.0f, 191 cmykColor.colorspaceValues[3] / 100.0f 192 }; 193 } 194 else 195 { 196 cmyk = convertRGBToCMYK( red, green, blue ); 197 } 198 199 editedPageTokens.remove( editedPageTokens.size() -1 ); 201 editedPageTokens.remove( editedPageTokens.size() -1 ); 202 editedPageTokens.remove( editedPageTokens.size() -1 ); 203 204 editedPageTokens.add( new COSFloat( cmyk[0] ) ); 206 editedPageTokens.add( new COSFloat( cmyk[1] ) ); 207 editedPageTokens.add( new COSFloat( cmyk[2] ) ); 208 editedPageTokens.add( new COSFloat( cmyk[3] ) ); 209 } 210 211 private void replaceGrayTokensWithCMYKTokens( List editedPageTokens, 212 List pageTokens, 213 int counter, 214 Hashtable colorEquivalents ) 215 { 216 float gray = ((COSNumber)pageTokens.get( counter - 1 )).floatValue(); 218 219 ColorSpaceInstance grayColor = new ColorSpaceInstance(); 220 grayColor.colorspace = "Grayscale"; 221 grayColor.colorspaceValues = new int[] { Math.round( gray * 100 ) }; 222 ColorSpaceInstance cmykColor = (ColorSpaceInstance)colorEquivalents.get(grayColor); 223 float[] cmyk = null; 224 225 if( cmykColor != null ) 226 { 227 cmyk = new float[] { 228 cmykColor.colorspaceValues[0] / 100.0f, 229 cmykColor.colorspaceValues[1] / 100.0f, 230 cmykColor.colorspaceValues[2] / 100.0f, 231 cmykColor.colorspaceValues[3] / 100.0f 232 }; 233 } 234 else 235 { 236 cmyk = new float[] {0,0,0,gray}; 237 } 238 239 editedPageTokens.remove( editedPageTokens.size() -1 ); 241 242 editedPageTokens.add( new COSFloat( cmyk[0] ) ); 244 editedPageTokens.add( new COSFloat( cmyk[1] ) ); 245 editedPageTokens.add( new COSFloat( cmyk[2] ) ); 246 editedPageTokens.add( new COSFloat( cmyk[3] ) ); 247 } 248 249 private static float[] convertRGBToCMYK( float red, float green, float blue ) 250 { 251 float c = 1.0f - red; 256 float m = 1.0f - green; 257 float y = 1.0f - blue; 258 float k = 1.0f; 259 260 k = Math.min( Math.min( Math.min( c,k ), m), y ); 261 262 c = ( c - k ) / ( 1 - k ); 263 m = ( m - k ) / ( 1 - k ); 264 y = ( y - k ) / ( 1 - k ); 265 return new float[] { c,m,y,k}; 266 } 267 268 private static int[] stringToIntArray( String string ) 269 { 270 String [] ints = string.split( "," ); 271 int[] retval = new int[ints.length]; 272 for( int i=0; i<ints.length; i++ ) 273 { 274 retval[i] = Integer.parseInt( ints[i] ); 275 } 276 return retval; 277 } 278 279 286 public static void main( String [] args ) throws Exception 287 { 288 String password = ""; 289 String inputFile = null; 290 String outputFile = null; 291 String destColorspace = "CMYK"; 292 293 Pattern colorEquivalentPattern = Pattern.compile( 294 "^(.*):\\((.*)\\)" + 295 "=(.*):\\((.*)\\)$"); 296 Matcher colorEquivalentMatcher = null; 297 298 Hashtable colorEquivalents = new Hashtable (); 300 301 for( int i=0; i<args.length; i++ ) 302 { 303 if( args[i].equals( PASSWORD ) ) 304 { 305 i++; 306 if( i >= args.length ) 307 { 308 usage(); 309 } 310 password = args[i]; 311 } 312 if( args[i].equals( DEST_COLORSPACE ) ) 313 { 314 i++; 315 if( i >= args.length ) 316 { 317 usage(); 318 } 319 destColorspace = args[i]; 320 } 321 if(args[i].equals( CONVERSION ) ) 322 { 323 i++; 324 if( i >= args.length ) 325 { 326 usage(); 327 } 328 329 colorEquivalentMatcher = colorEquivalentPattern.matcher(args[i]); 330 if(!colorEquivalentMatcher.matches()) 331 { 332 usage(); 333 } 334 String srcColorSpace = colorEquivalentMatcher.group(1); 335 String srcColorvalues = colorEquivalentMatcher.group(2); 336 String destColorSpace = colorEquivalentMatcher.group(3); 337 String destColorvalues = colorEquivalentMatcher.group(4); 338 339 ConvertColorspace.ColorSpaceInstance source = new ColorSpaceInstance(); 340 source.colorspace = srcColorSpace; 341 source.colorspaceValues = stringToIntArray( srcColorvalues ); 342 343 ColorSpaceInstance dest = new ColorSpaceInstance(); 344 dest.colorspace = destColorSpace; 345 dest.colorspaceValues = stringToIntArray( destColorvalues ); 346 347 colorEquivalents.put(source, dest); 348 349 } 350 else 351 { 352 if( inputFile == null ) 353 { 354 inputFile = args[i]; 355 } 356 else 357 { 358 outputFile = args[i]; 359 } 360 } 361 } 362 363 if( inputFile == null ) 364 { 365 usage(); 366 } 367 368 if( outputFile == null || outputFile.equals(inputFile)) 369 { 370 usage(); 371 } 372 373 PDDocument doc = null; 374 try 375 { 376 doc = PDDocument.load( inputFile ); 377 if( doc.isEncrypted() ) 378 { 379 try 380 { 381 doc.decrypt( password ); 382 } 383 catch( InvalidPasswordException e ) 384 { 385 if( !password.equals( "" ) ) { 387 System.err.println( "Error: The supplied password is incorrect." ); 388 System.exit( 2 ); 389 } 390 else 391 { 392 System.err.println( "Error: The document is encrypted." ); 394 usage(); 395 } 396 } 397 } 398 ConvertColorspace converter = new ConvertColorspace(); 399 converter.replaceColors(doc, colorEquivalents, destColorspace ); 400 doc.save( outputFile ); 401 } 402 finally 403 { 404 if( doc != null ) 405 { 406 doc.close(); 407 } 408 } 409 410 411 } 412 413 414 417 private static void usage() 418 { 419 System.err.println( "Usage: java org.pdfbox.ConvertColorspace [OPTIONS] <PDF Input file> " 420 +"<PDF Output File>\n" + 421 " -password <password> Password to decrypt document\n" + 422 " -equiv <color equivalent> Color equivalent to use for conversion.\n" + 423 " -destColorspace <color equivalent> The destination colorspace, CMYK is the only '" + 424 "supported colorspace." + 425 " \n" + 426 " The equiv format is : <source colorspace>:(colorspace value)=<dest colorspace>:(colorspace value)" + 427 " This option can be used as many times as necessary\n" + 428 " The supported equiv colorspaces are RGB and CMYK.\n" + 429 " RGB color values are integers between 0 and 255" + 430 " CMYK color values are integer between 0 and 100.\n" + 431 " Example: java org.pdfbox.ConvertColorspace -equiv RGB:(255,0,0)=CMYK(0,99,100,0) input.pdf output.pdf\n" + 432 " <PDF Input file> The PDF document to use\n" + 433 " <PDF Output file> The PDF file to write the result to. Must be different of input file\n" 434 ); 435 System.exit( 1 ); 436 } 437 438 442 private static class ColorSpaceInstance 443 { 444 private String colorspace = null; 445 private int[] colorspaceValues = null; 446 447 450 public int hashCode() 451 { 452 int code = colorspace.hashCode(); 453 for( int i=0; i<colorspaceValues.length; i++ ) 454 { 455 code += colorspaceValues[i]; 456 } 457 return code; 458 } 459 460 463 public boolean equals( Object o ) 464 { 465 boolean retval = false; 466 if( o instanceof ColorSpaceInstance ) 467 { 468 ColorSpaceInstance other = (ColorSpaceInstance)o; 469 if( this.colorspace.equals( other.colorspace ) && 470 colorspaceValues.length == other.colorspaceValues.length ) 471 { 472 retval = true; 473 for( int i=0; i<colorspaceValues.length && retval; i++ ) 474 { 475 retval = retval && colorspaceValues[i] == other.colorspaceValues[i]; 476 } 477 } 478 } 479 return retval; 480 } 481 } 482 } 483 484 | Popular Tags |