iTextSharp 重用嵌入在 Acrofield 中的字体 [英] iTextSharp Re-use Font Embedded In Acrofield

查看:30
本文介绍了iTextSharp 重用嵌入在 Acrofield 中的字体的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

There are tips in "iText In Action" that cover setting fonts, as well as the "FontFactory.RegisterDirectories" method (which is, as the book says...an expensive call). However, in my case, the font that I want to use for new fields is already embedded in the document (in an existing Acrofield). With no guarantee that the same font will exist on the user's machine (or on a web server)....is there a way that I can register that already-embedded font, so that I can re-use it for other objects? In the code below, Acrofield "TheFieldIWantTheFontFrom" has the font that I want to re-use for a field named "my_new_field". Any help would be greatly appreciated!

        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:MadScienceMSE_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:MadScienceMSE_Results.pdf"))
            {
                //byte[] b = outList[i];
                fs.Write(content, 0, (int)content.Length);
                fs.Flush();
            }

        }

推荐答案

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

Yes you can re-use fonts and the PDF specification actually encourages it. You should, however, keep in mind that some fonts may be embedded as subsets only.

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

The below code is adapted from this post (be careful, that site has nasty popups sometimes). See the comments in the code for more information. This code was tested against 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),您需要选择不同的字体文件来运行它.

And here's the test code that runs this. It first creates a sample document with an embedded font and then it creates a second document based upon that and re-uses that font. In your code you'll need to actually know beforehand what the font name is that you're searching for. If you don't have ROCK.TTF (Rockwell) installed you'll need to pick a different font file to run this.

//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,则返回在提供的字典中找到的第一个字体对象.此更改允许我引入以下方法,该方法按名称查找特定字段并获取字体.

I made a small modification to the findFontInFontDict method above. The second parameter is now optional. If null it returns the first font object that it finds in the supplied dictionary. This change allows me to introduce the below method which looks for a specific field by name and gets the font.

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

这篇关于iTextSharp 重用嵌入在 Acrofield 中的字体的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