KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > pdfbox > ConvertColorspace


1 /**
2  * Copyright (c) 2006, www.pdfbox.org
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  * this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright notice,
11  * this list of conditions and the following disclaimer in the documentation
12  * and/or other materials provided with the distribution.
13  * 3. Neither the name of pdfbox; nor the names of its
14  * contributors may be used to endorse or promote products derived from this
15  * software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
24  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  * http://www.pdfbox.org
29  *
30  */

31 package org.pdfbox;
32
33 import java.io.IOException JavaDoc;
34 import java.util.ArrayList JavaDoc;
35 import java.util.Hashtable JavaDoc;
36 import java.util.List JavaDoc;
37 import java.util.regex.Matcher JavaDoc;
38 import java.util.regex.Pattern JavaDoc;
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 /**
51  * This is the main program that simply parses the pdf document and replace
52  * change a PDF to use a specific colorspace.
53  *
54  * @author <a HREF="ben@benlitchfield.com">Ben Litchfield</a>
55  * @author Pierre-Yves LandurĂ© (pierre-yves@landure.org)
56  * @version $Revision: 1.5 $
57  */

58 public class ConvertColorspace
59 {
60
61     private static final String JavaDoc PASSWORD = "-password";
62     private static final String JavaDoc CONVERSION = "-equiv";
63     private static final String JavaDoc DEST_COLORSPACE = "-toColorspace";
64
65     /**
66      * private constructor.
67     */

68     private ConvertColorspace()
69     {
70         //static class
71
}
72
73     /**
74      * The method that replace RGB colors by CMYK ones.
75      *
76      * @param inputFile input file name.
77      * @param colorEquivalents a dictionnary for the color equivalents.
78      * @param destColorspace The destination colorspace, currently CMYK is supported.
79      *
80      * @throws IOException If there is an error parsing the document.
81      */

82     private void replaceColors( PDDocument inputFile,
83                                Hashtable JavaDoc colorEquivalents,
84                                String JavaDoc destColorspace ) throws IOException JavaDoc
85     {
86         if( !destColorspace.equals( "CMYK" ) )
87         {
88             throw new IOException JavaDoc( "Error: Unknown colorspace " + destColorspace );
89         }
90         List JavaDoc pagesList = inputFile.getDocumentCatalog().getAllPages();
91
92         PDPage currentPage = null;
93         PDFStreamParser parser = null;
94         List JavaDoc pageTokens = null;
95         List JavaDoc editedPageTokens = null;
96
97         for(int pageCounter = 0; pageCounter < pagesList.size(); pageCounter++) // For each document page
98
{
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 JavaDoc();
105
106             for( int counter = 0; counter < pageTokens.size(); counter++) // For each page token
107
{
108                 Object JavaDoc token = pageTokens.get( counter );
109                 if( token instanceof PDFOperator ) // Test if PDFOperator
110
{
111                     PDFOperator tokenOperator = (PDFOperator)token;
112
113                     if(tokenOperator.getOperation().equals("rg")) // Test if "rg" Operator.
114
{
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")) // Test if "rg" Operator.
122
{
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")) // Test if "rg" Operator.
130
{
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")) // Test if "rg" Operator.
138
{
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 // Test if PDFOperator
151
{
152                     editedPageTokens.add( token );
153                 }
154             } // For each page token
155

156             // We replace original page content by the edited one.
157
PDStream updatedPageContents = new PDStream(inputFile);
158             ContentStreamWriter contentWriter = new ContentStreamWriter( updatedPageContents.createOutputStream() );
159             contentWriter.writeTokens( editedPageTokens );
160             currentPage.setContents( updatedPageContents );
161
162         } // For each document page
163
}
164     
165     private void replaceRGBTokensWithCMYKTokens( List JavaDoc editedPageTokens,
166                                                  List JavaDoc pageTokens,
167                                                  int counter,
168                                                  Hashtable JavaDoc colorEquivalents )
169     {
170 // Get current RGB color.
171
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         //remove the RGB components that are already part of the editedPageTokens list
200
editedPageTokens.remove( editedPageTokens.size() -1 );
201         editedPageTokens.remove( editedPageTokens.size() -1 );
202         editedPageTokens.remove( editedPageTokens.size() -1 );
203
204         // Add the new CMYK color
205
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 JavaDoc editedPageTokens,
212                                                   List JavaDoc pageTokens,
213                                                   int counter,
214                                                   Hashtable JavaDoc colorEquivalents )
215     {
216 // Get current RGB color.
217
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         //remove the Gray components that are already part of the editedPageTokens list
240
editedPageTokens.remove( editedPageTokens.size() -1 );
241
242         // Add the new CMYK color
243
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         //
252
// RGB->CMYK from From
253
// http://en.wikipedia.org/wiki/Talk:CMYK_color_model
254
//
255
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 JavaDoc string )
269     {
270         String JavaDoc[] 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     /**
280      * Infamous main method.
281      *
282      * @param args Command line arguments, should be one and a reference to a file.
283      *
284      * @throws Exception If there is an error parsing the document.
285      */

286     public static void main( String JavaDoc[] args ) throws Exception JavaDoc
287     {
288         String JavaDoc password = "";
289         String JavaDoc inputFile = null;
290         String JavaDoc outputFile = null;
291         String JavaDoc destColorspace = "CMYK";
292
293         Pattern JavaDoc colorEquivalentPattern = Pattern.compile(
294             "^(.*):\\((.*)\\)" +
295             "=(.*):\\((.*)\\)$");
296         Matcher JavaDoc colorEquivalentMatcher = null;
297
298         //key= value=java.awt.Color
299
Hashtable JavaDoc colorEquivalents = new Hashtable JavaDoc();
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 JavaDoc srcColorSpace = colorEquivalentMatcher.group(1);
335               String JavaDoc srcColorvalues = colorEquivalentMatcher.group(2);
336               String JavaDoc destColorSpace = colorEquivalentMatcher.group(3);
337               String JavaDoc 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( "" ) )//they supplied the wrong password
386
{
387                         System.err.println( "Error: The supplied password is incorrect." );
388                         System.exit( 2 );
389                     }
390                     else
391                     {
392                         //they didn't suppply a password and the default of "" was wrong.
393
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     /**
415      * This will print the usage requirements and exit.
416      */

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     /**
439      *
440      *
441      */

442     private static class ColorSpaceInstance
443     {
444         private String JavaDoc colorspace = null;
445         private int[] colorspaceValues = null;
446         
447         /**
448          * {@inheritDoc}
449          */

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         /**
461          * {@inheritDoc}
462          */

463         public boolean equals( Object JavaDoc 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