1 31 package org.pdfbox; 32 33 import org.pdfbox.cos.COSArray; 34 import org.pdfbox.cos.COSBase; 35 import org.pdfbox.cos.COSDictionary; 36 import org.pdfbox.cos.COSName; 37 import org.pdfbox.cos.COSStream; 38 import org.pdfbox.cos.COSInteger; 39 import org.pdfbox.exceptions.COSVisitorException; 40 import org.pdfbox.pdfparser.PDFParser; 41 import org.pdfbox.pdfwriter.COSWriter; 42 43 import org.pdfbox.pdmodel.PDDocument; 44 import org.pdfbox.pdmodel.PDDocumentCatalog; 45 import org.pdfbox.pdmodel.PDPage; 46 import org.pdfbox.pdmodel.PDResources; 47 48 import java.io.FileInputStream ; 49 import java.io.FileOutputStream ; 50 import java.io.IOException ; 51 import java.io.InputStream ; 52 import java.io.ByteArrayOutputStream ; 53 import java.io.OutputStream ; 54 55 import java.util.ArrayList ; 56 import java.util.Iterator ; 57 import java.util.List ; 58 import java.util.TreeMap ; 59 import java.util.Map ; 60 61 78 public class Overlay 79 { 80 83 public static final COSName XOBJECT = COSName.getPDFName("XObject"); 84 87 public static final COSName PROC_SET = COSName.getPDFName("ProcSet"); 88 91 public static final COSName EXT_G_STATE = COSName.getPDFName("ExtGState" ); 92 93 private List layoutPages = new ArrayList (10); 94 95 private PDDocument pdfOverlay; 96 private PDDocument pdfDocument; 97 private int pageCount = 0; 98 private COSStream saveGraphicsStateStream; 99 private COSStream restoreGraphicsStateStream; 100 101 111 public static void main( String [] args ) throws IOException , COSVisitorException 112 { 113 if( args.length != 3 ) 114 { 115 usage(); 116 System.exit(1); 117 } 118 else 119 { 120 PDDocument overlay = null; 121 PDDocument pdf = null; 122 123 try 124 { 125 overlay = getDocument( args[0] ); 126 pdf = getDocument( args[1] ); 127 Overlay overlayer = new Overlay(); 128 overlayer.overlay( overlay, pdf ); 129 writeDocument( pdf, args[2] ); 130 } 131 finally 132 { 133 if( overlay != null ) 134 { 135 overlay.close(); 136 } 137 if( pdf != null ) 138 { 139 pdf.close(); 140 } 141 } 142 } 143 } 144 145 private static void writeDocument( PDDocument pdf, String filename ) throws IOException , COSVisitorException 146 { 147 FileOutputStream output = null; 148 COSWriter writer = null; 149 try 150 { 151 output = new FileOutputStream ( filename ); 152 writer = new COSWriter( output ); 153 writer.write( pdf ); 154 } 155 finally 156 { 157 if( writer != null ) 158 { 159 writer.close(); 160 } 161 if( output != null ) 162 { 163 output.close(); 164 } 165 } 166 } 167 168 private static PDDocument getDocument( String filename ) throws IOException 169 { 170 FileInputStream input = null; 171 PDFParser parser = null; 172 PDDocument result = null; 173 try 174 { 175 input = new FileInputStream ( filename ); 176 parser = new PDFParser( input ); 177 parser.parse(); 178 result = parser.getPDDocument(); 179 } 180 finally 181 { 182 if( input != null ) 183 { 184 input.close(); 185 } 186 } 187 return result; 188 } 189 190 private static void usage() 191 { 192 System.err.println( "usage: java " + Overlay.class.getName() + "<overlay.pdf> <document.pdf> <result.pdf>" ); 193 } 194 195 198 private static class LayoutPage 199 { 200 private final COSBase contents; 201 private final COSDictionary res; 202 private final Map objectNameMap; 203 204 211 public LayoutPage(COSBase contentsValue, COSDictionary resValue, Map objectNameMapValue) 212 { 213 contents = contentsValue; 214 res = resValue; 215 objectNameMap = objectNameMapValue; 216 } 217 } 218 219 231 public PDDocument overlay( PDDocument overlay, PDDocument destination ) throws IOException 232 { 233 pdfOverlay = overlay; 234 pdfDocument = destination; 235 236 PDDocumentCatalog overlayCatalog = pdfOverlay.getDocumentCatalog(); 237 collectLayoutPages( overlayCatalog.getAllPages() ); 238 239 COSDictionary saveGraphicsStateDic = new COSDictionary(); 240 saveGraphicsStateStream = new COSStream( saveGraphicsStateDic, pdfDocument.getDocument().getScratchFile() ); 241 OutputStream saveStream = saveGraphicsStateStream.createUnfilteredStream(); 242 saveStream.write( " q\n".getBytes() ); 243 saveStream.flush(); 244 245 restoreGraphicsStateStream = new COSStream( saveGraphicsStateDic, pdfDocument.getDocument().getScratchFile() ); 246 OutputStream restoreStream = restoreGraphicsStateStream.createUnfilteredStream(); 247 restoreStream.write( " Q\n".getBytes() ); 248 restoreStream.flush(); 249 250 251 PDDocumentCatalog pdfCatalog = pdfDocument.getDocumentCatalog(); 252 processPages( pdfCatalog.getAllPages() ); 253 254 return pdfDocument; 255 } 256 257 private void collectLayoutPages( List pages) throws IOException 258 { 259 Iterator pagesIter = pages.iterator(); 260 while( pagesIter.hasNext() ) 261 { 262 PDPage page = (PDPage)pagesIter.next(); 263 COSBase contents = page.getCOSDictionary().getDictionaryObject( COSName.CONTENTS ); 264 PDResources resources = page.findResources(); 265 if( resources == null ) 266 { 267 resources = new PDResources(); 268 page.setResources( resources ); 269 } 270 COSDictionary res = resources.getCOSDictionary(); 271 272 if( contents instanceof COSStream ) 273 { 274 COSStream stream = (COSStream) contents; 275 Map objectNameMap = new TreeMap (); 276 stream = makeUniqObjectNames(objectNameMap, stream); 277 278 layoutPages.add(new LayoutPage(stream, res, objectNameMap)); 279 } 280 else if( contents instanceof COSArray ) 281 { 282 throw new UnsupportedOperationException ("Layout pages with COSArray currently not supported."); 283 } 285 else 286 { 287 throw new IOException ( "Contents are unknown type:" + contents.getClass().getName() ); 288 } 289 } 290 } 291 292 private COSStream makeUniqObjectNames(Map objectNameMap, COSStream stream) throws IOException 293 { 294 ByteArrayOutputStream baos = new ByteArrayOutputStream (10240); 295 296 byte[] buf = new byte[10240]; 297 int read; 298 InputStream is = stream.getUnfilteredStream(); 299 while ((read = is.read(buf)) > -1) 300 { 301 baos.write(buf, 0, read); 302 } 303 304 buf = baos.toByteArray(); 305 baos = new ByteArrayOutputStream (buf.length + 100); 306 StringBuffer sbObjectName = new StringBuffer (10); 307 boolean bInObjectIdent = false; 308 boolean bInText = false; 309 boolean bInEscape = false; 310 for (int i = 0; i<buf.length; i++) 311 { 312 byte b = buf[i]; 313 314 if (!bInEscape) 315 { 316 if (!bInText && b == '(') 317 { 318 bInText = true; 319 } 320 if (bInText && b == ')') 321 { 322 bInText = false; 323 } 324 if (b == '\\') 325 { 326 bInEscape = true; 327 } 328 329 if (!bInText && !bInEscape) 330 { 331 if (b == '/') 332 { 333 bInObjectIdent = true; 334 } 335 else if (bInObjectIdent && Character.isWhitespace((char) b)) 336 { 337 bInObjectIdent = false; 338 339 342 String objectName = sbObjectName.toString().substring(1); 343 String newObjectName = objectName + "overlay"; 344 baos.write('/'); 345 baos.write(newObjectName.getBytes()); 346 347 objectNameMap.put(objectName, COSName.getPDFName(newObjectName)); 348 349 sbObjectName.delete(0, sbObjectName.length()); 350 } 351 } 352 353 if (bInObjectIdent) 354 { 355 sbObjectName.append((char) b); 356 continue; 357 } 358 } 359 else 360 { 361 bInEscape = false; 362 } 363 364 baos.write(b); 365 } 366 367 COSDictionary streamDict = new COSDictionary(); 368 streamDict.setItem(COSName.LENGTH, new COSInteger(baos.size())); 369 COSStream output = new COSStream(streamDict, pdfDocument.getDocument().getScratchFile()); 370 output.setFilters(stream.getFilters()); 371 OutputStream os = output.createUnfilteredStream(); 372 baos.writeTo(os); 373 os.close(); 374 375 return output; 376 } 377 378 private void processPages( List pages ) throws IOException 379 { 380 Iterator pageIter = pages.iterator(); 381 while( pageIter.hasNext() ) 382 { 383 PDPage page = (PDPage)pageIter.next(); 384 COSDictionary pageDictionary = page.getCOSDictionary(); 385 COSBase contents = pageDictionary.getDictionaryObject( COSName.CONTENTS ); 386 if( contents instanceof COSStream ) 387 { 388 COSStream contentsStream = (COSStream)contents; 389 pageCount++; 391 392 COSArray array = new COSArray(); 393 394 array.add(contentsStream); 395 396 mergePage( array, page ); 397 398 pageDictionary.setItem(COSName.CONTENTS, array); 399 } 400 else if( contents instanceof COSArray ) 401 { 402 COSArray contentsArray = (COSArray)contents; 403 404 mergePage( contentsArray, page ); 405 } 406 else 407 { 408 throw new IOException ( "Contents are unknown type:" + contents.getClass().getName() ); 409 } 410 } 411 } 412 413 private void mergePage(COSArray array, PDPage page ) 414 { 415 int layoutPageNum = pageCount % layoutPages.size(); 416 LayoutPage layoutPage = (LayoutPage) layoutPages.get(layoutPageNum); 417 PDResources resources = page.findResources(); 418 if( resources == null ) 419 { 420 resources = new PDResources(); 421 page.setResources( resources ); 422 } 423 COSDictionary docResDict = resources.getCOSDictionary(); 424 COSDictionary layoutResDict = layoutPage.res; 425 mergeArray(PROC_SET, docResDict, layoutResDict); 426 mergeDictionary(COSName.FONT, docResDict, layoutResDict, layoutPage.objectNameMap); 427 mergeDictionary(XOBJECT, docResDict, layoutResDict, layoutPage.objectNameMap); 428 mergeDictionary(EXT_G_STATE, docResDict, layoutResDict, layoutPage.objectNameMap); 429 430 array.add(0, saveGraphicsStateStream ); 438 array.add( restoreGraphicsStateStream ); 439 array.add(layoutPage.contents); 440 } 441 442 448 private void mergeDictionary(COSName name, COSDictionary dest, COSDictionary source, Map objectNameMap) 449 { 450 COSDictionary destDict = (COSDictionary) dest.getDictionaryObject(name); 451 COSDictionary sourceDict = (COSDictionary) source.getDictionaryObject(name); 452 453 if (destDict == null) 454 { 455 destDict = new COSDictionary(); 456 dest.setItem(name, destDict); 457 } 458 if( sourceDict != null ) 459 { 460 461 Iterator iterKeys = sourceDict.keyList().iterator(); 462 while (iterKeys.hasNext()) 463 { 464 COSName key = (COSName) iterKeys.next(); 465 COSName mappedKey = (COSName) objectNameMap.get(key.getName()); 466 if (mappedKey == null) 467 { 468 continue; 470 } 471 472 destDict.setItem(mappedKey, sourceDict.getItem(key)); 473 } 474 } 475 } 476 477 483 private void mergeArray(COSName name, COSDictionary dest, COSDictionary source) 484 { 485 COSArray destDict = (COSArray) dest.getDictionaryObject(name); 486 COSArray sourceDict = (COSArray) source.getDictionaryObject(name); 487 488 if (destDict == null) 489 { 490 destDict = new COSArray(); 491 dest.setItem(name, destDict); 492 } 493 494 for (int sourceDictIdx = 0; sourceDict != null && sourceDictIdx<sourceDict.size(); sourceDictIdx++) 495 { 496 COSBase key = sourceDict.get(sourceDictIdx); 497 if (key instanceof COSName) 498 { 499 COSName keyname = (COSName) key; 500 501 boolean bFound = false; 502 for (int destDictIdx = 0; destDictIdx<destDict.size(); destDictIdx++) 503 { 504 COSBase destkey = destDict.get(destDictIdx); 505 if (destkey instanceof COSName) 506 { 507 COSName destkeyname = (COSName) destkey; 508 if (destkeyname.equals(keyname)) 509 { 510 bFound = true; 511 break; 512 } 513 } 514 } 515 if (!bFound) 516 { 517 destDict.add(keyname); 518 } 519 } 520 } 521 } 522 } | Popular Tags |