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.