【问题标题】:iTextSharp Re-use Font Embedded In AcrofieldiTextSharp 重用嵌入 Acrofield 的字体
【发布时间】:2014-04-06 19:37:59
【问题描述】:

在“iText In Action”中有一些技巧,包括设置字体,以及“FontFactory.RegisterDirectories”方法(正如书中所说......一个昂贵的调用)。但是,就我而言,我想用于新字段的字体已经嵌入到文档中(在现有的 Acrofield 中)。不保证用户的机器(或网络服务器)上会存在相同的字体......有没有办法可以注册已经嵌入的字体,以便我可以将它重新用于其他对象?在下面的代码中,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<object[]> 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<string> 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();
            }

        }

【问题讨论】:

    标签: c# .net pdf fonts itextsharp


    【解决方案1】:

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

    以下代码改编自this post(请注意,该网站有时会出现令人讨厌的弹出窗口)。有关详细信息,请参阅代码中的 cmets。此代码已针对 iTextSharp 5.4.4 进行了测试。

    /// <summary>
    /// Look for the given font name (not file name) in the supplied PdfReader's AcroForm dictionary.
    /// </summary>
    /// <param name="reader">An open PdfReader to search for fonts in.</param>
    /// <param name="fontName">The font's name as listed in the PDF.</param>
    /// <returns>A BaseFont object if the font is found or null.</returns>
    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);
    }
    
    /// <summary>
    /// Helper method to look at a specific font dictionary for a given font string.
    /// </summary>
    /// <remarks>
    /// This method is a helper method and should not be called directly without knowledge of
    /// the internals of the PDF spec.
    /// </remarks>
    /// <param name="fontDict">A /FONT dictionary.</param>
    /// <param name="fontName">Optional. The font's name as listed in the PDF. If not supplied then the first font found is returned.</param>
    /// <returns>A BaseFont object if the font is found or null.</returns>
    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 (Rockwell),则需要选择不同的字体文件来运行它。

    //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;
    }
    

    【讨论】:

    • 克里斯太棒了!你有魔法!这很好用。如果我想查找特定 Acrofield 的字体名称,我该怎么做?在此示例中,字体名称为“Georgia,Bold”,而不是 FontFactory.RegisterDirectories 方法找到的“georgia-bold”。
    • 这个答案+1。我正在为我的下一本书收集示例,这个示例看起来很适合写进那本书。
    • @cro717,我还没有机会跳回去,但我今晚可以试试。
    • 非常感谢克里斯。谢谢!
    • @cro717,我终于有机会更新上面的代码以针对特定字段
    猜你喜欢
    • 2016-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多