重写itext的PdfCopy类 package com.lowagie.text.pdf; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import com.lowagie.text.Document; import com.lowagie.text.DocumentException; import com.lowagie.text.ExceptionConverter; /** * also copy ocgs from imported pages; * the optional Configs item of OCProperties (an array of alternate optional content configurations) is ignored (no copy) * * parts of the code copied from PdfCopy * 19.5.2007 * * @author Jürgen Hofmann (j.hofmann at processmc dot de) * */ public class PdfCopyWithOcg extends PdfCopy { // merge ocgs with equal display name. // normally each ocg from different documents has its own display name in the layer-tab of acrobat reader. // if set true, ocgs with equal names are combined - in this case, the configuration of the first ocg is used private boolean mergeEqualNames = false; private final HashMap currentOcgs = new HashMap(); // original reference and new reference of ocg private final HashMap ocgNames = new HashMap(); // display name of ocg, used to merge ocg with equal display names private final HashSet orderSet = new HashSet(); // holds ocgs of the new ORDER dictionary private PdfDictionary dDictionary = null; // the new D dictionary public static void main(String[] args) { // we create a PDF file try { // we create a PdfReader object //PdfReader reader = new PdfReader("C:\\temp\\automatic.pdf"); PdfReader reader = new PdfReader("C:\\temp\\phil04.pdf"); // step 1 Document document = new Document(reader.getPageSizeWithRotation(1)); // step 2 PdfCopyWithOcg copy = new PdfCopyWithOcg(document, new FileOutputStream( "C:\\temp\\test_out.pdf")); // step 3 document.open(); // step 4 // copy.setMergeEqualNames(true); System.out.println("Tampered? " + reader.isTampered()); /* PdfImportedPage page; int n = reader.getNumberOfPages(); for (int i = 1; i < 5; ++i) { page = copy.getImportedPage(reader, i); copy.addPage(page); } reader = new PdfReader("C:\\temp\\phil5.pdf"); n = reader.getNumberOfPages(); for (int i = 1; i < 3; ++i) { page = copy.getImportedPage(reader, i); copy.addPage(page); } */ reader = new PdfReader("C:\\temp\\contentgroups.pdf"); copy.addPage(copy.getImportedPage(reader, 1)); reader = new PdfReader("C:\\temp\\layers.pdf"); copy.addPage(copy.getImportedPage(reader, 1)); reader = new PdfReader("C:\\temp\\nestedlayers.pdf"); copy.addPage(copy.getImportedPage(reader, 1)); reader = new PdfReader("C:\\temp\\optionalcontent.pdf"); copy.addPage(copy.getImportedPage(reader, 1)); reader = new PdfReader("C:\\temp\\orderedlayers.pdf"); copy.addPage(copy.getImportedPage(reader, 1)); System.out.println("Tampered? " + reader.isTampered()); // step 5 document.close(); } catch (IOException e) { e.printStackTrace(); } catch (DocumentException e) { e.printStackTrace(); } } /** * Constructor * @param document * @param os outputstream */ public PdfCopyWithOcg(Document document, OutputStream os) throws DocumentException { super(document, os); OCProperties = new PdfOCProperties(); } /* * the getCatalog method is part of PdfCopy. * we wrap this so that we can add the OCProperties */ @Override protected PdfDictionary getCatalog(PdfIndirectReference rootObj) { try { PdfDictionary theCat = pdf.getCatalog(rootObj); if (acroForm != null) theCat.put(PdfName.ACROFORM, acroForm); // added if(OCProperties != null) theCat.put(PdfName.OCPROPERTIES, OCProperties); if (newBookmarks == null || newBookmarks.isEmpty()) return theCat; PdfDictionary top = new PdfDictionary(); PdfIndirectReference topRef = getPdfIndirectReference(); Object kids[] = SimpleBookmark.iterateOutlines(this, topRef, newBookmarks, false); top.put(PdfName.FIRST, (PdfIndirectReference)kids[0]); top.put(PdfName.LAST, (PdfIndirectReference)kids[1]); top.put(PdfName.COUNT, new PdfNumber(((Integer)kids[2]).intValue())); addToBody(top, topRef); theCat.put(PdfName.OUTLINES, topRef); return theCat; } catch (IOException e) { throw new ExceptionConverter(e); } } /* * the PdfIndirectReference method is part of PdfCopy. * we wrap this so that we can extend it */ @Override protected PdfIndirectReference copyIndirect(PRIndirectReference in) throws IOException, BadPdfFormatException { PdfIndirectReference theRef; RefKey key = new RefKey(in); IndirectReferences iRef = (IndirectReferences)indirects.get(key); if (iRef != null) { theRef = iRef.getRef(); if (iRef.getCopied()) return theRef; } else { theRef = body.getPdfIndirectReference(); iRef = new IndirectReferences(theRef); indirects.put(key, iRef); } PdfObject obj = PdfReader.getPdfObjectRelease(in); if (obj != null && obj.isDictionary()) { PdfName type = (PdfName)((PdfDictionary)obj).get(PdfName.TYPE); if (type != null && PdfName.PAGE.equals(type)) return theRef; } obj = copyObject(obj); // *************** OCG start ************** //PDF Reference Version 1.7, 4.10.2, optional content in content streams: // ... // The property list associated with the marked content specifies either an optional // content group or optional content membership dictionary to which the content // belongs. Because a group must be an indirect object and a membership dictionary // contains references to indirect objects,... // // save all ocgs of the imported page, so we can reconstruct a valid ocproperties dictionary if (obj != null && obj.isDictionary() ) { PdfName type = (PdfName)((PdfDictionary)obj).get(PdfName.TYPE); if (type != null && PdfName.OCG.equals(type)) { // referenced Object is OCG PdfString name = (PdfString)((PdfDictionary)obj).get(PdfName.NAME); if (mergeEqualNames && name != null) { IndirectReferences ref = (IndirectReferences)ocgNames.get(name.toString()); if (ref == null) ocgNames.put(name.toString(), iRef); else { currentOcgs.put(key,ref); return ref.getRef(); } } currentOcgs.put(key,iRef); } } // ************** OCG end **************** iRef.setCopied(); addToBody(obj, theRef); return theRef; } /** * Add an imported page to our output * @param iPage an imported page * @throws IOException, BadPdfFormatException */ @Override public void addPage(PdfImportedPage iPage) throws IOException, BadPdfFormatException { int pageNum = setFromIPage(iPage); // initialize current OCGs currentOcgs.clear(); // initialize OCProperties dictionary if (dDictionary == null) { dDictionary = new PdfDictionary(); dDictionary.put(PdfName.ORDER, new PdfArray()); dDictionary.put(PdfName.AS, new PdfArray()); PdfArray ocgs = new PdfArray(); OCProperties.put(PdfName.D, dDictionary); OCProperties.put(PdfName.OCGS, ocgs); } PdfDictionary thePage = reader.getPageN(pageNum); PRIndirectReference origRef = reader.getPageOrigRef(pageNum); reader.releasePage(pageNum); RefKey key = new RefKey(origRef); PdfIndirectReference pageRef; IndirectReferences iRef = (IndirectReferences)indirects.get(key); if (iRef != null && !iRef.getCopied()) { pageReferences.add(iRef.getRef()); iRef.setCopied(); } pageRef = getCurrentPage(); if (iRef == null) { iRef = new IndirectReferences(pageRef); indirects.put(key, iRef); } iRef.setCopied(); PdfDictionary newPage = copyDictionary(thePage); root.addPage(newPage); ++currentPageNumber; // set OCProperties updateOCProperties(reader); } /* * set new OCProperties * * from PDF-Reference 1.7 (section 4.10): * OCG: Type [name (OCG), required] * Name [string, required] * Intent [name or array (View, Design), optional] * Usage [dict, optional] * | - CreatorInfo [dict, optional] * | - Language [dict, optional] * | - Export [dict, optional] * | - Zoom [dict, optional] * | - Print [dict, optional] * | - View [dict, optional] * | - User [dict, optional] * | - PageElement [dict, optional] * * OCProperties - OCGs [array,required] * | - Configs [array, optional] (see D) **** not yet handled **** * | - D [dict, required] * | - Name [text, optional] * | - Creator [text, optional] * | - BaseState [name (ON, OFF, Unchanged), optional] * | - ON [array, optional] * | - OFF [array, optional] * | - Intent [name or array (View, Design, All; or combinations), optional] * | - AS [array, optional] * | - Event [name (View,Print,Export), required] * | - OCGs [array, optional] * | - Category [array, required] (array of names corresponding to a usage dict (View Print Export Zoom User Language) * | - ORDER [array, optional] * | - ocgs or arrays for nested ocgs with (optional) first entry as string * | - ListMode [name (AllPages, VisiblePages), optional] * | - RBGroups [array, optional] * | - Locked [array, optional] (PDF 1.6) * * */ private void updateOCProperties(PdfReader reader) throws IOException, BadPdfFormatException { PdfObject obj; PdfDictionary catalog = reader.getCatalog(); obj = PdfReader.getPdfObject(catalog.get(PdfName.OCPROPERTIES)); if (obj == null || !obj.isDictionary()) return; PdfDictionary ocproperties = (PdfDictionary)obj; obj = PdfReader.getPdfObject(ocproperties.get(PdfName.D)); if (obj == null || !obj.isDictionary()) return; PdfDictionary d = (PdfDictionary)obj; // Name obj = PdfReader.getPdfObject(d.get(PdfName.NAME)); if (obj != null && obj.isString()) if (dDictionary.get(PdfName.NAME) == null) dDictionary.put(PdfName.NAME, obj); // Creator obj = PdfReader.getPdfObject(d.get(PdfName.CREATOR)); if (obj != null && obj.isString()) if (dDictionary.get(PdfName.CREATOR) == null) dDictionary.put(PdfName.CREATOR, obj); // ON if (getBaseState(dDictionary) != null && !getBaseState(dDictionary).equals(PdfName.ON)) addOcgs(PdfName.ON,d); // OFF if (getBaseState(dDictionary) == null || !getBaseState(dDictionary).equals(PdfName.OFF)) addOcgs(PdfName.OFF,d); // Locked (since PDF 1.6) addOcgs(new PdfName("Locked"),d); //Intent (name or array) obj = PdfReader.getPdfObject(d.get(PdfName.INTENT)); if (obj != null && obj.isName()) if (dDictionary.get(PdfName.INTENT) == null) dDictionary.put(PdfName.INTENT, obj); // ListMode obj = PdfReader.getPdfObject(d.get(PdfName.LISTMODE)); if (obj != null && obj.isName()) if (dDictionary.get(PdfName.LISTMODE) == null) dDictionary.put(PdfName.LISTMODE, obj); // ORDER PdfArray order = null; obj = PdfReader.getPdfObject(d.get(PdfName.ORDER)); if (obj != null && obj.isArray()) order = (PdfArray)obj; else order = new PdfArray(); // iterate over all Order items Iterator it = order.listIterator(); while (it.hasNext()) { PdfObject item = (PdfObject)it.next(); if (item != null && item.isArray() && ((PdfArray)item).size() > 0) { // array of ocgs PdfArray subArray = getOrderArray((PdfArray)item); if (subArray.size() > 0) ((PdfArray)(dDictionary.get(PdfName.ORDER))).add(subArray); } if (item != null && item.isIndirect()) if (currentOcgs.containsKey(new RefKey((PdfIndirectReference)item))) { IndirectReferences ocg = (IndirectReferences)currentOcgs.get(new RefKey((PdfIndirectReference)item)); if (ocg != null && !orderSet.contains(ocg)) { ((PdfArray)(dDictionary.get(PdfName.ORDER))).add(ocg.getRef()); orderSet.add(ocg); } } } // AS PdfArray as = null; obj = PdfReader.getPdfObject(d.get(PdfName.AS)); if (obj != null && obj.isArray()) as = (PdfArray)obj; else as = new PdfArray(); it = as.listIterator(); while (it.hasNext()) { // iterate over all AS entries obj = (PdfObject)it.next(); PdfDictionary asItem = (PdfDictionary)PdfReader.getPdfObject(obj); PdfDictionary newAsEntry = new PdfDictionary(); obj = asItem.get(PdfName.OCGS); PdfArray ocgs = (PdfArray)PdfReader.getPdfObject(obj); if (ocgs == null || ocgs.size() == 0) continue; obj = asItem.get(PdfName.EVENT); PdfName event = (PdfName)PdfReader.getPdfObject(obj); PdfArray newOcgs = new PdfArray(); Iterator itOcg = ocgs.listIterator(); while (itOcg.hasNext()) { PdfIndirectReference ocg = (PdfIndirectReference)itOcg.next(); IndirectReferences newOcg = (IndirectReferences)currentOcgs.get(new RefKey(ocg)); // ocg may not be included in current page if (newOcg != null && !newAsContainsOcgAndEvent(newOcg.getRef(),event)) if (currentOcgs.containsKey(new RefKey(ocg))) newOcgs.add(newOcg.getRef()); } if (newOcgs.size() == 0) continue; obj = asItem.get(PdfName.CATEGORY); //(Zoom Print View User PageElement Export Language CreaterInfo?) PdfArray category = (PdfArray)copyObject(obj); if (newOcgs.size() > 0) { newAsEntry.put(PdfName.EVENT, event); newAsEntry.put(PdfName.OCGS, newOcgs); newAsEntry.put(PdfName.CATEGORY, category); ((PdfArray)(dDictionary.get(PdfName.AS))).add(newAsEntry); } } // save all OCGS Iterator ocgIt = currentOcgs.values().iterator(); while (ocgIt.hasNext()) { IndirectReferences ocg = (IndirectReferences)ocgIt.next(); if (!((PdfArray)(OCProperties.get(PdfName.OCGS))).contains(ocg.getRef())) ((PdfArray)(OCProperties.get(PdfName.OCGS))).add(ocg.getRef()); } // RBGroups PdfArray rbGroups = null; obj = PdfReader.getPdfObject(d.get(PdfName.RBGROUPS)); if (obj != null && obj.isArray()) rbGroups = (PdfArray)obj; else rbGroups = new PdfArray(); it = rbGroups.listIterator(); while (it.hasNext()) { PdfObject item = (PdfObject)it.next(); if (item != null && item.isArray() && ((PdfArray)item).size() > 0) { PdfArray subArray = getSubArray((PdfArray)item); if (subArray.size() > 0) { PdfArray array = (PdfArray)(dDictionary.get(PdfName.RBGROUPS)); if (array == null) { array = new PdfArray(); dDictionary.put(PdfName.RBGROUPS, array); } array.add(subArray); } } } } /* * add array of ocgs from original D to new D, if not yet set */ private void addOcgs(PdfName pdfName, PdfDictionary dDict) { PdfArray array = null; PdfObject obj = PdfReader.getPdfObject(dDict.get(pdfName)); if (obj != null && obj.isArray()) array = (PdfArray)obj; else array = new PdfArray(); Iterator it = array.listIterator(); while (it.hasNext()) { PdfObject object = (PdfObject)it.next(); if (object != null && object.isIndirect()) if (currentOcgs.containsKey(new RefKey((PdfIndirectReference)object))) { PdfIndirectReference ocg = ((IndirectReferences)currentOcgs.get(new RefKey((PdfIndirectReference)object))).getRef(); if (ocg != null && (dDictionary.get(pdfName)== null || !((PdfArray)dDictionary.get(pdfName)).contains(ocg))) { if (dDictionary.get(pdfName) == null) dDictionary.put(pdfName, new PdfArray()); ((PdfArray)(dDictionary.get(pdfName))).add(ocg); } } } } /* * check if given ocg-event combination already exists in new AS */ private boolean newAsContainsOcgAndEvent(PdfIndirectReference ocg, PdfName event) { PdfArray as = (PdfArray)dDictionary.get(PdfName.AS); if (as == null || as.size() == 0) return false; Iterator it = as.listIterator(); while (it.hasNext()) { PdfDictionary asEntry = (PdfDictionary)it.next(); if (asEntry == null) continue; PdfArray ocgs = (PdfArray)asEntry.get(PdfName.OCGS); if (ocgs == null || ocgs.size() == 0) continue; PdfName ev = (PdfName)asEntry.get(PdfName.EVENT); if (ev == null) continue; if (ocgs.contains(ocg) && ev.equals(event)) return true; } return false; } /* * evaluate sub arrays of Order array */ private PdfArray getOrderArray(PdfArray array) { PdfArray newArray = new PdfArray(); if (array != null && array.size() > 0) { int i = 0; String name = null; if (array.getPdfObject(i) != null && array.getPdfObject(i).isString()) { // first entry may be String name = array.getPdfObject(i).toString(); ++i; } for(;i < array.size(); ++i) { PdfObject entry = array.getPdfObject(i); if (entry != null && entry.isIndirect() && currentOcgs.containsKey(new RefKey((PdfIndirectReference)entry))) { IndirectReferences ocg = (IndirectReferences)currentOcgs.get(new RefKey((PdfIndirectReference)entry)); if (!orderSet.contains(ocg)) { newArray.add(ocg.getRef()); orderSet.add(ocg); } } if (entry != null && entry.isArray()) { PdfArray subArray = getOrderArray((PdfArray)entry); if (subArray.size() > 0) newArray.add(subArray); } } if (newArray.size() > 0 && name != null) newArray.addFirst(new PdfString(name)); } return newArray; } /* * get copy of PdfArray (of ocgs and other PdfArrays...); only copy ocgs of the current page */ private PdfArray getSubArray(PdfArray array) { PdfArray newArray = new PdfArray(); if (array != null && array.size() > 0) { int i = 0; for(;i < array.size(); ++i) { PdfObject entry = array.getPdfObject(i); if (entry != null && entry.isIndirect() && currentOcgs.containsKey(new RefKey((PdfIndirectReference)entry))) { IndirectReferences ocg = (IndirectReferences)currentOcgs.get(new RefKey((PdfIndirectReference)entry)); newArray.add(ocg.getRef()); } if (entry != null && entry.isArray()) { PdfArray subArray = getSubArray((PdfArray)entry); if (subArray.size() > 0) newArray.add(subArray); } } } return newArray; } /* * return BaseState of dict */ private PdfName getBaseState(PdfDictionary dict) { PdfObject obj = dict.get(new PdfName("BaseState")); if (obj != null && obj.isName()) return (PdfName)obj; return null; } /** * merge ocgs with equal display names. * the configuration of the first ocg is used. * @param mergeEqualNames */ public void setMergeEqualNames(boolean mergeEqualNames) { this.mergeEqualNames = mergeEqualNames; } }