重写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;
}
}