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

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

问题描述

有在iText的在行动,涵盖设置字体,以及FontFactory.RegisterDirectories的方法(这是因为书上说......一个昂贵的调用)的提示。但是,在我的情况,我想用新的领域的字体已嵌入到文档中(在现有Acrofield)。由于不能保证相同的字体将存在于用户的机器(或Web服务器)上....是没有办法,我可以注册已经嵌入字体,这样我就可以重新使用它的其他对象的方法吗?在code以下,AcrofieldTheFieldIWantTheFontFrom有,我想重新使用了一个名为my_new_field字段中的字体。任何帮助将大大AP preciated!

 使用(MemoryStream的输出=新的MemoryStream())
        {

            //使用iTextSharp的PDF阅读器,以获得字段和发送到
            //印章来设置文档中的字段
            PdfReader pdfReader =新PdfReader(@C:\ MadScience \ MSE_030414.pdf);

            //初始化加工厂(MS是MemoryStream对象)
            PdfStamper pdfStamper =新PdfStamper(pdfReader,输出);

            //获取参考,以PDF文档字段
            AcroFields pdfFormFields = pdfStamper.AcroFields;

            // *** code尚未得以充分利用,以帮助我的字体发行
            // ***可能是帮助?
            //名单,其中,对象[] GT;字体= BaseFont.GetDocumentFonts(pdfReader);
            // BASEFONT [] baseFonts =新BASEFONT [fonts.Count]
            //字符串[] FN =新的字符串[fonts.Count]
            //的for(int i = 0; I< fonts.Count;我++)
            // {
            //对象[] OBJ =(对象[])字体[I]
            // baseFonts [I] = BaseFont.CreateFont((PRIndirectReference)(物镜[1]));
            // FN [我] = 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(在registeredFonts字符串s)
            // {
            // Console.WriteLine(pre-注册:+ S);
            //}

            如果(!FontFactory.Contains(格粗体))
            {
                FontFactory.RegisterDirectories();
                Console.WriteLine(有登记的一切); }

            // registeredFonts = iTextSharp.text.FontFactory.RegisteredFonts;

            //的foreach(在registeredFonts字符串s)
            // {
            // Console.WriteLine(后注册成功。+ S);
            //}

            字体myfont = FontFactory.GetFont(格粗体);

            字符串nameOfField =my_field;

            AcroFields.Item FLD = pdfFormFields.GetFieldItem(nameOfField);

            //设置窗体字段的文本
            pdfFormFields.SetField(nameOfField,考的东西);

            pdfFormFields.SetField(TheFieldIWantTheFontFrom,测试更多的东西);

            布尔madeit = pdfFormFields.SetFieldProperty(nameOfField,TEXTFONT,myfont.BaseFont,NULL);

            布尔madeit2 = pdfFormFields.SetFieldProperty(nameOfField,TEXTSIZE,8F,NULL);

            pdfFormFields.RegenerateField(nameOfField);

            //设置扁平化标志设置为false,那么该文件可以继续编辑
            pdfStamper.FormFlattening = TRUE;

            //关闭PDF压模
            pdfStamper.Close();

            //从MemoryStream的字节
            byte []的内容= output.ToArray();

            使用(的FileStream FS = File.Create(@C:\ MadScience \ MSE_Results.pdf))
            {
                // byte []的B = outList [I]
                fs.Write(含量,0,(int)的content.Length);
                fs.Flush();
            }

        }
 

解决方案

是的,你可以重复使用的字体和PDF规范实际上鼓励它。你应该,但是,请记住,有些字体可以嵌入作为唯一的子集。

下面code是改编自这帖子(注意,该网站有讨厌的弹出窗口有时)。请参见code中的注释以获取更多信息。这code经测试,iTextSharp的5.4.4。

  ///<总结>
///寻找给定的字体名称(不是文件名)中提供的PdfReader的AcroForm字典。
///< /总结>
///< PARAM NAME =读者>开放PdfReader搜索在字体< /参数>
///< PARAM NAME =的fontName>如在PDF中列出的字体的名称和LT; /参数>
///<返回>一种BASEFONT对象,如果字体被发现或空< /回报>
静态BASEFONT findFontInForm(PdfReader读者,字符串的fontName){
    //获取文档的acroform字典
    PdfDictionary acroForm =(PdfDictionary)PdfReader.GetPdfObject(reader.Catalog.Get(PdfName.ACROFORM));

    //保释如果没有一个
    如果(acroForm == NULL){
        返回null;
    }

    //获取资源字典
    变种DR = acroForm.GetAsDict(PdfName.DR);

    //获取字体库(按规格要求)
    VAR FONT = DR.GetAsDict(PdfName.FONT);

    //寻找实际的字体,并将其返回
    返回findFontInFontDict(字体,的fontName);
}

