From 52326ca09c4ca269bf07d2af8b4dd5df682691f5 Mon Sep 17 00:00:00 2001 From: pdanner Date: Tue, 2 Nov 2010 14:43:44 +0000 Subject: added helper for WAI / struct content git-svn-id: https://joinup.ec.europa.eu/svn/pdf-as/trunk@595 7b5415b0-85f9-ee4d-85bd-d5d0c3b42d1c --- .../wag/egov/egiz/pdf/StructContentHelper.java | 277 +++++++++++++++++++++ 1 file changed, 277 insertions(+) create mode 100644 src/main/java/at/knowcenter/wag/egov/egiz/pdf/StructContentHelper.java 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 new file mode 100644 index 0000000..07f2328 --- /dev/null +++ b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/StructContentHelper.java @@ -0,0 +1,277 @@ +package at.knowcenter.wag.egov.egiz.pdf; + +import org.apache.log4j.Logger; + +import at.gv.egiz.pdfas.exceptions.ErrorCode; +import at.knowcenter.wag.egov.egiz.exceptions.PresentableException; + +import com.lowagie.text.pdf.PdfArray; +import com.lowagie.text.pdf.PdfContentByte; +import com.lowagie.text.pdf.PdfDictionary; +import com.lowagie.text.pdf.PdfFormField; +import com.lowagie.text.pdf.PdfIndirectReference; +import com.lowagie.text.pdf.PdfName; +import com.lowagie.text.pdf.PdfNumber; +import com.lowagie.text.pdf.PdfObject; +import com.lowagie.text.pdf.PdfStamper; +import com.lowagie.text.pdf.PdfStamperImp; + +/** + * 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. + * @author exthex + * + */ +public class StructContentHelper { + private static Logger logger = Logger.getLogger(StructContentHelper.class); + + private int nextMcid = 0; + private boolean isTagged = false; + + private PdfStamper stamper; + private PdfStamperImp stamperImp; + private PdfContentByte content; + private PdfDictionary page; + private PdfNumber parentTreeNextKey = null; + private PdfNumber annotationParentTreeKey = null; + private PdfArray rootKids = null; + + StructContentHelper(PdfStamper stamper, PdfContentByte content, int pageNr) { + this.stamper = stamper; + this.content = content; + stamperImp = ((PdfStamperImp) stamper.getWriter()); + page = stamper.getReader().getPageN(pageNr); + } + + /** + * Create and write structured content for signature block + * @throws PresentableException + */ + void buildMainStructData() throws PresentableException { + // TODO titel/alttext woher? + + try { + + PdfDictionary markDict = stamper.getReader().getCatalog().getAsDict(PdfName.MARKINFO); + if (markDict != null) { + isTagged = markDict.getAsBoolean(PdfName.MARKED).booleanValue(); + } + if (!isTagged) { + BinarySignature.logger + .debug("input document is not tagged. no structure/wai information is written"); + return; + } + + PdfDictionary root = getStructTreeRoot(); + stamperImp.markUsed(root); + + PdfArray parentTreeNums = getParentTreeNums(); + + 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); + } + + if (strucParNr == null) { // no StructParents entry yet, make new one + parentTreeNextKey = root.getAsNumber(new PdfName("ParentTreeNextKey")); // read next proposed key + 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; + + } else { + parentTreeKey = strucParNr; + } + + + // 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; + } + 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")); + + newStruct.put(PdfName.PG, page.getIndRef()); + //newStruct.put(PdfName.ALT, new PdfString("meine feiner signaturblock alttext")); + newStruct.put(PdfName.K, new PdfNumber(nextMcid)); + + + // ADD everything at the end because nothing can be written after + + PdfIndirectReference newStructRef = stamper.getWriter().addToBody(newStruct) + .getIndirectReference(); + rootKids.add(newStructRef); + + parentTreeEntry.add(newStructRef); + + stamperImp.markUsed(parentTreeEntry); + + if (strucParNr == null) { + parentTreeNums.add(stamper.getWriter().addToBody(parentTreeEntry) + .getIndirectReference()); + stamperImp.markUsed(parentTreeNums); + stamperImp.markUsed(root.getAsDict(PdfName.PARENTTREE)); + } + + } catch (Exception ex) { + logger.error("error", ex); + throw new PresentableException(ErrorCode.CANNOT_WRITE_PDF, + "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. + * Call {@link #endSigBlockContent()} afterwards + */ + void beginSigBlockContent() { + if (isTagged) { + content.getInternalBuffer().append(new PdfName("Table").getBytes()).append(" <> BDC").append_i('\n'); + } + } + + /** + * End tag for signature block content stream. Place this after the signature block is written to a content stream + */ + void endSigBlockContent() { + if (isTagged) { + content.endMarkedContentSequence(); + } + } + + + /** + * Build new StructParent entry for signature annotation. + * @return + */ + public PdfNumber buildAnnotStructParent() { + if (this.isTagged) { + // new parent tree entry + if (parentTreeNextKey == null) { + parentTreeNextKey = getStructTreeRoot().getAsNumber(new PdfName("ParentTreeNextKey")); // read next proposed key + } + annotationParentTreeKey = new PdfNumber(parentTreeNextKey.intValue()); + parentTreeNextKey.increment(); + + return annotationParentTreeKey; + } else { + return null; + } + + } + + /** + * Build and write structured content for adobe signature anntotation + * @param sigFormField + * @param title + * @throws PresentableException + */ + 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")); + + adobeSigStruct.put(PdfName.PG, page.getIndRef()); + // adobeSigStruct.put(PdfName.ALT, new + // PdfString("mein feiner signaturtag")); + + PdfDictionary objr = new PdfDictionary(); + objr.put(PdfName.TYPE, new PdfName("OBJR")); + objr.put(PdfName.PG, page.getIndRef()); + objr.put(new PdfName("Obj"), sigFormField.getIndirectReference()); + + PdfIndirectReference objrRef = stamper.getWriter().addToBody(objr).getIndirectReference(); + adobeSigStruct.put(PdfName.K, objrRef); + PdfIndirectReference adobeSigStructRef = stamper.getWriter().addToBody(adobeSigStruct) + .getIndirectReference(); + + // root_a.add(adobeSigStructRef); + + PdfArray parentTreeNums = getParentTreeNums(); + // create new entry in ParentTree + parentTreeNums.add(annotationParentTreeKey); + //PdfArray parentTreeEntry = new PdfArray(); + + // 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); + + rootKids.add(adobeSigStructRef); + stamperImp.markUsed(rootKids); + + stamperImp.markUsed(parentTreeNums); + stamperImp.markUsed(root.getAsDict(PdfName.PARENTTREE)); + + } catch (Exception ex) { + logger.error("error", ex); + throw new PresentableException(ErrorCode.CANNOT_WRITE_PDF, + "error writing structured signature content", ex); + } + + } + + private PdfArray getParentTreeNums() { + return getStructTreeRoot().getAsDict(PdfName.PARENTTREE).getAsArray(PdfName.NUMS); + } + + private PdfDictionary getStructTreeRoot() { + return stamper.getReader().getCatalog().getAsDict(PdfName.STRUCTTREEROOT); + } + +} -- cgit v1.2.3