diff --git a/src/UglyToad.PdfPig.Tests/PublicApiScannerTests.cs b/src/UglyToad.PdfPig.Tests/PublicApiScannerTests.cs index af2449ee2..a2d9d397e 100644 --- a/src/UglyToad.PdfPig.Tests/PublicApiScannerTests.cs +++ b/src/UglyToad.PdfPig.Tests/PublicApiScannerTests.cs @@ -123,6 +123,7 @@ public void OnlyExposedApiIsPublic() "UglyToad.PdfPig.PdfFonts.FontDetails", "UglyToad.PdfPig.PdfFonts.FontStretch", "UglyToad.PdfPig.PdfFonts.IFont", + "UglyToad.PdfPig.PdfFonts.IType3Font", "UglyToad.PdfPig.Geometry.GeometryExtensions", "UglyToad.PdfPig.Geometry.UserSpaceUnit", "UglyToad.PdfPig.Graphics.BaseStreamProcessor`1", diff --git a/src/UglyToad.PdfPig/PdfFonts/IType3Font.cs b/src/UglyToad.PdfPig/PdfFonts/IType3Font.cs new file mode 100644 index 000000000..89b8d0db8 --- /dev/null +++ b/src/UglyToad.PdfPig/PdfFonts/IType3Font.cs @@ -0,0 +1,28 @@ +namespace UglyToad.PdfPig.PdfFonts +{ + using System.Diagnostics.CodeAnalysis; + using Tokens; + + /// + /// A Type 3 font, in which glyphs are defined as ordinary PDF content streams ("CharProcs") rather + /// than vector outlines. Rendering a Type 3 glyph requires processing the corresponding CharProc + /// content stream against the active graphics state, after replacing the current transformation + /// matrix with the text rendering matrix and concatenating the font matrix (PDF 1.7 §9.6.4). + /// + public interface IType3Font : IFont + { + /// + /// The Type 3 font's /Resources dictionary, used by all of its CharProc content streams. + /// May be null if the font does not declare resources. + /// + DictionaryToken? Type3Resources { get; } + + /// + /// Resolve a character code to its CharProc content stream via the font's encoding. + /// + /// The PDF character code (not a Unicode code point). + /// The CharProc stream when found, otherwise null. + /// true if a CharProc was resolved for the given character code. + bool TryGetCharProc(int characterCode, [NotNullWhen(true)] out StreamToken? charProcStream); + } +} diff --git a/src/UglyToad.PdfPig/PdfFonts/Parser/Handlers/Type3FontHandler.cs b/src/UglyToad.PdfPig/PdfFonts/Parser/Handlers/Type3FontHandler.cs index c62e168b7..533c29def 100644 --- a/src/UglyToad.PdfPig/PdfFonts/Parser/Handlers/Type3FontHandler.cs +++ b/src/UglyToad.PdfPig/PdfFonts/Parser/Handlers/Type3FontHandler.cs @@ -1,5 +1,6 @@ namespace UglyToad.PdfPig.PdfFonts.Parser.Handlers { + using System.Collections.Generic; using Cmap; using Core; using Fonts; @@ -52,9 +53,30 @@ public IFont Generate(DictionaryToken dictionary) var name = GetFontName(dictionary); + var charProcs = ReadCharProcs(dictionary); + dictionary.TryGet(NameToken.Resources, scanner, out DictionaryToken? resources); + return new Type3Font(name, boundingBox, fontMatrix, encoding!, firstCharacter, - lastCharacter, widths, toUnicodeCMap!); + lastCharacter, widths, toUnicodeCMap!, charProcs, resources); + } + + private IReadOnlyDictionary? ReadCharProcs(DictionaryToken dictionary) + { + if (!dictionary.TryGet(NameToken.CharProcs, scanner, out DictionaryToken? charProcsDictionary)) + { + return null; + } + + var result = new Dictionary(charProcsDictionary.Data.Count); + foreach (var entry in charProcsDictionary.Data) + { + if (DirectObjectFinder.TryGet(entry.Value, scanner, out StreamToken? charProcStream)) + { + result[entry.Key] = charProcStream; + } + } + return result; } private NameToken GetFontName(DictionaryToken dictionary) diff --git a/src/UglyToad.PdfPig/PdfFonts/Simple/Type3Font.cs b/src/UglyToad.PdfPig/PdfFonts/Simple/Type3Font.cs index f9629a6f1..afb401576 100644 --- a/src/UglyToad.PdfPig/PdfFonts/Simple/Type3Font.cs +++ b/src/UglyToad.PdfPig/PdfFonts/Simple/Type3Font.cs @@ -9,7 +9,7 @@ using Fonts.Encodings; using Tokens; - internal class Type3Font : IFont + internal class Type3Font : IType3Font { private readonly PdfRectangle boundingBox; private readonly TransformationMatrix fontMatrix; @@ -20,6 +20,7 @@ internal class Type3Font : IFont private readonly int lastChar; private readonly double[] widths; private readonly ToUnicodeCMap toUnicodeCMap; + private readonly IReadOnlyDictionary? charProcs; /// /// Type 3 fonts are usually unnamed. @@ -30,9 +31,13 @@ internal class Type3Font : IFont public FontDetails Details { get; } + /// + public DictionaryToken? Type3Resources { get; } + public Type3Font(NameToken name, PdfRectangle boundingBox, TransformationMatrix fontMatrix, Encoding encoding, int firstChar, int lastChar, double[] widths, - CMap toUnicodeCMap) + CMap toUnicodeCMap, IReadOnlyDictionary? charProcs, + DictionaryToken? resources) { Name = name; @@ -43,6 +48,8 @@ public Type3Font(NameToken name, PdfRectangle boundingBox, TransformationMatrix this.lastChar = lastChar; this.widths = widths; this.toUnicodeCMap = new ToUnicodeCMap(toUnicodeCMap); + this.charProcs = charProcs; + Type3Resources = resources; Details = FontDetails.GetDefault(name?.Data); // Assumption is ZapfDingbats is not possible here. We need to change the behaviour if not the case @@ -140,6 +147,33 @@ public bool TryGetPath(int characterCode, [NotNullWhen(true)] out IReadOnlyList< return false; } + /// + public bool TryGetCharProc(int characterCode, [NotNullWhen(true)] out StreamToken? charProcStream) + { + charProcStream = null; + if (charProcs is null || encoding is null) + { + return false; + } + + string name; + try + { + name = encoding.GetName(characterCode); + } + catch + { + return false; + } + + if (string.IsNullOrEmpty(name)) + { + return false; + } + + return charProcs.TryGetValue(name, out charProcStream); + } + /// /// /// Type 3 fonts do not use vector paths. Always returns false.