///<总结>
/// Helper方法来看待一个特定的字体字典给定字体的字符串。
///< /总结>
///<说明>
///此方法是一个辅助方法,不应该被直接调用而不知识
///的PDF规范的内部。
///< /说明>
///< PARAM NAME =fontDict> A / FONT词典和LT; /参数>
///< PARAM NAME =的fontName>可选。如在PDF中列出的字体的名称。如果没有提供,然后找到的第一个字体返回< /参数>
///<返回>一种BASEFONT对象,如果字体被发现或空< /回报>
静态BASEFONT findFontInFontDict(PdfDictionary fontDict,字符串的fontName){
    //这个code是改编自http://osdir.com/ml/java.lib.itext.general/2004-09/msg00018.html
    的foreach(在fontDict.Keys VAR internalFontName){
        变种internalFontDict =(PdfDictionary)PdfReader.GetPdfObject(fontDict.Get(internalFontName));
        变种baseFontName =(PdfName)PdfReader.GetPdfObject(internalFontDict.Get(PdfName.BASEFONT));
        ////比较名称,忽略baseFontName初始'/'
        如果(的fontName == NULL || baseFontName.ToString()的IndexOf(的fontName)== 1){
            VAR IREF =(PRIndirectReference)fontDict.GetAsIndirectObject(internalFontName);
            如果(IREF!= NULL){
                返回BaseFont.CreateFont(IREF);
            }
        }
    }

    返回null;
}
 

下面是运行此测试code。它首先创建嵌入字体的样本文件,然后创建基于该第二个文件,并重新使用该字体。在您的code,你需要真正预先知道的字体名称是您正在寻找的。如果您没有ROCK.TTF(洛氏)安装你需要选择一个不同的字体文件来运行这个。

  //测试文件,我们将创建一个带有嵌入字体
VAR文件1 = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop)的test.pdf);

//辅助文件,我们会尝试从再使用上面的字体
变种FILE2 = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop)条件,test2.pdf);

//路径字体文件,我们想使用
变种fontFilePath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Fonts),ROCK.TTF);

//创建一个BASEFONT对象
VAR字体= BaseFont.CreateFont(fontFilePath,BaseFont.WINANSI,真正的);

//获得我们要进行搜索后的名称。
VAR searchForFontName = font.PostscriptFontName;

//第1步 - 创建示例文件
//下面块的AcroForm创建了一个样本PDF文件嵌入字体,没有什么太特别的
使用(VAR FS =新的FileStream(文件1,FileMode.Create,FileAccess.Write,FileShare.None)){
    使用(VAR DOC =新的文件()){
        使用(VAR作家= PdfWriter.GetInstance(DOC,FS)){
            doc.Open();

            //创建我们的现场,设置字体,并将其添加到文档
            变种TF =新的TextField(作家,新iTextSharp.text.Rectangle(50,50,400,150),直呼其名);
            tf.Font =字体;
            writer.AddAnnotation(tf.GetTextField());

            doc.Close();
        }
    }
}

//第二步 - 寻找字体
//这将使用一个压模使用的字体在现有的PDF之上绘制已经嵌入
使用(VAR FS =新的FileStream(文件2,FileMode.Create,FileAccess.Write,FileShare.None)){
    使用(VAR读卡器=新PdfReader(文件1)){
        使用(VAR压模=新PdfStamper(读卡器,FS)){

            //尝试获取字体文件
            变种F = findFontInForm(读卡器,searchForFontName);

            //确保我们发现了一些
            如果(F!= NULL){

                //绘制一些文本
                变种CB = stamper.GetOverContent(1);
                cb.BeginText();
                cb.MoveText(200,400);
                cb.SetFontAndSize(男,72);
                cb.ShowText(你好!);
                cb.EndText();
            }
        }
    }
}
 

修改

我做了一个小的修改到 findFontInFontDict 上述方法。第二个参数现在是可选的。如果为null,则返回的第一个字体对象,发现在所提供的词典。这种变化让我来介绍以下方法,寻找特定的字段​​名称和获得的字体。

 静态BASEFONT findFontByFieldName(PdfReader读者,字符串字段名){
    //获取文档的acroform字典
    PdfDictionary acroForm =(PdfDictionary)PdfReader.GetPdfObject(reader.Catalog.Get(PdfName.ACROFORM));

    //保释如果没有一个
    如果(acroForm == NULL){
        返回null;
    }

    //获取fields数组
    VAR栏= acroForm.GetAsArray(PdfName.FIELDS);
    如果(FIELDS == NULL || FIELDS.Length == 0){
        返回null;
    }

    //循环遍历每个字段参考
    的foreach(在字段中VAR fieldIR){
        VAR场=(PdfDictionary)PdfReader.GetPdfObject(fieldIR);

        //检查字段名对所提供的字段名
        如果(field.GetAsString(PdfName.T)的ToString()== fieldName的){

            //获取资源字典
            变种DR = acroForm.GetAsDict(PdfName.DR);

            //获取字体库(按规格要求)
            VAR FONT = DR.GetAsDict(PdfName.FONT);

            返回findFontInFontDict(字体);
        }
    }

    返回null;
}
 

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:\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();
            }

        }

解决方案

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.

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

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

EDIT

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天全站免登陆