iTextSharp重复使用Acrofield中嵌入的字体

“iText In Action”中有一些提示,包括设置字体,以及“FontFactory.RegisterDirectories”方法(正如书中所说的那样……一个昂贵的调用)。 但是,就我而言,我想用于新字段的字体已经嵌入到文档中(在现有的Acrofield中)。 不能保证在用户的机器上(或在Web服务器上)存在相同的字体….有没有办法可以注册已经嵌入的字体,以便我可以将它重新用于其他对象? 在下面的代码中,Acrofield“TheFieldIWantTheFontFrom”具有我想要为名为“my_new_field”的字段重用的字体。 任何帮助将不胜感激!

using (MemoryStream output = new MemoryStream()) { // Use iTextSharp PDF Reader, to get the fields and send to the //Stamper to set the fields in the document PdfReader pdfReader = new PdfReader(@"C:\MadScience\MSE_030414.pdf"); // Initialize Stamper (ms is a MemoryStream object) PdfStamper pdfStamper = new PdfStamper(pdfReader, output); // Get Reference to PDF Document Fields AcroFields pdfFormFields = pdfStamper.AcroFields; //*** CODE THAT HAVE NOT YET BEEN ABLE TO MAKE USE OF TO ASSIST WITH MY FONT ISSUE //*** MIGHT BE HELP? //List fonts = BaseFont.GetDocumentFonts(pdfReader); //BaseFont[] baseFonts = new BaseFont[fonts.Count]; //string[] fn = new string[fonts.Count]; //for (int i = 0; i < fonts.Count; i++) //{ // Object[] obj = (Object[])fonts[i]; // baseFonts[i] = BaseFont.CreateFont((PRIndirectReference)(obj[1])); // fn[i] = baseFonts[i].PostscriptFontName.ToString(); // //Console.WriteLine(baseFonts[i].FamilyFontName[0][1].ToString()); // //FontFactory.RegisteredFonts.Add(fn[i]); // //FontFactory.Register( // Console.WriteLine(fn[i]); //} //ICollection registeredFonts = iTextSharp.text.FontFactory.RegisteredFonts; //foreach (string s in registeredFonts) //{ // Console.WriteLine("pre-registered: " + s); //} if (!FontFactory.Contains("georgia-bold")) { FontFactory.RegisterDirectories(); Console.WriteLine("had to register everything"); } //registeredFonts = iTextSharp.text.FontFactory.RegisteredFonts; //foreach (string s in registeredFonts) //{ // Console.WriteLine("post-registered: " + s); //} Font myfont = FontFactory.GetFont("georgia-bold"); string nameOfField = "my_field"; AcroFields.Item fld = pdfFormFields.GetFieldItem(nameOfField); //set the text of the form field pdfFormFields.SetField(nameOfField, "test stuff"); pdfFormFields.SetField("TheFieldIWantTheFontFrom", "test more stuff"); bool madeit = pdfFormFields.SetFieldProperty(nameOfField, "textfont", myfont.BaseFont, null); bool madeit2 = pdfFormFields.SetFieldProperty(nameOfField, "textsize", 8f, null); pdfFormFields.RegenerateField(nameOfField); // Set the flattening flag to false, so the document can continue to be edited pdfStamper.FormFlattening = true; // close the pdf stamper pdfStamper.Close(); //get the bytes from the MemoryStream byte[] content = output.ToArray(); using (FileStream fs = File.Create(@"C:\MadScience\MSE_Results.pdf")) { //byte[] b = outList[i]; fs.Write(content, 0, (int)content.Length); fs.Flush(); } } 

是的,您可以重复使用字体,PDF规范实际上鼓励它。 但是,您应该记住,某些字体可能仅作为子集嵌入。

下面的代码改编自这篇文章 (小心,该网站有时会有令人讨厌的弹出窗口)。 有关更多信息,请参阅代码中的注释。 此代码针对iTextSharp 5.4.4进行了测试。

 ///  /// Look for the given font name (not file name) in the supplied PdfReader's AcroForm dictionary. ///  /// An open PdfReader to search for fonts in. /// The font's name as listed in the PDF. /// A BaseFont object if the font is found or null. static BaseFont findFontInForm(PdfReader reader, String fontName) { //Get the document's acroform dictionary PdfDictionary acroForm = (PdfDictionary)PdfReader.GetPdfObject(reader.Catalog.Get(PdfName.ACROFORM)); //Bail if there isn't one if (acroForm == null) { return null; } //Get the resource dictionary var DR = acroForm.GetAsDict(PdfName.DR); //Get the font dictionary (required per spec) var FONT = DR.GetAsDict(PdfName.FONT); //Look for the actual font and return it return findFontInFontDict(FONT, fontName); } ///  /// Helper method to look at a specific font dictionary for a given font string. ///  ///  /// This method is a helper method and should not be called directly without knowledge of /// the internals of the PDF spec. ///  /// A /FONT dictionary. /// Optional. The font's name as listed in the PDF. If not supplied then the first font found is returned. /// A BaseFont object if the font is found or null. static BaseFont findFontInFontDict(PdfDictionary fontDict, string fontName) { //This code is adapted from http://osdir.com/ml/java.lib.itext.general/2004-09/msg00018.html foreach (var internalFontName in fontDict.Keys) { var internalFontDict = (PdfDictionary)PdfReader.GetPdfObject(fontDict.Get(internalFontName)); var baseFontName = (PdfName)PdfReader.GetPdfObject(internalFontDict.Get(PdfName.BASEFONT)); //// compare names, ignoring the initial '/' in the baseFontName if (fontName == null || baseFontName.ToString().IndexOf(fontName) == 1) { var iRef = (PRIndirectReference)fontDict.GetAsIndirectObject(internalFontName); if (iRef != null) { return BaseFont.CreateFont(iRef); } } } return null; } 

这是运行它的测试代码。 它首先创建一个带有嵌入字体的示例文档,然后根据该文档创建第二个文档并重新使用该字体。 在您的代码中,您需要事先确实知道您要搜索的字体名称。 如果你没有安装ROCK.TTF(罗克韦尔),你需要选择一个不同的字体文件来运行它。

 //Test file that we'll create with an embedded font var file1 = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "test.pdf"); //Secondary file that we'll try to re-use the font above from var file2 = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "test2.pdf"); //Path to font file that we'd like to use var fontFilePath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Fonts), "ROCK.TTF"); //Create a basefont object var font = BaseFont.CreateFont(fontFilePath, BaseFont.WINANSI, true); //Get the name that we're going to be searching for later on. var searchForFontName = font.PostscriptFontName; //Step #1 - Create sample document //The below block creates a sample PDF file with an embedded font in an AcroForm, nothing too special using (var fs = new FileStream(file1, FileMode.Create, FileAccess.Write, FileShare.None)) { using (var doc = new Document()) { using (var writer = PdfWriter.GetInstance(doc, fs)) { doc.Open(); //Create our field, set the font and add it to the document var tf = new TextField(writer, new iTextSharp.text.Rectangle(50, 50, 400, 150), "first-name"); tf.Font = font; writer.AddAnnotation(tf.GetTextField()); doc.Close(); } } } //Step #2 - Look for font //This uses a stamper to draw on top of the existing PDF using a font already embedded using (var fs = new FileStream(file2, FileMode.Create, FileAccess.Write, FileShare.None)) { using (var reader = new PdfReader(file1)) { using (var stamper = new PdfStamper(reader, fs)) { //Try to get the font file var f = findFontInForm(reader, searchForFontName); //Make sure we found something if (f != null) { //Draw some text var cb = stamper.GetOverContent(1); cb.BeginText(); cb.MoveText(200, 400); cb.SetFontAndSize(f, 72); cb.ShowText("Hello!"); cb.EndText(); } } } } 

编辑

我对上面的findFontInFontDict方法进行了一些小修改。 第二个参数现在是可选的。 如果为null,则返回它在提供的字典中找到的第一个字体对象。 此更改允许我引入以下方法,该方法按名称查找特定字段并获取字体。

 static BaseFont findFontByFieldName(PdfReader reader, String fieldName) { //Get the document's acroform dictionary PdfDictionary acroForm = (PdfDictionary)PdfReader.GetPdfObject(reader.Catalog.Get(PdfName.ACROFORM)); //Bail if there isn't one if (acroForm == null) { return null; } //Get the fields array var FIELDS = acroForm.GetAsArray(PdfName.FIELDS); if (FIELDS == null || FIELDS.Length == 0) { return null; } //Loop through each field reference foreach (var fieldIR in FIELDS) { var field = (PdfDictionary)PdfReader.GetPdfObject(fieldIR); //Check the field name against the supplied field name if (field.GetAsString(PdfName.T).ToString() == fieldName) { //Get the resource dictionary var DR = acroForm.GetAsDict(PdfName.DR); //Get the font dictionary (required per spec) var FONT = DR.GetAsDict(PdfName.FONT); return findFontInFontDict(FONT); } } return null; }