From 0e1cc67d55a018bd4f8a134b5a616e040743f5ae Mon Sep 17 00:00:00 2001 From: pdanner Date: Fri, 11 Feb 2011 14:32:40 +0000 Subject: added additional wai/structcontent support: verify link, signature logo git-svn-id: https://joinup.ec.europa.eu/svn/pdf-as/trunk@748 7b5415b0-85f9-ee4d-85bd-d5d0c3b42d1c --- .../wag/egov/egiz/pdf/AdobeSignatureHelper.java | 2 +- .../wag/egov/egiz/pdf/BinarySignature.java | 14 +- .../wag/egov/egiz/pdf/StructContentHelper.java | 618 +++++++++++++++------ 3 files changed, 453 insertions(+), 181 deletions(-) diff --git a/src/main/java/at/knowcenter/wag/egov/egiz/pdf/AdobeSignatureHelper.java b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/AdobeSignatureHelper.java index deab953..71b2dcd 100644 --- a/src/main/java/at/knowcenter/wag/egov/egiz/pdf/AdobeSignatureHelper.java +++ b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/AdobeSignatureHelper.java @@ -144,7 +144,7 @@ public class AdobeSignatureHelper { HashMap exc = new HashMap(); exc.put(PdfName.CONTENTS, new Integer(2)); - PdfNumber parentNum = structHelper.buildAnnotStructParent(); + PdfNumber parentNum = structHelper.buildAdobeSigStructParent(); if (parentNum != null) { PdfFormField sigField = PdfFormField.createSignature(stamper.getWriter()); sap.setSigFormField(sigField); diff --git a/src/main/java/at/knowcenter/wag/egov/egiz/pdf/BinarySignature.java b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/BinarySignature.java index c34ee68..0465c5d 100644 --- a/src/main/java/at/knowcenter/wag/egov/egiz/pdf/BinarySignature.java +++ b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/BinarySignature.java @@ -849,8 +849,12 @@ public abstract class BinarySignature // table_height = " + pdf_table.getTotalHeight()); PdfTemplate table_template = content.createTemplate(pdf_table.getTotalWidth(), pdf_table.getTotalHeight()); - table_template.setCompress(Boolean.FALSE); // do not compress sigblock because we rewrite it afterwards for bin sig + table_template.setCompress(Boolean.FALSE); // do not compress sigblock because we rewrite it afterwards for bin sig + // exthex + StructContentHelper structHelper = new StructContentHelper(stamper, content, pi.getPage()); + structHelper.buildMainStructData(table_template); + pdf_table.writeSelectedRows(0, -1, 0, pdf_table.getTotalHeight(), table_template); // table_template.moveTo(0, 0); @@ -863,9 +867,7 @@ public abstract class BinarySignature // pdf_table.writeSelectedRows(0, -1, SIGNATURE_BORDER / 2, // table_position, content); - // exthex - StructContentHelper structHelper = new StructContentHelper(stamper, content, pi.getPage()); - structHelper.buildMainStructData(so); + structHelper.beginSigBlockContent(); @@ -873,6 +875,9 @@ public abstract class BinarySignature structHelper.endSigBlockContent(); + structHelper.buildFigureStructData(so, table_template); + structHelper.buildVerifyLinkStructData(table_template); + ActualTablePos atp = new ActualTablePos(); atp.page = pi.getPage(); @@ -943,6 +948,7 @@ public abstract class BinarySignature //org.apache.commons.io.FileUtils.writeByteArrayToFile(new java.io.File("C:/out.pdf"), ((at.gv.egiz.pdfas.impl.output.ByteArrayDataSink) written_pdf).getByteArray()); // iui.signed_pdf = baos.toByteArray(); + structHelper.removeCurrent(); return iui; } catch (IOException e) 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 2592b04..e6ad646 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 @@ -1,5 +1,7 @@ package at.knowcenter.wag.egov.egiz.pdf; +import java.io.IOException; + import org.apache.log4j.Logger; import at.gv.egiz.pdfas.exceptions.ErrorCode; @@ -17,26 +19,44 @@ import com.lowagie.text.pdf.PdfObject; import com.lowagie.text.pdf.PdfStamper; import com.lowagie.text.pdf.PdfStamperImp; import com.lowagie.text.pdf.PdfString; +import com.lowagie.text.pdf.PdfTemplate; +import com.lowagie.text.pdfas.StructContentWriter; +import com.lowagie.text.pdfas.StructContentWriterHolder; /** * 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 * defined order. The object cannot be reused for several signatures.
- * See pdf spec "Logical Structure" for details. + * 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 { +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 = "PDF-AS Signatur"; - - private static Logger logger = Logger.getLogger(StructContentHelper.class); + private static final String ALT_TEXT_DEFAULT = "Signaturbildmarke"; + private final static String ALT_TEXT_CONF_KEY = "sigLogoAltText"; - private final static String ALT_TEXT_CONF_KEY = "sigBlockAltText"; private int nextMcid = 0; + /** + * MCID value used for the sigblock marked contend identifier + */ + private int sigBlockMcid =-1; + /** + * MCID value for "Bildmarke" marked content sequence + */ + private int figureMcid = -1; + /** + * MCID value for verify link marked content sequence + */ + private int linkMcid = -1; + private String linkUrlString = null; private boolean isTagged = false; private PdfStamper stamper; @@ -45,142 +65,221 @@ public class StructContentHelper { private PdfDictionary page; private PdfNumber parentTreeNextKey = null; private PdfNumber annotationParentTreeKey = null; - private PdfArray rootKids = null; + /** + * Kids array (K) of the StructTreeRoot + */ + private PdfArray structTreeRootKids = null; + /** + * 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} + * for thread local access from itext. + * + * @param stamper + * @param content + * @param pageNr + */ StructContentHelper(PdfStamper stamper, PdfContentByte content, int pageNr) { this.stamper = stamper; this.content = content; stamperImp = ((PdfStamperImp) stamper.getWriter()); page = stamper.getReader().getPageN(pageNr); + StructContentWriterHolder.setThreadLocalWriter(this); + } + + /** + * Remove thread local helper + */ + public void removeCurrent() { + StructContentWriterHolder.removeThreadLocalWriter(); } /** - * Create and write structured content for signature block + * Create and write structured content for signature block. This method initialized the whole StructTreeRoot stuff. + * @param sigBlockObj * @throws PresentableException */ - void buildMainStructData(SignatureObject so) throws PresentableException { + void buildMainStructData(PdfTemplate sigBlockObj) throws PresentableException { try { - - PdfDictionary markDict = stamper.getReader().getCatalog().getAsDict(PdfName.MARKINFO); - if (markDict != null) { - isTagged = markDict.getAsBoolean(PdfName.MARKED).booleanValue(); - } + checkTagging(); if (!isTagged) { - BinarySignature.logger.debug("Input document is not tagged. no structure/wai information is written"); return; } + + doAnnoTabOrder(); + + PdfDictionary structTreeRoot = getStructTreeRoot(); + stamperImp.markUsed(structTreeRoot); + + 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); + + PdfIndirectReference newStructRef = createStructElem(SIGBLOCK_STRUCT_TYPE, new PdfNumber(sigBlockMcid), structTreeRoot.getIndRef()); + + // ADD everything at the end because nothing can be written afterwards + structTreeRootKids.add(newStructRef); + mainParentTreeNumEntry.add(newStructRef); + + stamperImp.markUsed(mainParentTreeNumEntry); + +// if (structParentsNr == null) { +// parentTreeNums.add(stamper.getWriter().addToBody(mainParentTreeNumEntry).getIndirectReference()); +// stamperImp.markUsed(parentTreeNums); +// stamperImp.markUsed(structTreeRoot.getAsDict(PdfName.PARENTTREE)); +// } - BinarySignature.logger.debug("Input is tagged. Writing structure/WAI data."); - PdfDictionary root = getStructTreeRoot(); - stamperImp.markUsed(root); + } catch (Exception ex) { + logger.error("error", ex); + throw new PresentableException(ErrorCode.CANNOT_WRITE_PDF, + "error writing structured signature content", ex); + } + } - PdfArray parentTreeNums = getParentTreeNums(); + /** + * 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 + * @throws PresentableException + */ + void buildFigureStructData(SignatureObject so, PdfTemplate sigBlockObj) throws PresentableException { + try { + if (isTagged && isFigureMarked()) { - PdfNumber parentTreeKey = null; - int numsIdx = -1; - PdfNumber strucParNr = page.getAsNumber(PdfName.STRUCTPARENTS); - PdfArray parentTreeEntry = null; - if (page.getAsName(new PdfName("Tabs")) == null) { - page.put(new PdfName("Tabs"), PdfName.S); // set explicit annotation order - stamperImp.markUsed(page); - } + PdfDictionary structTreeRoot = getStructTreeRoot(); + PdfIndirectReference mcrRef = createMcrStructElem(this.figureMcid, sigBlockObj.getIndirectReference()); + PdfIndirectReference figureRef = createStructElem("Figure", mcrRef, + getAltText(so.getSignatureTypeDefinition().getType()), structTreeRoot.getIndRef()); - if (strucParNr == null) { // no StructParents entry yet, make new one - parentTreeNextKey = root.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); - this.parentTreeNextKey = new PdfNumber(nextI); - root.put(PARENTTREENEXTKEY, this.parentTreeNextKey); - } - - parentTreeKey = new PdfNumber(parentTreeNextKey.intValue()); - parentTreeNextKey.increment(); - page.put(PdfName.STRUCTPARENTS, parentTreeKey); // write /StructParents entry to page - - stamperImp.markUsed(page); - // create new entry in ParentTree - parentTreeNums.add(parentTreeKey); - parentTreeEntry = new PdfArray(); - // parentTreeNums.add(stamper.getWriter().addToBody(parentTreeEntry).getIndirectReference()); - // simpl.markUsed(parentTreeNums); - // simpl.markUsed(root.getAsDict(PdfName.PARENTTREE)); - numsIdx = parentTreeNums.size() - 1; + structTreeRootKids.add(figureRef); + mainParentTreeNumEntry.add(figureRef); - } else { - parentTreeKey = strucParNr; - parentTreeNextKey = root.getAsNumber(PARENTTREENEXTKEY); // read next proposed key - if (parentTreeNextKey == null) { // this can be null if a non-perfact pdf creator was at work - // find the next key by counting - int nextI = 0; - if (parentTreeNums != null) { - nextI = ((int) parentTreeNums.size() / 2); - } - this.parentTreeNextKey = new PdfNumber(nextI); - root.put(PARENTTREENEXTKEY, this.parentTreeNextKey); - } - } + stamperImp.markUsed(structTreeRootKids); + stamperImp.markUsed(structTreeRoot); + stamperImp.markUsed(mainParentTreeNumEntry); - // find my structParentEntry - if (numsIdx < 0) { - // it's a weird data structure: "number tree", see pdf reference if you really want to understand - - // if the array has no gaps it is easy: - numsIdx = strucParNr.intValue() * 2; - if (parentTreeNums.getAsNumber(numsIdx).intValue() != strucParNr.intValue()) { // there seem to be gaps - for (numsIdx = 0; numsIdx < parentTreeNums.size(); numsIdx += 2) { // search manually - if (parentTreeNums.getAsNumber(numsIdx).intValue() == strucParNr.intValue()) { - break; - } - } - } - numsIdx += 1; - } - if (parentTreeEntry == null) { - parentTreeEntry = parentTreeNums.getAsArray(numsIdx); } - nextMcid = parentTreeEntry.size(); - - PdfObject root_k = root.getDirectObject(PdfName.K); - stamperImp.markUsed(root_k); - if (root_k instanceof PdfDictionary) { - rootKids = new PdfArray(); - stamperImp.markUsed(rootKids); - rootKids.add(root_k.getIndRef()); - root.put(PdfName.K, rootKids); - - } else { // has to be array - rootKids = (PdfArray) root_k; + if (isTagged && mainParentTreeNumEntry.getIndRef() == null) { + getParentTreeNums().add(stamper.getWriter().addToBody(mainParentTreeNumEntry).getIndirectReference()); + stamperImp.markUsed(getParentTreeNums()); + stamperImp.markUsed(getStructTreeRoot().getAsDict(PdfName.PARENTTREE)); } - PdfDictionary newStruct = new PdfDictionary(); - // simpl.markUsed(newStruct); - newStruct.put(PdfName.S, new PdfName("Table")); - //newStruct.put(PdfName.T, new PdfString("mein Titel")); - newStruct.put(PdfName.P, root.getIndRef()); - newStruct.put(PdfName.TYPE, new PdfName("StructElem")); + } catch (Exception ex) { + logger.error("error", ex); + throw new PresentableException(ErrorCode.CANNOT_WRITE_PDF, + "error writing structured signature content", ex); + } + + } + + /** + * 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 + * @throws PresentableException + */ + void buildVerifyLinkStructData(PdfTemplate sigBlockObj) throws PresentableException { + if (!this.isTagged || !this.isLinkMarked()) return; + + try { + PdfNumber parentTreeKey = getNewParentTreeKey(); + + PdfArray annots = obrainAnnotsFromPage(); + PdfIndirectReference linkAnnotRef = createLinkAnnot(parentTreeKey); + annots.add(linkAnnotRef); + + PdfIndirectReference objr = createObjrStructElem(linkAnnotRef); + 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); + + stamper.getWriter().addToBody(linkKids, linkKidsRef); + structTreeRootKids.add(linkRef); + + // create new entry in ParentTree + PdfArray parentTreeNums = getParentTreeNums(); + parentTreeNums.add(parentTreeKey); + parentTreeNums.add(linkRef); + + stamperImp.markUsed(parentTreeNums); + stamperImp.markUsed(structTreeRoot.getAsDict(PdfName.PARENTTREE)); + stamperImp.markUsed(structTreeRootKids); + stamperImp.markUsed(linkKids); + + stamperImp.markUsed(structTreeRoot); + + } catch (IOException e) { + logger.error("error", e); + throw new PresentableException(ErrorCode.CANNOT_WRITE_PDF, + "error writing structured signature content", e); + } + } + + /** + * Build new StructParent entry for signature annotation. + * @return + */ + PdfNumber buildAdobeSigStructParent() { + if (this.isTagged) { + this.annotationParentTreeKey = getNewParentTreeKey(); + return annotationParentTreeKey; + } else { + return null; + } + } - newStruct.put(PdfName.PG, page.getIndRef()); - newStruct.put(PdfName.ALT, new PdfString(getAltText(so.getSignatureTypeDefinition().getType()))); - newStruct.put(PdfName.K, new PdfNumber(nextMcid)); + /** + * Build and write structured content for adobe signature annotation + * + * @param sigFormField + * @param title + * @throws PresentableException + */ + void buildAdobeSigStruct(PdfFormField sigFormField, String title) throws PresentableException { + if (!isTagged) + return; + try { + PdfDictionary root = getStructTreeRoot(); - // ADD everything at the end because nothing can be written after + PdfIndirectReference objrRef = createObjrStructElem(sigFormField.getIndirectReference()); - PdfIndirectReference newStructRef = stamper.getWriter().addToBody(newStruct) - .getIndirectReference(); - rootKids.add(newStructRef); + PdfIndirectReference adobeSigStructRef = createStructElem("Link", objrRef, root.getIndRef()); - parentTreeEntry.add(newStructRef); + PdfArray parentTreeNums = getParentTreeNums(); + // create new entry in ParentTree + parentTreeNums.add(annotationParentTreeKey); + parentTreeNums.add(adobeSigStructRef); - stamperImp.markUsed(parentTreeEntry); + structTreeRootKids.add(adobeSigStructRef); + stamperImp.markUsed(structTreeRootKids); - if (strucParNr == null) { - parentTreeNums.add(stamper.getWriter().addToBody(parentTreeEntry) - .getIndirectReference()); - stamperImp.markUsed(parentTreeNums); - stamperImp.markUsed(root.getAsDict(PdfName.PARENTTREE)); - } + stamperImp.markUsed(parentTreeNums); + stamperImp.markUsed(root.getAsDict(PdfName.PARENTTREE)); } catch (Exception ex) { logger.error("error", ex); @@ -188,7 +287,7 @@ public class StructContentHelper { "error writing structured signature content", ex); } - } + } /** * Start tag for signature block content stream. Place this before the signature block is written to a content stream. @@ -196,8 +295,8 @@ public class StructContentHelper { */ void beginSigBlockContent() { if (isTagged) { - content.getInternalBuffer().append(new PdfName("Table").getBytes()).append(" <> BDC").append_i('\n'); + content.getInternalBuffer().append(new PdfName(SIGBLOCK_STRUCT_TYPE).getBytes()).append(" <> BDC").append('\n'); } } @@ -209,85 +308,253 @@ public class StructContentHelper { content.endMarkedContentSequence(); } } - - + /** - * Build new StructParent entry for signature annotation. - * @return + * Writes start tag for signature logo marked content sequence. */ - public PdfNumber buildAnnotStructParent() { - if (this.isTagged) { - // new parent tree entry - if (parentTreeNextKey == null) { - parentTreeNextKey = getStructTreeRoot().getAsNumber(PARENTTREENEXTKEY); // read next proposed key + public void beginFigureContent(PdfContentByte localContent) { + if (isTagged) { + if (!isFigureMarked()) { + this.figureMcid = this.nextMcid++; + localContent.getInternalBuffer().append("/Figure <> BDC\n"); + } else { + logger.warn("cannot tag multiple figures (bildmarken)"); } - annotationParentTreeKey = new PdfNumber(parentTreeNextKey.intValue()); - parentTreeNextKey.increment(); - - return annotationParentTreeKey; - } else { - return null; } + } + + /** + * Writes end tag for signature logo marked content sequence. + */ + public void endFigureContent(PdfContentByte localContent) { + if (isTagged && isFigureMarked()) { + localContent.endMarkedContentSequence(); + } + } + + /** + * 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) + if (isTagged) { + if (!isLinkMarked()) { + this.linkUrlString = urlString; + this.linkMcid = this.nextMcid++; + localContent.getInternalBuffer().append("/Span <> BDC\n"); + } else { + logger.warn("cannot tag multiple verify links"); + } + } } /** - * Build and write structured content for adobe signature anntotation - * @param sigFormField - * @param title - * @throws PresentableException + * Writes end tag for verify link marked content sequence. */ - public void buildAdobeSigStruct(PdfFormField sigFormField, String title) throws PresentableException { - if (!isTagged) - return; - try { - PdfDictionary root = getStructTreeRoot(); - PdfDictionary adobeSigStruct = new PdfDictionary(); - adobeSigStruct.put(PdfName.S, new PdfName("Link")); // TODO what type? link, annot (1.5) Form - //adobeSigStruct.put(PdfName.T, new PdfString(title)); - adobeSigStruct.put(PdfName.P, root.getIndRef()); - adobeSigStruct.put(PdfName.TYPE, new PdfName("StructElem")); + public void endLinkContent(PdfContentByte localContent) { + if (isTagged && isLinkMarked()) { + localContent.endMarkedContentSequence(); + } + } + - adobeSigStruct.put(PdfName.PG, page.getIndRef()); - // adobeSigStruct.put(PdfName.ALT, new - // PdfString("mein feiner signaturtag")); + /** + * set explicit annotation tab order if missing + */ + private void doAnnoTabOrder() { + if (page.getAsName(new PdfName("Tabs")) == null) { + page.put(new PdfName("Tabs"), PdfName.S); // set explicit annotation TAB order + stamperImp.markUsed(page); + } + } - PdfDictionary objr = new PdfDictionary(); - objr.put(PdfName.TYPE, new PdfName("OBJR")); - objr.put(PdfName.PG, page.getIndRef()); - objr.put(new PdfName("Obj"), sigFormField.getIndirectReference()); + private void checkTagging() { + PdfDictionary markDict = stamper.getReader().getCatalog().getAsDict(PdfName.MARKINFO); + if (markDict != null) { + isTagged = markDict.getAsBoolean(PdfName.MARKED).booleanValue(); + } + if (!isTagged) { + logger.debug("input document is not tagged. no structure/wai information is written"); + } + logger.debug("Input is tagged. Writing structure/WAI data."); + } - PdfIndirectReference objrRef = stamper.getWriter().addToBody(objr).getIndirectReference(); - adobeSigStruct.put(PdfName.K, objrRef); - PdfIndirectReference adobeSigStructRef = stamper.getWriter().addToBody(adobeSigStruct) - .getIndirectReference(); + private PdfIndirectReference createLinkAnnot(PdfNumber structParentNr) throws IOException { + 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); + + // 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})); + + linkAnnot.put(PdfName.STRUCTPARENT, structParentNr); + linkAnnot.put(PdfName.SUBTYPE, PdfName.LINK); + + return stamper.getWriter().addToBody(linkAnnot).getIndirectReference(); + } - // root_a.add(adobeSigStructRef); + private PdfArray obrainAnnotsFromPage() throws IOException { + PdfArray annots = this.page.getAsArray(PdfName.ANNOTS); + if (annots == null) { + annots = new PdfArray(); + page.put(PdfName.ANNOTS, annots); + stamperImp.markUsed(this.page); + stamper.getWriter().addToBody(annots); + } + return annots; + } - PdfArray parentTreeNums = getParentTreeNums(); + private PdfArray obtainStructTreeRootKids(PdfDictionary structTreeRoot) { + PdfArray rk = null; + PdfObject root_k = structTreeRoot.getDirectObject(PdfName.K); + stamperImp.markUsed(root_k); + if (root_k instanceof PdfDictionary) { + rk = new PdfArray(); + stamperImp.markUsed(structTreeRootKids); + rk.add(root_k.getIndRef()); + structTreeRoot.put(PdfName.K, structTreeRootKids); + + } else { // has to be array + rk = (PdfArray) root_k; + } + return rk; + } + + private PdfArray obtainParentTreeEntry(PdfDictionary structTreeRoot, PdfArray parentTreeNums, + 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 + this.parentTreeNextKey = new PdfNumber(nextI); + 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 + structParentsNr = parentTreeKey; + + stamperImp.markUsed(page); // create new entry in ParentTree - parentTreeNums.add(annotationParentTreeKey); - //PdfArray parentTreeEntry = new PdfArray(); + parentTreeNums.add(parentTreeKey); + parentTreeEntry = new PdfArray(); + numsIdx = parentTreeNums.size() - 1; + + } 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 + // find the next key by counting + int nextI = 0; + if (parentTreeNums != null) { + nextI = ((int) parentTreeNums.size() / 2); + } + this.parentTreeNextKey = new PdfNumber(nextI); + structTreeRoot.put(PARENTTREENEXTKEY, this.parentTreeNextKey); + } + } + + // add Structparents entry to xobject content stream + sigBlockObj.addAttribute(PdfName.STRUCTPARENTS, structParentsNr); + + // find my structParentEntry + if (numsIdx < 0) { + // it's a weird data structure: "number tree", see pdf reference if you really want to understand + + // if the array has no gaps it is easy: + numsIdx = structParentsNr.intValue() * 2; + if (parentTreeNums.getAsNumber(numsIdx).intValue() != structParentsNr.intValue()) { // there seem to be gaps + for (numsIdx = 0; numsIdx < parentTreeNums.size(); numsIdx += 2) { // search manually + if (parentTreeNums.getAsNumber(numsIdx).intValue() == structParentsNr.intValue()) { + break; + } + } + } + numsIdx += 1; + } + if (parentTreeEntry == null) { + parentTreeEntry = parentTreeNums.getAsArray(numsIdx); + } + return parentTreeEntry; + } + +// private PdfIndirectReference createStructElem(String structType, PdfObject kid) throws IOException { +// return createStructElem(structType, kid, null); +// } - // parentTreeNums.add(stamper.getWriter().addToBody(parentTreeEntry).getIndirectReference()); - // simpl.markUsed(parentTreeNums); - // simpl.markUsed(root.getAsDict(PdfName.PARENTTREE)); - // parentTreeEntry.add(adobeSigStructRef); - // parentTreeNums.add(stamper.getWriter().addToBody(parentTreeEntry).getIndirectReference()); - parentTreeNums.add(adobeSigStructRef); + 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 + if (parentRef != null) { + newStruct.put(PdfName.P, parentRef); + } + newStruct.put(PdfName.TYPE, new PdfName("StructElem")); - rootKids.add(adobeSigStructRef); - stamperImp.markUsed(rootKids); + newStruct.put(PdfName.PG, page.getIndRef()); + if (altText != null) { + newStruct.put(PdfName.ALT, new PdfString(altText)); + } + // newStruct.put(PdfName.ALT, new PdfString(getAltText(so.getSignatureTypeDefinition().getType()))); + //newStruct.put(PdfName.K, new PdfNumber(nextMcid)); + newStruct.put(PdfName.K, kid); - stamperImp.markUsed(parentTreeNums); - stamperImp.markUsed(root.getAsDict(PdfName.PARENTTREE)); + return stamper.getWriter().addToBody(newStruct).getIndirectReference(); + } - } catch (Exception ex) { - logger.error("error", ex); - throw new PresentableException(ErrorCode.CANNOT_WRITE_PDF, - "error writing structured signature content", ex); + 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 } + PdfNumber res = new PdfNumber(parentTreeNextKey.intValue()); + parentTreeNextKey.increment(); + return res; + } + + private PdfIndirectReference createObjrStructElem(PdfIndirectReference objRef) throws IOException { + PdfDictionary objr = new PdfDictionary(); + 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() { @@ -298,9 +565,8 @@ public class StructContentHelper { return stamper.getReader().getCatalog().getAsDict(PdfName.STRUCTTREEROOT); } - public static String getAltText(String sigProfile) { + private static String getAltText(String sigProfile) { return AdobeSignatureHelper.getDefaultableConfigProperty(sigProfile, ALT_TEXT_CONF_KEY, ALT_TEXT_DEFAULT); - } - + } } -- cgit v1.2.3