Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/UglyToad.PdfPig.Tests/PublicApiScannerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
28 changes: 28 additions & 0 deletions src/UglyToad.PdfPig/PdfFonts/IType3Font.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
namespace UglyToad.PdfPig.PdfFonts
{
using System.Diagnostics.CodeAnalysis;
using Tokens;

/// <summary>
/// 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).
/// </summary>
public interface IType3Font : IFont
{
/// <summary>
/// The Type 3 font's <c>/Resources</c> dictionary, used by all of its CharProc content streams.
/// May be <c>null</c> if the font does not declare resources.
/// </summary>
DictionaryToken? Type3Resources { get; }

/// <summary>
/// Resolve a character code to its CharProc content stream via the font's encoding.
/// </summary>
/// <param name="characterCode">The PDF character code (not a Unicode code point).</param>
/// <param name="charProcStream">The CharProc stream when found, otherwise <c>null</c>.</param>
/// <returns><c>true</c> if a CharProc was resolved for the given character code.</returns>
bool TryGetCharProc(int characterCode, [NotNullWhen(true)] out StreamToken? charProcStream);
}
}
24 changes: 23 additions & 1 deletion src/UglyToad.PdfPig/PdfFonts/Parser/Handlers/Type3FontHandler.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
namespace UglyToad.PdfPig.PdfFonts.Parser.Handlers
{
using System.Collections.Generic;
using Cmap;
using Core;
using Fonts;
Expand Down Expand Up @@ -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<string, StreamToken>? ReadCharProcs(DictionaryToken dictionary)
{
if (!dictionary.TryGet(NameToken.CharProcs, scanner, out DictionaryToken? charProcsDictionary))
{
return null;
}

var result = new Dictionary<string, StreamToken>(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)
Expand Down
38 changes: 36 additions & 2 deletions src/UglyToad.PdfPig/PdfFonts/Simple/Type3Font.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -20,6 +20,7 @@ internal class Type3Font : IFont
private readonly int lastChar;
private readonly double[] widths;
private readonly ToUnicodeCMap toUnicodeCMap;
private readonly IReadOnlyDictionary<string, StreamToken>? charProcs;

/// <summary>
/// Type 3 fonts are usually unnamed.
Expand All @@ -30,9 +31,13 @@ internal class Type3Font : IFont

public FontDetails Details { get; }

/// <inheritdoc/>
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<string, StreamToken>? charProcs,
DictionaryToken? resources)
{
Name = name;

Expand All @@ -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
Expand Down Expand Up @@ -140,6 +147,33 @@ public bool TryGetPath(int characterCode, [NotNullWhen(true)] out IReadOnlyList<
return false;
}

/// <inheritdoc/>
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);
}

/// <summary>
/// <inheritdoc/>
/// <para>Type 3 fonts do not use vector paths. Always returns <c>false</c>.</para>
Expand Down
Loading