From f41fb52ffdf58199de8b7c399fc97a96c961a25d Mon Sep 17 00:00:00 2001 From: afitzek Date: Mon, 9 Jan 2012 11:23:48 +0000 Subject: Fixing Problem with multiple signatures in tagged PDFs git-svn-id: https://joinup.ec.europa.eu/svn/pdf-as/trunk@893 7b5415b0-85f9-ee4d-85bd-d5d0c3b42d1c --- .../wag/egov/egiz/pdf/StructContentHelper.java | 205 +++++++++++---------- 1 file changed, 108 insertions(+), 97 deletions(-) diff --git a/src/main/java/at/knowcenter/wag/egov/egiz/pdf/StructContentHelper.java b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/StructContentHelper.java index f8cb09e..7ca5a0a 100644 --- a/src/main/java/at/knowcenter/wag/egov/egiz/pdf/StructContentHelper.java +++ b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/StructContentHelper.java @@ -53,23 +53,23 @@ import com.lowagie.text.pdfas.UrlInTextFinder; /** * Helper class for writing the structure hierarchy of the signature elements. * Everything is written with the PdfObject low level API because there is no better support. - * The structured content is only written for structured (==tagged) input documents. The methods have to be called in the + * The structured content is only written for structured (==tagged) input documents. The methods have to be called in the * defined order. The object cannot be reused for several signatures.
* See pdf spec "Logical Structure" for details.
* The struct writing could be a little more abstracted, but this would include quite some itext extension work. And like this it * fits better to PDF-AS / wprinz coding style :-( * @author exthex - * + * */ public class StructContentHelper implements StructContentWriter { private static final Logger logger = Logger.getLogger(StructContentHelper.class); - + private static final String SIGBLOCK_STRUCT_TYPE = "P"; private static final PdfName PARENTTREENEXTKEY = new PdfName("ParentTreeNextKey"); - private static final String ALT_TEXT_DEFAULT = "Signaturbildmarke"; - private final static String ALT_TEXT_CONF_KEY = "sigLogoAltText"; - - + private static final String ALT_TEXT_DEFAULT = "Signaturbildmarke"; + private final static String ALT_TEXT_CONF_KEY = "sigLogoAltText"; + + private int nextMcid = 0; /** * MCID value used for the sigblock marked contend identifier @@ -94,7 +94,7 @@ public class StructContentHelper implements StructContentWriter { private PdfDictionary page; private PdfNumber parentTreeNextKey = null; private PdfNumber annotationParentTreeKey = null; - + /** * Temporary save a pos */ @@ -103,21 +103,21 @@ public class StructContentHelper implements StructContentWriter { * Cell position of the signature verify link overlay */ private Rectangle verifyLinkCellPos = null; - + /** * Kids array (K) of the StructTreeRoot */ private PdfArray structTreeRootKids = null; /** - * Entry in the ParentTree.Nums array used for sigtable structs + * Entry in the ParentTree.Nums array used for sigtable structs */ private PdfArray mainParentTreeNumEntry; /** - * Create new helper for one signature, and bind it to {@link StructContentWriterHolder} + * Create new helper for one signature, and bind it to {@link StructContentWriterHolder} * for thread local access from itext. - * + * * @param stamper * @param content * @param pageNr @@ -136,44 +136,55 @@ public class StructContentHelper implements StructContentWriter { public void removeCurrent() { StructContentWriterHolder.removeThreadLocalWriter(); } - + /** * Prepare structured content for signature block. This method initializes the whole StructTreeRoot stuff. - * @param sigBlockObj + * @param sigBlockObj * @throws PresentableException */ void prepareStructData(PdfTemplate sigBlockObj) throws PresentableException { try { - checkTagging(); + checkTagging(); if (!isTagged) { return; } - + doAnnoTabOrder(); PdfDictionary structTreeRoot = getStructTreeRoot(); stamperImp.markUsed(structTreeRoot); - PdfArray parentTreeNums = getParentTreeNums(); - + PdfArray parentTreeNums = getParentTreeNums(); + PdfNumber structParentsNr = page.getAsNumber(PdfName.STRUCTPARENTS); // read StructParents entry from current page mainParentTreeNumEntry = obtainParentTreeEntry(structTreeRoot, parentTreeNums, structParentsNr, sigBlockObj); - + nextMcid = mainParentTreeNumEntry.size(); sigBlockMcid = nextMcid; nextMcid++; this.structTreeRootKids = obtainStructTreeRootKids(structTreeRoot); - + + if(this.structTreeRootKids == null) + { + this.structTreeRootKids = this.createStructTreeRootKids(structTreeRoot); + } + } catch (Exception ex) { logger.error("error", ex); throw new PresentableException(ErrorCode.CANNOT_WRITE_PDF, "error writing structured signature content", ex); } } - + + PdfArray createStructTreeRootKids(PdfDictionary structTreeRoot) { + PdfArray tmp = new PdfArray(); + structTreeRoot.put(PdfName.K, tmp); + return tmp; + } + /** * Create struct data for main signature block * @throws PresentableException @@ -196,7 +207,7 @@ public class StructContentHelper implements StructContentWriter { "error writing structured signature content", ex); } } - + /** * Finish struct data for signblock and it's elements (NOT for the external link and annot!) @@ -218,8 +229,8 @@ public class StructContentHelper implements StructContentWriter { } /** - * Build the structured content for the signature logo (bildmarke). {@link #beginFigureContent(PdfContentByte)} and - * {@link #endFigureContent(PdfContentByte)} have to be called before this method to mark the logo in the stream. This + * Build the structured content for the signature logo (bildmarke). {@link #beginFigureContent(PdfContentByte)} and + * {@link #endFigureContent(PdfContentByte)} have to be called before this method to mark the logo in the stream. This * is done implicitly in the modified itext source (see {@link StructContentWriterHolder}). * @param so * @param sigBlockObj @@ -231,7 +242,7 @@ public class StructContentHelper implements StructContentWriter { PdfDictionary structTreeRoot = getStructTreeRoot(); PdfIndirectReference mcrRef = createMcrStructElem(this.figureMcid, sigBlockObj.getIndirectReference()); - PdfIndirectReference figureRef = createStructElem("Figure", mcrRef, + PdfIndirectReference figureRef = createStructElem("Figure", mcrRef, getAltText(so.getSignatureTypeDefinition().getType()), structTreeRoot.getIndRef()); structTreeRootKids.add(figureRef); @@ -250,42 +261,42 @@ public class StructContentHelper implements StructContentWriter { } } - + /** * Build the link annotation for the signature verification link and the structured content accordingly.
* The tagging does NOT work if the link is placed in a binary signature replace cell (phlengh for this cell)!! * @param sigBlockObj - * @param atp - * @throws PresentableException + * @param atp + * @throws PresentableException */ void buildVerifyLinkStructData(PdfTemplate sigBlockObj, ActualTablePos atp) throws PresentableException { - if (!this.isTagged || !this.isLinkMarked() || !isLinkFound()) return; + if (!this.isTagged || !this.isLinkMarked() || !isLinkFound()) return; try { PdfNumber parentTreeKey = getNewParentTreeKey(); - - PdfArray annots = obrainAnnotsFromPage(); - - PdfIndirectReference linkAnnotRef = createLinkAnnot(parentTreeKey, atp); - annots.add(linkAnnotRef); - + + PdfArray annots = obrainAnnotsFromPage(); + + PdfIndirectReference linkAnnotRef = createLinkAnnot(parentTreeKey, atp); + annots.add(linkAnnotRef); + PdfIndirectReference objr = createObjrStructElem(linkAnnotRef); - PdfIndirectReference mcr = createMcrStructElem(this.linkMcid, sigBlockObj.getIndirectReference()); - + PdfIndirectReference mcr = createMcrStructElem(this.linkMcid, sigBlockObj.getIndirectReference()); + PdfDictionary structTreeRoot = getStructTreeRoot(); - + PdfArray linkKids = new PdfArray(); PdfIndirectReference linkKidsRef = stamper.getWriter().getPdfIndirectReference(); - + PdfIndirectReference linkRef = createStructElem("Link", linkKidsRef, structTreeRoot.getIndRef()); linkKids.add(objr); - - PdfIndirectReference span = createStructElem("Span", mcr, linkRef); - linkKids.add(span); - + + PdfIndirectReference span = createStructElem("Span", mcr, linkRef); + linkKids.add(span); + stamper.getWriter().addToBody(linkKids, linkKidsRef); structTreeRootKids.add(linkRef); - + // create new entry in ParentTree PdfArray parentTreeNums = getParentTreeNums(); parentTreeNums.add(parentTreeKey); @@ -295,16 +306,16 @@ public class StructContentHelper implements StructContentWriter { stamperImp.markUsed(structTreeRoot.getAsDict(PdfName.PARENTTREE)); stamperImp.markUsed(structTreeRootKids); stamperImp.markUsed(linkKids); - - stamperImp.markUsed(structTreeRoot); - + + stamperImp.markUsed(structTreeRoot); + } catch (IOException e) { logger.error("error", e); throw new PresentableException(ErrorCode.CANNOT_WRITE_PDF, "error writing structured signature content", e); } } - + private boolean isLinkFound() { return this.linkUrlString != null && this.verifyLinkCellPos != null && this.linkPosMap.size() > 0 && this.linkMcid >= 0; } @@ -320,11 +331,11 @@ public class StructContentHelper implements StructContentWriter { } else { return null; } - } + } /** * Build and write structured content for adobe signature annotation - * + * * @param sigFormField * @param title * @throws PresentableException @@ -378,7 +389,7 @@ public class StructContentHelper implements StructContentWriter { content.endMarkedContentSequence(); } } - + /** * Writes start tag for signature logo marked content sequence. */ @@ -392,18 +403,18 @@ public class StructContentHelper implements StructContentWriter { } } } - + /** * Writes end tag for signature logo marked content sequence. */ public void endFigureContent(PdfContentByte localContent) { if (isTagged && isFigureMarked()) { - localContent.endMarkedContentSequence(); + localContent.endMarkedContentSequence(); } } - + /** - * Writes start tag for verify link marked content sequence. + * Writes start tag for verify link marked content sequence. */ public void beginLinkContent(PdfContentByte localContent, String urlString) { // it's called from here com.lowagie.text.pdf.PdfContentByte.showText(String) @@ -416,7 +427,7 @@ public class StructContentHelper implements StructContentWriter { } else { logger.warn("cannot tag multiple verify links"); } - } + } } /** @@ -425,8 +436,8 @@ public class StructContentHelper implements StructContentWriter { public void endLinkContent(PdfContentByte localContent) { if (isTagged && isLinkMarked()) { localContent.endMarkedContentSequence(); - } - } + } + } /** * Implements {@link StructContentWriter#markPos(Rectangle)} @@ -436,22 +447,22 @@ public class StructContentHelper implements StructContentWriter { } /** - * Implements {@link StructContentWriter#storeCurrentPosAsLink()} + * Implements {@link StructContentWriter#storeCurrentPosAsLink()} */ public void storeCurrentPosAsLink() { - this.verifyLinkCellPos = new Rectangle(this.tempMarkedPos); + this.verifyLinkCellPos = new Rectangle(this.tempMarkedPos); } - + public void putVal(String key, Object val) { - tmpMap.put(key, val); + tmpMap.put(key, val); } public void storeVals() { linkPosMap = new HashMap(tmpMap); - } - + } + /** - * set explicit annotation tab order if missing + * set explicit annotation tab order if missing */ private void doAnnoTabOrder() { if (page.getAsName(new PdfName("Tabs")) == null) { @@ -470,31 +481,31 @@ public class StructContentHelper implements StructContentWriter { } logger.debug("Input is tagged. Writing structure/WAI data."); } - + private PdfIndirectReference createLinkAnnot(PdfNumber structParentNr, ActualTablePos atp) throws IOException { - PdfDictionary linkAnnot = new PdfDictionary(); - - PdfDictionary a = new PdfDictionary(); + PdfDictionary linkAnnot = new PdfDictionary(); + + PdfDictionary a = new PdfDictionary(); a.put(PdfName.S, new PdfName("URI")); a.put(PdfName.TYPE, PdfName.ACTION); a.put(PdfName.URI, new PdfString(this.linkUrlString)); linkAnnot.put(PdfName.A, a); - + PdfDictionary bs = new PdfDictionary(); bs.put(PdfName.W, new PdfNumber(0)); linkAnnot.put(PdfName.BS, bs); linkAnnot.put(PdfName.F, new PdfNumber(4)); - + // iText "converts" 0.0f to an integer, therefore we cannot use 0, not nice... //linkAnnot.put(PdfName.RECT, new PdfArray(new float[] {0.01f, 0.01f, 0.01f, 0.01f})); // take cell pos as link pos linkAnnot.put(PdfName.RECT, new PdfArray(calcLinkPos(atp))); - + linkAnnot.put(PdfName.STRUCTPARENT, structParentNr); linkAnnot.put(PdfName.SUBTYPE, PdfName.LINK); - + return stamper.getWriter().addToBody(linkAnnot).getIndirectReference(); } @@ -525,10 +536,10 @@ public class StructContentHelper implements StructContentWriter { private float getPosMapVal(String key) { return ((Float) this.linkPosMap.get(key)).floatValue(); } - + protected static PdfArray createPdfArrayFromTablePos(ActualTablePos pos) { return new PdfArray( new float[] {pos.x, pos.y, pos.x + pos.width, pos.y - pos.height}); - } + } private PdfArray obrainAnnotsFromPage() throws IOException { PdfArray annots = this.page.getAsArray(PdfName.ANNOTS); @@ -551,7 +562,7 @@ public class StructContentHelper implements StructContentWriter { rk.add(root_k.getIndRef()); structTreeRoot.put(PdfName.K, structTreeRootKids); - } else { // has to be array + } else if(root_k != null) { // has to be array rk = (PdfArray) root_k; } return rk; @@ -561,17 +572,17 @@ public class StructContentHelper implements StructContentWriter { PdfNumber structParentsNr, PdfTemplate sigBlockObj) { int numsIdx = -1; PdfArray parentTreeEntry = null; - + if (structParentsNr == null) { // no StructParents entry yet, make new one and add new parenttree entry PdfNumber parentTreeKey = null; parentTreeNextKey = structTreeRoot.getAsNumber(PARENTTREENEXTKEY); // read next proposed key if (parentTreeNextKey == null) { // this can be null if a non-perfect pdf creator was at work // find the next key by counting - int nextI = ((int) parentTreeNums.size() / 2); // know the "Number Trees" data structure from pdf-ref + int nextI = ((int) parentTreeNums.size() / 2); // know the "Number Trees" data structure from pdf-ref this.parentTreeNextKey = new PdfNumber(nextI); - structTreeRoot.put(PARENTTREENEXTKEY, this.parentTreeNextKey); // write ParentTreeNextKey entry + structTreeRoot.put(PARENTTREENEXTKEY, this.parentTreeNextKey); // write ParentTreeNextKey entry } - + parentTreeKey = new PdfNumber(parentTreeNextKey.intValue()); parentTreeNextKey.increment(); page.put(PdfName.STRUCTPARENTS, parentTreeKey); // write /StructParents entry to page @@ -583,7 +594,7 @@ public class StructContentHelper implements StructContentWriter { parentTreeEntry = new PdfArray(); numsIdx = parentTreeNums.size() - 1; - } else { // structparents entry already available, find parenttree entry + } else { // structparents entry already available, find parenttree entry //parentTreeKey = structParentsNr; parentTreeNextKey = structTreeRoot.getAsNumber(PARENTTREENEXTKEY); // read next proposed key if (parentTreeNextKey == null) { // this can be null if a non-perfact pdf creator was at work @@ -593,10 +604,10 @@ public class StructContentHelper implements StructContentWriter { nextI = ((int) parentTreeNums.size() / 2); } this.parentTreeNextKey = new PdfNumber(nextI); - structTreeRoot.put(PARENTTREENEXTKEY, this.parentTreeNextKey); - } + structTreeRoot.put(PARENTTREENEXTKEY, this.parentTreeNextKey); + } } - + // add Structparents entry to xobject content stream sigBlockObj.addAttribute(PdfName.STRUCTPARENTS, structParentsNr); @@ -620,7 +631,7 @@ public class StructContentHelper implements StructContentWriter { } return parentTreeEntry; } - + // private PdfIndirectReference createStructElem(String structType, PdfObject kid) throws IOException { // return createStructElem(structType, kid, null); // } @@ -628,10 +639,10 @@ public class StructContentHelper implements StructContentWriter { private PdfIndirectReference createStructElem(String structType, PdfObject kid, PdfIndirectReference parentRef) throws IOException { return createStructElem(structType, kid, null, parentRef); } - + private PdfIndirectReference createStructElem(String structType, PdfObject kid, String altText, PdfIndirectReference parentRef) throws IOException { - + PdfDictionary newStruct = new PdfDictionary(); newStruct.put(PdfName.S, new PdfName(structType)); //newStruct.put(PdfName.T, new PdfString("PDF-AS Signaturblock"));// eher nicht @@ -649,21 +660,21 @@ public class StructContentHelper implements StructContentWriter { newStruct.put(PdfName.K, kid); return stamper.getWriter().addToBody(newStruct).getIndirectReference(); - } + } private boolean isFigureMarked() { return this.figureMcid > -1; } - + private boolean isLinkMarked() { return this.linkMcid > -1; } - + private PdfNumber getNewParentTreeKey() { // new parent tree entry if (parentTreeNextKey == null) { - parentTreeNextKey = getStructTreeRoot().getAsNumber(PARENTTREENEXTKEY); // read next proposed key + parentTreeNextKey = getStructTreeRoot().getAsNumber(PARENTTREENEXTKEY); // read next proposed key } PdfNumber res = new PdfNumber(parentTreeNextKey.intValue()); parentTreeNextKey.increment(); @@ -676,28 +687,28 @@ public class StructContentHelper implements StructContentWriter { objr.put(PdfName.TYPE, new PdfName("OBJR")); objr.put(PdfName.PG, page.getIndRef()); objr.put(new PdfName("Obj"), objRef); - + return stamper.getWriter().addToBody(objr).getIndirectReference(); } - + private PdfIndirectReference createMcrStructElem(int mcid, PdfIndirectReference streamRef) throws IOException { PdfDictionary objr = new PdfDictionary(); objr.put(PdfName.TYPE, new PdfName("MCR")); objr.put(PdfName.PG, page.getIndRef()); objr.put(PdfName.MCID, new PdfNumber(mcid)); objr.put(new PdfName("Stm"), streamRef); - + return stamper.getWriter().addToBody(objr).getIndirectReference(); } - + private PdfArray getParentTreeNums() { return getStructTreeRoot().getAsDict(PdfName.PARENTTREE).getAsArray(PdfName.NUMS); } private PdfDictionary getStructTreeRoot() { return stamper.getReader().getCatalog().getAsDict(PdfName.STRUCTTREEROOT); - } - + } + private static String getAltText(String sigProfile) { return AdobeSignatureHelper.getDefaultableConfigProperty(sigProfile, ALT_TEXT_CONF_KEY, ALT_TEXT_DEFAULT); } -- cgit v1.2.3