Q: How do I embed fonts for existing PDFs with missing fonts? How do I
replace a font in an existing PDF?
We are trying to substitute the fonts using following code.
PDFNet.Initialize();
PDFNet.AddFontSubst("Times-Roman", "C:/WINNT/Fonts/times.ttf");
...
PDFDoc sourceDoc=new PDFDoc(sourceFile);
Page page=null;
for( ... each page ...) {
Obj res = page.GetResourceDict();
if (res != null) {
Obj fonts = res.FindObj("Font");
if (fonts != null)
{
DictIterator itr = fonts.DictBegin();
DictIterator end = fonts.DictEnd();
for (; itr!=end; itr.Next())
{
Obj fnt_dict = itr.Value();
pdftron.PDF.Font font = new pdftron.PDF.Font(fnt_dict);
if (font.IsEmbedded()) continue;
}
}
}
sourceDoc.Save( destFile, Doc.SaveOptions.e_linearized);
sourceDoc.Close();
-----
A: This is possible, but is not as simple as it may seem at first
look.
PDFNet.AddFontSubst function is used to override default font
substitution that occurs during processing of PDF documents with
missing fonts. This can be useful in situations where you would like
to use a specific font instead of the fonts selected by PDFNet.
AddFontSubst neither embeds the given font nor it modifies the PDF.
In order to embed a new font in a new or existing PDF, you could call
(Font.Create??). For example:
pdftron.PDF.Font new_font =
pdftron.PDF.Font.CreateTrueTypeFont(pdfdoc.GetSDFDoc(), "myfont.ttf",
true, false);
To replace the existing font with the new font you could use the
SDFDoc.Swap() method. For example:
pdfdoc.GetSDFDoc().Swap(new_font.GetSDFObj().GetObjNum(),
old_font.GetSDFObj().GetObjNum());
In some cases this will work, but it will fail if the two fonts have
different encodings. In PDF format, text can be represented using many
different types of encodings (including custom encodings). This
information is stored as the 'Encoding' entry in the font dictionary
(for more info, please see Section 5.5.5 'Character Encoding' in PDF
Reference Manual - http://www.pdftron.com/downloads/PDFReference16.pdf).
As a result, simple font swapping will work only if fonts encodings
match; otherwise the modified PDF document may contain junk text. To
resolve this issue you would need to create a new font encoding that
maps old-char codes to WinAnsiEncoding/Unicode and update the Encoding
entry in the font dictionary (e.g.
new_font.GetSDFObj().PutName("Encoding", "MacRomanEncoding") or
new_font.GetSDFObj().Put ("Encoding", encoding_dict)). Font method
'MapToUnicode' can be useful when creating the new Encoding
dictionary. The pseudocode may look along the following lines:
pdftron.SDF.Obj RemapFontEncoding(pdftron.PDF.Font old_font)
{
Obj encoding_dict = pdfdoc.CreateIndirectDict();
Obj diffs = encoding_dict.PutArray("Differences");
for (int i=0; i<256; ++i) {
Unicode uni = old_font.MapToUnicode(i);
String glyph_name = GetGlyphname(uni)
diffs.PushBackNumber(uni);
diffs.PushBackName(glyph_name);
}
}
String GetGlyphname(int uni) {
// given a Unicode code-point search the AGL (Adobe Glyph List)
// http://www.adobe.com/devnet/opentype/archives/glyph.html
// and return the corresponding glyph name.
}
...
pdftron.PDF.Font new_font =
pdftron.PDF.Font.CreateTrueTypeFont(pdfdoc.GetSDFDoc(), "myfont.ttf",
true, false); Obj new_font_obj = new_font.GetSDFObj(); // Modify font
Encoding new_font_obj.Put("Encoding", RemapFontEncoding(old_font)); //
Copy widths array...
Obj widths = old_font.GetSDFObj().FindObj("Widths");
if (widths != null) {
new_font_obj.Put("Widths", widths);
}
// Swap the two fonts
pdfdoc.GetSDFDoc().Swap(new_font.GetSDFObj().GetObjNum(),
old_font.GetSDFObj().GetObjNum());
pdfdoc.Save(...)