diff --git a/src/UglyToad.PdfPig/Fonts/Parser/Handlers/TrueTypeFontHandler.cs b/src/UglyToad.PdfPig/Fonts/Parser/Handlers/TrueTypeFontHandler.cs index 91987f18..49b1085d 100644 --- a/src/UglyToad.PdfPig/Fonts/Parser/Handlers/TrueTypeFontHandler.cs +++ b/src/UglyToad.PdfPig/Fonts/Parser/Handlers/TrueTypeFontHandler.cs @@ -2,6 +2,7 @@ { using System; using System.Collections.Generic; + using System.Linq; using SystemFonts; using Cmap; using Encodings; @@ -48,7 +49,9 @@ public IFont Generate(DictionaryToken dictionary, bool isLenientParsing) { - if (!dictionary.TryGetOptionalTokenDirect(NameToken.FirstChar, pdfScanner, out NumericToken firstCharacterToken)) + if (!dictionary.TryGetOptionalTokenDirect(NameToken.FirstChar, pdfScanner, out NumericToken firstCharacterToken) + || !dictionary.TryGet(NameToken.FontDescriptor, pdfScanner, out _) + || !dictionary.TryGet(NameToken.Widths, out IToken _)) { if (!dictionary.TryGetOptionalTokenDirect(NameToken.BaseFont, pdfScanner, out NameToken baseFont)) { @@ -72,7 +75,22 @@ thisEncoding = new AdobeFontMetricsEncoding(standard14Font); } - return new TrueTypeStandard14FallbackSimpleFont(baseFont, standard14Font, thisEncoding, fileSystemFont); + int? firstChar = null; + decimal[] widthsOverride = null; + + if (dictionary.TryGet(NameToken.FirstChar, pdfScanner, out firstCharacterToken)) + { + firstChar = firstCharacterToken.Int; + } + + if (dictionary.TryGet(NameToken.Widths, pdfScanner, out ArrayToken widthsArray)) + { + widthsOverride = widthsArray.Data.OfType() + .Select(x => x.Data).ToArray(); + } + + return new TrueTypeStandard14FallbackSimpleFont(baseFont, standard14Font, thisEncoding, fileSystemFont, + new TrueTypeStandard14FallbackSimpleFont.MetricOverrides(firstChar, widthsOverride)); } var firstCharacter = firstCharacterToken.Int; diff --git a/src/UglyToad.PdfPig/Fonts/Simple/TrueTypeStandard14FallbackSimpleFont.cs b/src/UglyToad.PdfPig/Fonts/Simple/TrueTypeStandard14FallbackSimpleFont.cs index 5442bb3a..343917d9 100644 --- a/src/UglyToad.PdfPig/Fonts/Simple/TrueTypeStandard14FallbackSimpleFont.cs +++ b/src/UglyToad.PdfPig/Fonts/Simple/TrueTypeStandard14FallbackSimpleFont.cs @@ -1,6 +1,7 @@ namespace UglyToad.PdfPig.Fonts.Simple { using System; + using System.Collections.Generic; using Core; using Encodings; using IO; @@ -18,16 +19,19 @@ private readonly FontMetrics fontMetrics; private readonly Encoding encoding; private readonly TrueTypeFontProgram font; + private readonly MetricOverrides overrides; public NameToken Name { get; } public bool IsVertical { get; } = false; - public TrueTypeStandard14FallbackSimpleFont(NameToken name, FontMetrics fontMetrics, Encoding encoding, TrueTypeFontProgram font) + public TrueTypeStandard14FallbackSimpleFont(NameToken name, FontMetrics fontMetrics, Encoding encoding, TrueTypeFontProgram font, + MetricOverrides overrides) { this.fontMetrics = fontMetrics; this.encoding = encoding ?? throw new ArgumentNullException(nameof(encoding)); this.font = font; + this.overrides = overrides; Name = name; } @@ -61,18 +65,39 @@ public CharacterBoundingBox GetBoundingBox(int characterCode) { + var width = 0m; + var fontMatrix = GetFontMatrix(); + if (font != null && font.TryGetBoundingBox(characterCode, out var bounds)) { bounds = fontMatrix.Transform(bounds); - return new CharacterBoundingBox(bounds, bounds.Width); + + if (overrides?.TryGetWidth(characterCode, out width) != true) + { + width = bounds.Width; + } + else + { + width = DefaultTransformation.TransformX(width); + } + + return new CharacterBoundingBox(bounds, width); } var name = encoding.GetName(characterCode); var metrics = fontMetrics.CharacterMetrics[name]; + if (overrides?.TryGetWidth(characterCode, out width) != true) + { + width = fontMatrix.TransformX(metrics.WidthX); + } + else + { + width = DefaultTransformation.TransformX(width); + } + bounds = fontMatrix.Transform(metrics.BoundingBox); - var width = fontMatrix.TransformX(metrics.WidthX); return new CharacterBoundingBox(bounds, width); } @@ -88,5 +113,43 @@ return DefaultTransformation; } + + public class MetricOverrides + { + public int? FirstCharacterCode { get; } + + public IReadOnlyList Widths { get; } + + public bool HasOverriddenMetrics { get; } + + public MetricOverrides(int? firstCharacterCode, IReadOnlyList widths) + { + FirstCharacterCode = firstCharacterCode; + Widths = widths; + HasOverriddenMetrics = FirstCharacterCode.HasValue && Widths != null + && Widths.Count > 0; + } + + public bool TryGetWidth(int characterCode, out decimal width) + { + width = 0; + + if (!HasOverriddenMetrics || !FirstCharacterCode.HasValue) + { + return false; + } + + var index = characterCode - FirstCharacterCode.Value; + + if (index < 0 || index >= Widths.Count) + { + return false; + } + + width = Widths[index]; + + return true; + } + } } } \ No newline at end of file