aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpdanner <pdanner@7b5415b0-85f9-ee4d-85bd-d5d0c3b42d1c>2011-02-11 14:32:40 +0000
committerpdanner <pdanner@7b5415b0-85f9-ee4d-85bd-d5d0c3b42d1c>2011-02-11 14:32:40 +0000
commit0e1cc67d55a018bd4f8a134b5a616e040743f5ae (patch)
tree6a0bdc4418551045e2402ec6a75a6920c1d49dd0
parent8c93b081b6f66262bd32adb8beb849cfa3c58cf9 (diff)
downloadpdf-as-3-0e1cc67d55a018bd4f8a134b5a616e040743f5ae.tar.gz
pdf-as-3-0e1cc67d55a018bd4f8a134b5a616e040743f5ae.tar.bz2
pdf-as-3-0e1cc67d55a018bd4f8a134b5a616e040743f5ae.zip
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
-rw-r--r--src/main/java/at/knowcenter/wag/egov/egiz/pdf/AdobeSignatureHelper.java2
-rw-r--r--src/main/java/at/knowcenter/wag/egov/egiz/pdf/BinarySignature.java14
-rw-r--r--src/main/java/at/knowcenter/wag/egov/egiz/pdf/StructContentHelper.java618
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.<br>
- * See pdf spec "Logical Structure" for details.
+ * See pdf spec "Logical Structure" for details.<br>
+ * 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 <code>StructTreeRoot</code>
+ */
+ 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.<br>
+ * 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(" <</MCID ")
- .append(nextMcid).append(">> BDC").append_i('\n');
+ content.getInternalBuffer().append(new PdfName(SIGBLOCK_STRUCT_TYPE).getBytes()).append(" <</MCID ")
+ .append(sigBlockMcid).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 <</MCID ").append(this.figureMcid).append(">> 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 <</MCID ").append(this.linkMcid).append(">> 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);
- }
-
+ }
}