iTextSharp Réutiliser les fonts incorporées dans Acrofield

“IText In Action” contient des astuces qui traitent du paramétrage des fonts, ainsi que de la méthode “FontFactory.RegisterDirectories” (qui, comme le dit le livre … est un appel coûteux). Cependant, dans mon cas, la police que je souhaite utiliser pour les nouveaux champs est déjà intégrée au document (dans un Acrofield existant). Sans garantie que la même police existera sur la machine de l’utilisateur (ou sur un serveur Web)…. Y a-t-il moyen de pouvoir enregistrer cette police déjà intégrée afin de pouvoir la réutiliser pour d’autres objects? Dans le code ci-dessous, Acrofield “TheFieldIWantTheFontFrom” a la police que je souhaite réutiliser pour un champ nommé “my_new_field”. Toute aide serait grandement appréciée!

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]; //ssortingng[] fn = new ssortingng[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 (ssortingng 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 (ssortingng s in registeredFonts) //{ // Console.WriteLine("post-registered: " + s); //} Font myfont = FontFactory.GetFont("georgia-bold"); ssortingng 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(); } } 

Oui, vous pouvez réutiliser des fonts et la spécification PDF l’encourage réellement. Gardez toutefois à l’esprit que certaines fonts peuvent être incorporées en tant que sous-ensembles uniquement.

Le code ci-dessous est adapté de ce message (attention, ce site contient parfois de mauvaises fenêtres popup). Voir les commentaires dans le code pour plus d’informations. Ce code a été testé sur 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, Ssortingng 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 ssortingng. ///  ///  /// 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, ssortingng 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.ToSsortingng().IndexOf(fontName) == 1) { var iRef = (PRIndirectReference)fontDict.GetAsIndirectObject(internalFontName); if (iRef != null) { return BaseFont.CreateFont(iRef); } } } return null; } 

Et voici le code de test qui exécute ceci. Il crée d’abord un exemple de document avec une police incorporée, puis crée un deuxième document basé sur celui-ci et réutilise cette police. Dans votre code, vous devez réellement connaître à l’avance le nom de la police que vous recherchez. Si ROCK.TTF (Rockwell) n’est pas installé, vous devrez choisir un fichier de police différent pour l’exécuter.

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

MODIFIER

J’ai apporté une petite modification à la méthode findFontInFontDict ci-dessus. Le second paramètre est maintenant optionnel. Si la valeur est null, le premier object de police trouvé dans le dictionnaire fourni est renvoyé. Cette modification me permet d’introduire la méthode ci-dessous, qui recherche un champ spécifique par son nom et obtient la police.

 static BaseFont findFontByFieldName(PdfReader reader, Ssortingng 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.GetAsSsortingng(PdfName.T).ToSsortingng() == 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; }