“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<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();
}
}
答案 0 :(得分:3)
是的,您可以重复使用字体,PDF规范实际上鼓励它。但是,您应该记住,某些字体可能仅作为子集嵌入。
以下代码改编自this post(小心,该网站有时会出现令人讨厌的弹出窗口)。有关更多信息,请参阅代码中的注释。此代码已针对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(罗克韦尔),你需要选择一个不同的字体文件来运行它。
//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;
}