From 535a04fa05f739ec16dd81666e3b0f82dfbd442d Mon Sep 17 00:00:00 2001 From: tknall Date: Wed, 9 Jan 2013 15:41:29 +0000 Subject: pdf-as-lib maven project files moved to pdf-as-lib git-svn-id: https://joinup.ec.europa.eu/svn/pdf-as/pdf-as/trunk@926 7b5415b0-85f9-ee4d-85bd-d5d0c3b42d1c --- .../wag/egov/egiz/pdf/StructContentHelper.java | 716 +++++++++++++++++++++ 1 file changed, 716 insertions(+) create mode 100644 pdf-as-lib/src/main/java/at/knowcenter/wag/egov/egiz/pdf/StructContentHelper.java (limited to 'pdf-as-lib/src/main/java/at/knowcenter/wag/egov/egiz/pdf/StructContentHelper.java') diff --git a/pdf-as-lib/src/main/java/at/knowcenter/wag/egov/egiz/pdf/StructContentHelper.java b/pdf-as-lib/src/main/java/at/knowcenter/wag/egov/egiz/pdf/StructContentHelper.java new file mode 100644 index 0000000..7ca5a0a --- /dev/null +++ b/pdf-as-lib/src/main/java/at/knowcenter/wag/egov/egiz/pdf/StructContentHelper.java @@ -0,0 +1,716 @@ +/** + * Copyright 2006 by Know-Center, Graz, Austria + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ +package at.knowcenter.wag.egov.egiz.pdf; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.apache.log4j.Logger; + +import at.gv.egiz.pdfas.exceptions.ErrorCode; +import at.knowcenter.wag.egov.egiz.exceptions.PresentableException; +import at.knowcenter.wag.egov.egiz.sig.SignatureObject; + +import com.lowagie.text.Rectangle; +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; +import com.lowagie.text.pdf.PdfString; +import com.lowagie.text.pdf.PdfTemplate; +import com.lowagie.text.pdfas.StructContentWriter; +import com.lowagie.text.pdfas.StructContentWriterHolder; +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 + * 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 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 Map linkPosMap = new HashMap(); + private Map tmpMap = new HashMap(); + + private PdfStamper stamper; + private PdfStamperImp stamperImp; + private PdfContentByte content; + private PdfDictionary page; + private PdfNumber parentTreeNextKey = null; + private PdfNumber annotationParentTreeKey = null; + + /** + * Temporary save a pos + */ + private Rectangle tempMarkedPos = null; + /** + * 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 + */ + 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(); + } + + /** + * Prepare structured content for signature block. This method initializes the whole StructTreeRoot stuff. + * @param sigBlockObj + * @throws PresentableException + */ + void prepareStructData(PdfTemplate sigBlockObj) throws PresentableException { + + try { + checkTagging(); + if (!isTagged) { + 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); + + 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 + */ + void buildSigBlockStructData() throws PresentableException { + if (!isTagged) return; + try { + PdfIndirectReference newStructRef = createStructElem(SIGBLOCK_STRUCT_TYPE, new PdfNumber( + sigBlockMcid), getStructTreeRoot().getIndRef()); + + // ADD everything at the end because nothing can be written afterwards + structTreeRootKids.add(newStructRef); + mainParentTreeNumEntry.add(newStructRef); + + stamperImp.markUsed(mainParentTreeNumEntry); + + } catch (Exception ex) { + logger.error("error", ex); + throw new PresentableException(ErrorCode.CANNOT_WRITE_PDF, + "error writing structured signature content", ex); + } + } + + + /** + * Finish struct data for signblock and it's elements (NOT for the external link and annot!) + * @throws PresentableException + */ + void finishMainStructData() throws PresentableException { + try { + if (isTagged && mainParentTreeNumEntry.getIndRef() == null) { + getParentTreeNums().add( + stamper.getWriter().addToBody(mainParentTreeNumEntry).getIndirectReference()); + stamperImp.markUsed(getParentTreeNums()); + stamperImp.markUsed(getStructTreeRoot().getAsDict(PdfName.PARENTTREE)); + } + } catch (Exception ex) { + logger.error("error", ex); + throw new PresentableException(ErrorCode.CANNOT_WRITE_PDF, + "error writing structured signature content", ex); + } + } + + /** + * 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()) { + + PdfDictionary structTreeRoot = getStructTreeRoot(); + PdfIndirectReference mcrRef = createMcrStructElem(this.figureMcid, sigBlockObj.getIndirectReference()); + PdfIndirectReference figureRef = createStructElem("Figure", mcrRef, + getAltText(so.getSignatureTypeDefinition().getType()), structTreeRoot.getIndRef()); + + structTreeRootKids.add(figureRef); + mainParentTreeNumEntry.add(figureRef); + + stamperImp.markUsed(structTreeRootKids); + stamperImp.markUsed(structTreeRoot); + stamperImp.markUsed(mainParentTreeNumEntry); + + } + + } 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 + * @param atp + * @throws PresentableException + */ + void buildVerifyLinkStructData(PdfTemplate sigBlockObj, ActualTablePos atp) throws PresentableException { + if (!this.isTagged || !this.isLinkMarked() || !isLinkFound()) return; + + try { + PdfNumber parentTreeKey = getNewParentTreeKey(); + + PdfArray annots = obrainAnnotsFromPage(); + + PdfIndirectReference linkAnnotRef = createLinkAnnot(parentTreeKey, atp); + 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); + } + } + + private boolean isLinkFound() { + return this.linkUrlString != null && this.verifyLinkCellPos != null && this.linkPosMap.size() > 0 && this.linkMcid >= 0; + } + + /** + * Build new StructParent entry for signature annotation. + * @return + */ + PdfNumber buildAdobeSigStructParent() { + if (this.isTagged) { + this.annotationParentTreeKey = getNewParentTreeKey(); + return annotationParentTreeKey; + } else { + return null; + } + } + + /** + * 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(); + + PdfIndirectReference objrRef = createObjrStructElem(sigFormField.getIndirectReference()); + + PdfIndirectReference adobeSigStructRef = createStructElem("Link", objrRef, root.getIndRef()); + + PdfArray parentTreeNums = getParentTreeNums(); + // create new entry in ParentTree + parentTreeNums.add(annotationParentTreeKey); + parentTreeNums.add(adobeSigStructRef); + + structTreeRootKids.add(adobeSigStructRef); + stamperImp.markUsed(structTreeRootKids); + + 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(SIGBLOCK_STRUCT_TYPE).getBytes()).append(" <> BDC").append('\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(); + } + } + + /** + * Writes start tag for signature logo marked content sequence. + */ + 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)"); + } + } + } + + /** + * 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"); + } + } + } + + /** + * Writes end tag for verify link marked content sequence. + */ + public void endLinkContent(PdfContentByte localContent) { + if (isTagged && isLinkMarked()) { + localContent.endMarkedContentSequence(); + } + } + + /** + * Implements {@link StructContentWriter#markPos(Rectangle)} + */ + public void markPos(Rectangle pos) { + this.tempMarkedPos = pos; + } + + /** + * Implements {@link StructContentWriter#storeCurrentPosAsLink()} + */ + public void storeCurrentPosAsLink() { + this.verifyLinkCellPos = new Rectangle(this.tempMarkedPos); + } + + public void putVal(String key, Object val) { + tmpMap.put(key, val); + } + + public void storeVals() { + linkPosMap = new HashMap(tmpMap); + } + + /** + * 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); + } + } + + 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."); + } + + + + private PdfIndirectReference createLinkAnnot(PdfNumber structParentNr, ActualTablePos atp) 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); + + 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(); + } + + + private PdfArray calcLinkPos(ActualTablePos atp) { + PdfArray res = new PdfArray(); + + float downY = atp.y - atp.height; + + float startX = atp.x + this.verifyLinkCellPos.getLeft(); + float yLine = getPosMapVal("yLine"); + float lineHigh = getPosMapVal("maxSize"); + float lineWidth = getPosMapVal("lineWidth"); + UrlInTextFinder finder = (UrlInTextFinder) this.linkPosMap.get("urlFinder"); + + // maybe one could calc the link pos even more exactly with char width counting + // but this should be close enough (see BidiLine.processLine and chunk.getcharwith) + float lineCorr = -2; + float xCorr = 5; + res.add(new PdfNumber(1 + startX + finder.calcLinkPosXStart(lineWidth))); + res.add(new PdfNumber(downY + yLine + lineHigh + lineCorr)); + res.add(new PdfNumber(xCorr + startX + finder.calcLinkPosXEnd(lineWidth))); + res.add(new PdfNumber(downY + yLine + lineCorr)); + + return res; + } + + 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); + if (annots == null) { + annots = new PdfArray(); + page.put(PdfName.ANNOTS, annots); + stamperImp.markUsed(this.page); + stamper.getWriter().addToBody(annots); + } + return annots; + } + + 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 if(root_k != null) { // 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(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); +// } + + 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")); + + 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); + + 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 + } + 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() { + 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