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
9 changes: 8 additions & 1 deletion src/UglyToad.PdfPig/Content/ResourceStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,12 @@ private void LoadFontDictionary(DictionaryToken fontDictionary)

try
{
loadedFonts[reference] = fontFactory.Get(fontObject);
var loadedFont = fontFactory.Get(fontObject);
// Stamp the font dictionary's indirect reference so consumers can
// distinguish same-named fonts (e.g. two subsets of one typeface
// embedded without unique subset prefixes). See FontDetails.FontDictionaryReference.
loadedFont.Details?.SetFontDictionaryReference(reference);
loadedFonts[reference] = loadedFont;
}
catch
{
Expand Down Expand Up @@ -286,6 +291,8 @@ public IFont GetFontDirectly(IndirectReferenceToken fontReferenceToken)

var font = fontFactory.Get(fontDictionaryToken);

font.Details?.SetFontDictionaryReference(fontReferenceToken.Data);

return font;
}

Expand Down
43 changes: 42 additions & 1 deletion src/UglyToad.PdfPig/PdfFonts/FontDetails.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
namespace UglyToad.PdfPig.PdfFonts
{
using Core;

/// <summary>
/// Summary details of the font used to draw a glyph.
/// </summary>
Expand Down Expand Up @@ -35,6 +37,16 @@ public sealed class FontDetails
/// </summary>
public bool IsItalic { get; }

/// <summary>
/// The indirect reference of the font dictionary this font was loaded from,
/// when it was loaded from an indirect object. Uniquely identifies the font
/// within its source document even when multiple font objects share the same
/// name (e.g. two different subsets of one typeface embedded without unique
/// subset prefixes). <c>null</c> for fonts defined directly inside a resource
/// dictionary (no indirect object) or for synthetic/fallback fonts.
/// </summary>
public IndirectReference? FontDictionaryReference { get; private set; }

private readonly Lazy<FontDetails> _bold;

/// <summary>
Expand All @@ -47,7 +59,14 @@ public FontDetails(string? name, bool isBold, int weight, bool isItalic)
Weight = weight;
IsItalic = isItalic;

_bold = isBold ? new Lazy<FontDetails>(() => this) : new Lazy<FontDetails>(() => new FontDetails(Name, true, Weight, IsItalic));
// Evaluated lazily so a FontDictionaryReference assigned after construction
// still propagates to the bold variant.
_bold = isBold
? new Lazy<FontDetails>(() => this)
: new Lazy<FontDetails>(() => new FontDetails(Name, true, Weight, IsItalic)
{
FontDictionaryReference = FontDictionaryReference
});
}

/// <summary>
Expand All @@ -66,8 +85,30 @@ public FontDetails AsBold()

internal FontDetails WithName(string? name) => name is not null
? new FontDetails(name, IsBold, Weight, IsItalic)
{
FontDictionaryReference = FontDictionaryReference
}
: this;

/// <summary>
/// Record the indirect reference of the font dictionary this font was loaded from.
/// Called by the resource system immediately after font construction; assigning the
/// same value again is a no-op.
/// </summary>
internal void SetFontDictionaryReference(IndirectReference reference)
{
FontDictionaryReference = reference;

// Propagate to an already-materialized bold variant. The bold Lazy caches its
// value, so a variant created before this stamp would otherwise keep a stale
// null reference forever. (A not-yet-materialized variant picks the reference
// up at evaluation time via the initializer closure.)
if (_bold.IsValueCreated && !ReferenceEquals(_bold.Value, this))
{
_bold.Value.SetFontDictionaryReference(reference);
}
}

/// <inheritdoc />
public override string ToString()
{
Expand Down
Loading