/** * Signs a PDF where space was already reserved. * @param reader the original PDF * @param fieldName the field to sign. It must be the last field * @param outs the output PDF * @param externalSignatureContainer the signature container doing the actual signing. Only the * method ExternalSignatureContainer.sign is used * @throws DocumentException * @throws IOException * @throws GeneralSecurityException */ public static void SignDeferred(PdfReader reader, String fieldName, Stream outs, IExternalSignatureContainer externalSignatureContainer) { AcroFields af = reader.AcroFields; PdfDictionary v = af.GetSignatureDictionary(fieldName); if (v == null) throw new DocumentException("No field"); if (!af.SignatureCoversWholeDocument(fieldName)) throw new DocumentException("Not the last signature"); PdfArray b = v.GetAsArray(PdfName.BYTERANGE); long[] gaps = b.AsLongArray(); if (b.Size != 4 || gaps[0] != 0) throw new DocumentException("Single exclusion space supported"); IRandomAccessSource readerSource = reader.SafeFile.CreateSourceView(); Stream rg = new RASInputStream(new RandomAccessSourceFactory().CreateRanged(readerSource, gaps)); byte[] signedContent = externalSignatureContainer.Sign(rg); int spaceAvailable = (int)(gaps[2] - gaps[1]) - 2; if ((spaceAvailable & 1) != 0) throw new DocumentException("Gap is not a multiple of 2"); spaceAvailable /= 2; if (spaceAvailable < signedContent.Length) throw new DocumentException("Not enough space"); StreamUtil.CopyBytes(readerSource, 0, gaps[1] + 1, outs); ByteBuffer bb = new ByteBuffer(spaceAvailable * 2); foreach (byte bi in signedContent) { bb.AppendHex(bi); } int remain = (spaceAvailable - signedContent.Length) * 2; for (int k = 0; k < remain; ++k) { bb.Append((byte)48); } bb.WriteTo(outs); StreamUtil.CopyBytes(readerSource, gaps[2] - 1, gaps[3] + 1, outs); }
/** * Converts an annotation structure item to a Form XObject annotation. * @param item the structure item * @throws IOException */ virtual protected void ConvertToXObject(StructureObject item) { PdfDictionary structElem = item.GetStructElem(); if (structElem == null) return; PdfDictionary dict = item.GetObjAsDict(); if (dict == null || !dict.CheckType(PdfName.ANNOT)) return; PdfDictionary ap = dict.GetAsDict(PdfName.AP); if (ap == null) return; PdfNumber structParent = dict.GetAsNumber(PdfName.STRUCTPARENT); if (structParent == null) return; PdfStream stream = ap.GetAsStream(PdfName.N); if (stream == null) return; stream.Put(PdfName.STRUCTPARENT, structParent); PdfIndirectReference xobjr = ap.GetAsIndirectObject(PdfName.N); if (xobjr == null) return; // remove the annotation from the page for (int i = 0; i < annots.Length; i++) { PdfIndirectReference annotref = annots.GetAsIndirectObject(i); if (item.GetObjRef().Number == annotref.Number) { annots.Remove(i); break; } } // replace the existing attributes by a PrintField attribute PdfDictionary attribute = new PdfDictionary(); attribute.Put(PdfName.O, PdfName.PRINTFIELD); PdfString description = dict.GetAsString(PdfName.TU); if (description == null) description = dict.GetAsString(PdfName.T); if (PdfName.BTN.Equals(dict.Get(PdfName.FT))) { PdfNumber fflags = dict.GetAsNumber(PdfName.FF); if (fflags != null) { int ff = fflags.IntValue; if ((ff & PdfFormField.FF_PUSHBUTTON) != 0) attribute.Put(PdfName.ROLE, PdfName.PB); // I don't think the condition below will ever be true if ((ff & PdfFormField.FF_RADIO) != 0) attribute.Put(PdfName.ROLE, PdfName.rb); else attribute.Put(PdfName.ROLE, PdfName.CB); } } else { attribute.Put(PdfName.ROLE, PdfName.TV); } attribute.Put(PdfName.DESC, description); // Updating the values of the StructElem dictionary PdfString t = structElem.GetAsString(PdfName.T); if (t == null || t.ToString().Trim().Length == 0) structElem.Put(PdfName.T, dict.GetAsString(PdfName.T)); structElem.Put(PdfName.A, attribute); structElem.Put(PdfName.S, PdfName.P); structElem.Put(PdfName.PG, pageref); // Defining a new MCID int mcid = items.ProcessMCID(structParents, item.GetRef()); LOGGER.Info("Using MCID " + mcid); structElem.Put(PdfName.K, new PdfNumber(mcid)); // removing the annotation from the parent tree items.RemoveFromParentTree(structParent); // Adding the XObject to the page PdfName xobj = new PdfName("XObj" + structParent.IntValue); LOGGER.Info("Creating XObject with name " + xobj); xobjects.Put(xobj, xobjr); PdfArray array = dict.GetAsArray(PdfName.RECT); // Getting the position of the annotation Rectangle rect = new Rectangle( array.GetAsNumber(0).FloatValue, array.GetAsNumber(1).FloatValue, array.GetAsNumber(2).FloatValue, array.GetAsNumber(3).FloatValue); rect.Normalize(); // A Do operator is forbidden inside a text block if (inText && !btWrite) { LOGGER.Debug("Introducing extra ET"); byte[] bytes = Encoding.ASCII.GetBytes("ET\n"); baos.Write(bytes, 0, bytes.Length); etExtra = true; } // Writing the marked-content sequence with the Do operator // Note that the position assumes that the CTM wasn't changed in the graphics state // TODO: do the math if the CTM did change! ByteBuffer buf = new ByteBuffer(); buf.Append("/P <</MCID "); buf.Append(mcid); buf.Append(">> BDC\n"); buf.Append("q 1 0 0 1 "); buf.Append(rect.Left.ToString(CultureInfo.InvariantCulture)); buf.Append(" "); buf.Append(rect.Bottom.ToString(CultureInfo.InvariantCulture)); buf.Append(" cm "); buf.Append(xobj.GetBytes()); buf.Append(" Do Q\n"); buf.Append("EMC\n"); buf.Flush(); buf.WriteTo(baos); // if we were inside a text block, we've introduced an ET, so we'll need to write a BT if (inText) btWrite = true; }