/** * Creates a new TrueType font for embedding. */ public TrueTypeEmbedder(Document document, PdfDictionary dict, TrueTypeFont ttf, bool embedSubset) { this.document = document; this.embedSubset = embedSubset; this.ttf = ttf; fontDescriptor = CreateFontDescriptor(ttf); if (!IsEmbeddingPermitted(ttf)) { throw new IOException("This font does not permit embedding"); } if (!embedSubset) { // full embedding PdfStream stream = new PdfStream(ttf.OriginalData); stream.Header[PdfName.Length] = stream.Header[PdfName.Length1] = PdfInteger.Get(ttf.OriginalDataSize); fontDescriptor.FontFile2 = new FontFile(document, stream); } dict[PdfName.Type] = PdfName.Font; dict[PdfName.BaseFont] = new PdfName(ttf.Name); // choose a Unicode "cmap" cmapLookup = ttf.GetUnicodeCmapLookup(); }
/** * Constructor. * * @param fontDictionary The font dictionary according to the PDF specification. * @param parent The parent font. * @param trueTypeFont The true type font used to create the parent font * @throws IOException */ public CIDFontType2(PdfDirectObject fontDictionary, PdfType0Font parent, TrueTypeFont trueTypeFont) : base(fontDictionary, parent) { FontDescriptor fd = FontDescriptor; if (trueTypeFont != null) { ttf = trueTypeFont; isEmbedded = true; isDamaged = false; } else { bool fontIsDamaged = false; TrueTypeFont ttfFont = null; FontFile stream = null; if (fd != null) { // Acrobat looks in FontFile too, even though it is not in the spec, see PDFBOX-2599 stream = fd.FontFile2 ?? fd.FontFile3 ?? fd.FontFile; } if (stream != null) { try { // embedded OTF or TTF OTFParser otfParser = new OTFParser(true); ttfFont = otfParser.Parse((Bytes.Buffer)stream.BaseDataObject.ExtractBody(true), fd.FontName); if (ttfFont is OpenTypeFont otf && otf.IsPostScript) { // PDFBOX-3344 contains PostScript outlines instead of TrueType fontIsDamaged = true; Debug.WriteLine($"warning: Found CFF/OTF but expected embedded TTF font {fd.FontName}"); } } catch (IOException e) { fontIsDamaged = true; Debug.WriteLine($"warning: Could not read embedded OTF for font {BaseFont} {e}"); } } isEmbedded = ttfFont != null; isDamaged = fontIsDamaged; if (ttfFont == null) { ttfFont = FindFontOrSubstitute(); } ttf = ttfFont; } cmapLookup = ttf.GetUnicodeCmapLookup(false); cid2gid = ReadCIDToGIDMap(); }
/** * This will load a PFB to be embedded into a document. * * @param doc The PDF document that will hold the embedded font. * @param dict The Font dictionary to write to. * @param pfbStream The pfb input. * @throws IOException If there is an error loading the data. */ public PdfType1FontEmbedder(Document doc, PdfDictionary dict, Bytes.IInputStream pfbStream, Encoding encoding) { dict[PdfName.Subtype] = PdfName.Type1; // read the pfb byte[] pfbBytes = pfbStream.ToByteArray(); PfbParser pfbParser = new PfbParser(pfbBytes); type1 = Type1Font.CreateWithPFB(pfbBytes); if (encoding == null) { fontEncoding = Type1Encoding.FromFontBox(type1.Encoding); } else { fontEncoding = encoding; } // build font descriptor FontDescriptor fd = BuildFontDescriptor(type1); PdfStream fontStream = new PdfStream(pfbParser.GetInputStream()); fontStream.Header[PdfName.Length] = PdfInteger.Get(pfbParser.Size); for (int i = 0; i < pfbParser.Lengths.Length; i++) { fontStream.Header[new PdfName("Length" + (i + 1))] = PdfInteger.Get(pfbParser.Lengths[i]); } fd.FontFile = new FontFile(doc, fontStream); // set the values dict[PdfName.FontDescriptor] = fd.BaseObject; dict[PdfName.BaseFont] = PdfName.Get(type1.Name); // widths List <int> widths = new List <int>(256); for (int code = 0; code <= 255; code++) { string name = fontEncoding.GetName(code); int width = (int)Math.Round(type1.GetWidth(name)); widths.Add(width); } dict[PdfName.FirstChar] = PdfInteger.Get(0); dict[PdfName.LastChar] = PdfInteger.Get(255); dict[PdfName.Widths] = new PdfArray(widths.Select(p => PdfInteger.Get(p))); dict[PdfName.Encoding] = encoding.GetPdfObject(); }
/** * Returns a FontDescriptor for the given PFB. */ public static FontDescriptor BuildFontDescriptor(Type1Font type1) { bool isSymbolic = type1.Encoding is BuiltInEncoding; FontDescriptor fd = new FontDescriptor(); fd.FontName = type1.Name; fd.FontFamily = type1.FamilyName; fd.NonSymbolic = !isSymbolic; fd.Symbolic = isSymbolic; fd.FontBBox = new Rectangle(type1.FontBBox); fd.ItalicAngle = type1.ItalicAngle; fd.Ascent = type1.FontBBox.Top; fd.Descent = type1.FontBBox.Bottom; fd.CapHeight = type1.BlueValues[2]; fd.StemV = 0; // for PDF/A return(fd); }
public void BuildFontFile2(Bytes.Buffer ttfStream) { PdfStream stream = new PdfStream(ttfStream); // as the stream was closed within the PdfStream constructor, we have to recreate it using (var input = (Bytes.Buffer)stream.ExtractBody(true)) { ttf = new TTFParser().ParseEmbedded(input); if (!IsEmbeddingPermitted(ttf)) { throw new IOException("This font does not permit embedding"); } if (fontDescriptor == null) { fontDescriptor = CreateFontDescriptor(ttf); } } stream.Header[PdfName.Length1] = PdfInteger.Get(ttf.OriginalDataSize); fontDescriptor.FontFile2 = new FontFile(document, stream); }
/** * Returns a FontDescriptor for the given AFM. Used only for Standard 14 fonts. * * @param metrics AFM */ public static FontDescriptor BuildFontDescriptor(FontMetrics metrics) { bool isSymbolic = metrics.EncodingScheme.Equals("FontSpecific", StringComparison.Ordinal); FontDescriptor fd = new FontDescriptor(); fd.FontName = metrics.FontName; fd.FontFamily = metrics.FamilyName; fd.NonSymbolic = !isSymbolic; fd.Symbolic = isSymbolic; fd.FontBBox = new Rectangle(metrics.FontBBox); fd.ItalicAngle = metrics.ItalicAngle; fd.Ascent = metrics.Ascender; fd.Descent = metrics.Descender; fd.CapHeight = metrics.CapHeight; fd.XHeight = metrics.XHeight; fd.AvgWidth = metrics.GetAverageCharacterWidth(); fd.CharSet = metrics.CharacterSet; fd.StemV = 0; // for PDF/A return(fd); }
/** * Constructor. * * @param fontDictionary The font dictionary according to the PDF specification. * @param parent The parent font. */ public CIDFontType0(PdfDirectObject fontDictionary, PdfType0Font parent) : base(fontDictionary, parent) { FontDescriptor fd = FontDescriptor; FontFile fontFile = null; byte[] bytes = null; if (fd != null) { fontFile = fd.FontFile3 ?? fd.FontFile; if (fontFile != null) { bytes = fontFile.BaseDataObject.ExtractBody(true).GetBuffer(); } } bool fontIsDamaged = false; CFFFont cffFont = null; if (bytes != null && bytes.Length > 0 && (bytes[0] & 0xff) == '%') { // PDFBOX-2642 contains a corrupt PFB font instead of a CFF Debug.WriteLine("warn: Found PFB but expected embedded CFF font " + fd.FontName); try { t1Font = PdfType1Font.LoadType1Font(fontFile); } catch (DamagedFontException e) { Debug.WriteLine($"warn: Can't read damaged embedded Type1 font {fd.FontName} {e}"); fontIsDamaged = true; } catch (IOException e) { Debug.WriteLine($"error: Can't read the embedded Type1 font {fd.FontName} {e}"); fontIsDamaged = true; } } else if (bytes != null) { CFFParser cffParser = new CFFParser(); try { cffFont = cffParser.Parse(bytes, new FF3ByteSource(fd, bytes))[0]; } catch (IOException e) { Debug.WriteLine("error: Can't read the embedded CFF font " + fd.FontName, e); fontIsDamaged = true; } } if (cffFont != null) { // embedded if (cffFont is CFFCIDFont) { cidFont = (CFFCIDFont)cffFont; t1Font = null; } else { cidFont = null; t1Font = cffFont; } cid2gid = ReadCIDToGIDMap(); isEmbedded = true; isDamaged = false; } else if (t1Font != null) { if (t1Font is Type1Font type1Font) { cidFont = null; } cid2gid = ReadCIDToGIDMap(); isEmbedded = true; isDamaged = false; } else { // find font or substitute CIDFontMapping mapping = FontMappers.Instance.GetCIDFont(BaseFont, FontDescriptor, CIDSystemInfo); BaseFont font; if (mapping.IsCIDFont) { cffFont = mapping.Font.CFF.Font; if (cffFont is CFFCIDFont) { cidFont = (CFFCIDFont)cffFont; t1Font = null; font = cidFont; } else { // PDFBOX-3515: OpenType fonts are loaded as CFFType1Font CFFType1Font f = (CFFType1Font)cffFont; cidFont = null; t1Font = f; font = f; } } else { cidFont = null; t1Font = mapping.TrueTypeFont; font = t1Font; } if (mapping.IsFallback) { Debug.WriteLine($"warning: Using fallback {font.Name} for CID-keyed font {BaseFont}"); } isEmbedded = false; isDamaged = fontIsDamaged; } fontMatrixTransform = FontMatrix; fontMatrixTransform = fontMatrixTransform.PostConcat(SKMatrix.CreateScale(1000, 1000)); }
/** * Creates a new font descriptor dictionary for the given TTF. */ private FontDescriptor CreateFontDescriptor(TrueTypeFont ttf) { FontDescriptor fd = new FontDescriptor(document, new PdfDictionary()); fd.FontName = ttf.Name; OS2WindowsMetricsTable os2 = ttf.OS2Windows; PostScriptTable post = ttf.PostScript; // Flags var flags = (FlagsEnum)0; flags |= (post.IsFixedPitch > 0 || ttf.HorizontalHeader.NumberOfHMetrics == 1) ? FlagsEnum.FixedPitch : 0; int fsSelection = os2.FsSelection; flags |= ((fsSelection & (ITALIC | OBLIQUE)) != 0) ? FlagsEnum.Italic : 0; switch (os2.FamilyClass) { case OS2WindowsMetricsTable.FAMILY_CLASS_CLAREDON_SERIFS: case OS2WindowsMetricsTable.FAMILY_CLASS_FREEFORM_SERIFS: case OS2WindowsMetricsTable.FAMILY_CLASS_MODERN_SERIFS: case OS2WindowsMetricsTable.FAMILY_CLASS_OLDSTYLE_SERIFS: case OS2WindowsMetricsTable.FAMILY_CLASS_SLAB_SERIFS: flags |= FlagsEnum.Serif; break; case OS2WindowsMetricsTable.FAMILY_CLASS_SCRIPTS: flags |= FlagsEnum.Script; break; default: break; } fd.FontWeight = os2.WeightClass; flags |= FlagsEnum.Symbolic; flags &= ~FlagsEnum.Nonsymbolic; fd.Flags = flags; // ItalicAngle fd.ItalicAngle = post.ItalicAngle; // FontBBox HeaderTable header = ttf.Header; float scaling = 1000f / header.UnitsPerEm; var skRect = new SKRect( header.XMin * scaling, header.YMin * scaling, header.XMax * scaling, header.YMax * scaling ); Rectangle rect = new Rectangle(skRect); fd.FontBBox = rect; // Ascent, Descent HorizontalHeaderTable hHeader = ttf.HorizontalHeader; fd.Ascent = hHeader.Ascender * scaling; fd.Descent = hHeader.Descender * scaling; // CapHeight, XHeight if (os2.Version >= 1.2) { fd.CapHeight = os2.CapHeight * scaling; fd.XHeight = os2.Height * scaling; } else { var capHPath = ttf.GetPath("H"); if (capHPath != null) { fd.CapHeight = (float)Math.Round(capHPath.Bounds.Bottom * scaling); } else { // estimate by summing the typographical +ve ascender and -ve descender fd.CapHeight = os2.TypoAscender + (os2.TypoDescender * scaling); } var xPath = ttf.GetPath("x"); if (xPath != null) { fd.XHeight = (float)Math.Round(xPath.Bounds.Bottom * scaling); } else { // estimate by halving the typographical ascender fd.XHeight = os2.TypoAscender / (2.0f * scaling); } } // StemV - there's no true TTF equivalent of this, so we estimate it fd.StemV = skRect.Width * .13f; return(fd); }