diff --git a/src/UglyToad.PdfPig.Fonts/CompactFontFormat/CharStrings/Type2CharStrings.cs b/src/UglyToad.PdfPig.Fonts/CompactFontFormat/CharStrings/Type2CharStrings.cs index 0dec8eaed..1d4058a54 100644 --- a/src/UglyToad.PdfPig.Fonts/CompactFontFormat/CharStrings/Type2CharStrings.cs +++ b/src/UglyToad.PdfPig.Fonts/CompactFontFormat/CharStrings/Type2CharStrings.cs @@ -11,7 +11,7 @@ /// as the local (per font) and global (per font set) subroutines. /// The CharStrings are lazily evaluated. /// - internal class Type2CharStrings + internal sealed class Type2CharStrings { private readonly object locker = new object(); private readonly Dictionary glyphs = new Dictionary(); @@ -45,7 +45,7 @@ public Type2Glyph Generate(string name, double defaultWidthX, double nominalWidt if (!CharStrings.TryGetValue(name, out var sequence)) { - if (!CharStrings.TryGetValue(".notdef", out sequence)) + if (!CharStrings.TryGetValue(GlyphList.NotDefined, out sequence)) { throw new InvalidOperationException($"No charstring sequence with the name /{name} in this font."); } @@ -149,7 +149,7 @@ private static void SetWidthFromArgumentsIfPresent(Type2BuildCharContext context } } - public class CommandSequence + public sealed class CommandSequence { public IReadOnlyList Values { get; } public IReadOnlyList CommandIdentifiers { get; } @@ -157,7 +157,6 @@ public class CommandSequence /// /// The ordered list of numbers and commands for a Type 2 charstring or subroutine. /// - public CommandSequence(IReadOnlyList values, IReadOnlyList commandIdentifiers) { Values = values; @@ -218,7 +217,7 @@ public CommandIdentifier(int commandIndex, bool isMultiByteCommand, byte command /// Since Type 2 CharStrings may define their width as the first argument (as a delta from the font's nominal width X) /// we can retrieve both details for the Type 2 glyph. /// - internal class Type2Glyph + internal sealed class Type2Glyph { /// /// The path of the glyph. diff --git a/src/UglyToad.PdfPig.Fonts/CompactFontFormat/Charsets/CompactFontFormatCharset.cs b/src/UglyToad.PdfPig.Fonts/CompactFontFormat/Charsets/CompactFontFormatCharset.cs index c69a927af..febc21a36 100644 --- a/src/UglyToad.PdfPig.Fonts/CompactFontFormat/Charsets/CompactFontFormatCharset.cs +++ b/src/UglyToad.PdfPig.Fonts/CompactFontFormat/Charsets/CompactFontFormatCharset.cs @@ -37,7 +37,7 @@ public virtual string GetNameByGlyphId(int glyphId) public virtual string GetNameByStringId(int stringId) { - return GlyphIdToStringIdAndName.Single(x => x.Value.stringId == stringId).Value.name; + return GlyphIdToStringIdAndName.SingleOrDefault(x => x.Value.stringId == stringId).Value.name; } public virtual int GetStringIdByGlyphId(int glyphId) diff --git a/src/UglyToad.PdfPig.Fonts/CompactFontFormat/CompactFontFormatBaseEncoding.cs b/src/UglyToad.PdfPig.Fonts/CompactFontFormat/CompactFontFormatBaseEncoding.cs index 8764541dc..ac9ec1dad 100644 --- a/src/UglyToad.PdfPig.Fonts/CompactFontFormat/CompactFontFormatBaseEncoding.cs +++ b/src/UglyToad.PdfPig.Fonts/CompactFontFormat/CompactFontFormatBaseEncoding.cs @@ -16,7 +16,7 @@ public override string GetName(int code) { if (!codeToNameMap.TryGetValue(code, out var name)) { - return ".notdef"; + return NotDefined; } return name; diff --git a/src/UglyToad.PdfPig.Fonts/CompactFontFormat/CompactFontFormatFont.cs b/src/UglyToad.PdfPig.Fonts/CompactFontFormat/CompactFontFormatFont.cs index b2cf357b9..36c30c2e9 100644 --- a/src/UglyToad.PdfPig.Fonts/CompactFontFormat/CompactFontFormatFont.cs +++ b/src/UglyToad.PdfPig.Fonts/CompactFontFormat/CompactFontFormatFont.cs @@ -27,7 +27,7 @@ public class CompactFontFormatFont /// /// The font matrix for this font. /// - public TransformationMatrix FontMatrix => TopDictionary.FontMatrix; + public TransformationMatrix FontMatrix => TopDictionary.FontMatrix.HasValue ? TopDictionary.FontMatrix.Value : TransformationMatrix.FromValues(0.001, 0, 0, 0.001, 0, 0); /// /// The value of Weight from the top dictionary or . @@ -50,6 +50,32 @@ internal CompactFontFormatFont(CompactFontFormatTopLevelDictionary topDictionary Encoding = fontEncoding; } + /// + /// Get the character name. Returns null if cannot be processed. + /// + public string GetCharacterName(int characterCode, bool isCid) + { + if (Encoding != null) + { + return Encoding.GetName(characterCode); + } + + if (Charset.IsCidCharset || isCid) + { + return Charset?.GetNameByStringId(characterCode); + } + + string characterName = GlyphList.AdobeGlyphList.UnicodeCodePointToName(characterCode); + + if (characterName.Equals(GlyphList.NotDefined, StringComparison.OrdinalIgnoreCase)) + { + // BobLD: Not tested + return Charset?.GetNameByStringId(characterCode); + } + + return characterName; + } + /// /// Get the bounding box for the character with the given name. /// @@ -143,9 +169,17 @@ protected virtual decimal GetNominalWidthX(string characterName) { return PrivateDictionary.NominalWidthX; } + + /// + /// Get the Font Matrix for the corresponding character name, if available. Return null if not. + /// + public virtual TransformationMatrix? GetFontMatrix(string characterName) + { + return TopDictionary.FontMatrix; + } } - internal class CompactFontFormatCidFont : CompactFontFormatFont + internal sealed class CompactFontFormatCidFont : CompactFontFormatFont { public IReadOnlyList FontDictionaries { get; } public IReadOnlyList PrivateDictionaries { get; } @@ -183,6 +217,26 @@ protected override decimal GetNominalWidthX(string characterName) return dictionary.NominalWidthX; } + public override TransformationMatrix? GetFontMatrix(string characterName) + { + // BobLd: It seems PdfBox just returns TopDictionary.FontMatrix + // But see https://bugs.ghostscript.com/show_bug.cgi?id=690724 + // and https://github.com/veraPDF/veraPDF-library/issues/1010 + // TODO - We might need to multiply both matrices together + + if (TopDictionary.FontMatrix is not null) + { + return TopDictionary.FontMatrix; + } + + if (!TryGetFontDictionaryForCharacter(characterName, out var dictionary)) + { + return null; + } + + return dictionary.FontMatrix; + } + private bool TryGetPrivateDictionaryForCharacter(string characterName, out CompactFontFormatPrivateDictionary dictionary) { dictionary = null; @@ -199,5 +253,22 @@ private bool TryGetPrivateDictionaryForCharacter(string characterName, out Compa return true; } + + private bool TryGetFontDictionaryForCharacter(string characterName, out CompactFontFormatTopLevelDictionary dictionary) + { + dictionary = null; + + var glyphId = Charset.GetGlyphIdByName(characterName); + + var fd = FdSelect.GetFontDictionaryIndex(glyphId); + if (fd == -1) + { + return false; + } + + dictionary = FontDictionaries[fd]; + + return true; + } } } diff --git a/src/UglyToad.PdfPig.Fonts/CompactFontFormat/CompactFontFormatFontCollection.cs b/src/UglyToad.PdfPig.Fonts/CompactFontFormat/CompactFontFormatFontCollection.cs index 65b773723..5b8327201 100644 --- a/src/UglyToad.PdfPig.Fonts/CompactFontFormat/CompactFontFormatFontCollection.cs +++ b/src/UglyToad.PdfPig.Fonts/CompactFontFormat/CompactFontFormatFontCollection.cs @@ -8,7 +8,7 @@ /// A Compact Font Format (CFF) font program as described in The Compact Font Format specification (Adobe Technical Note #5176). /// A CFF font may contain multiple fonts and achieves compression by sharing details between fonts in the set. /// - public class CompactFontFormatFontCollection + public sealed class CompactFontFormatFontCollection { /// /// The decoded header table for this font. @@ -65,16 +65,11 @@ public TransformationMatrix GetFirstTransformationMatrix() /// /// Get the name for the character with the given character code from the font. /// - public string GetCharacterName(int characterCode) + public string GetCharacterName(int characterCode, bool isCid) { - var font = FirstFont; + var name = FirstFont.GetCharacterName(characterCode, isCid); - if (font.Encoding != null) - { - return font.Encoding.GetName(characterCode); - } - - return ".notdef"; + return name ?? GlyphList.NotDefined; } } } diff --git a/src/UglyToad.PdfPig.Fonts/CompactFontFormat/CompactFontFormatFormat0Encoding.cs b/src/UglyToad.PdfPig.Fonts/CompactFontFormat/CompactFontFormatFormat0Encoding.cs index a8e62a569..f80d4bfc1 100644 --- a/src/UglyToad.PdfPig.Fonts/CompactFontFormat/CompactFontFormatFormat0Encoding.cs +++ b/src/UglyToad.PdfPig.Fonts/CompactFontFormat/CompactFontFormatFormat0Encoding.cs @@ -2,12 +2,12 @@ { using System.Collections.Generic; - internal class CompactFontFormatFormat0Encoding : CompactFontFormatBuiltInEncoding + internal sealed class CompactFontFormatFormat0Encoding : CompactFontFormatBuiltInEncoding { public CompactFontFormatFormat0Encoding(IReadOnlyList<(int code, int sid, string str)> values, IReadOnlyList supplements) : base(supplements) { - Add(0, 0, ".notdef"); + Add(0, 0, NotDefined); foreach (var value in values) { diff --git a/src/UglyToad.PdfPig.Fonts/CompactFontFormat/CompactFontFormatFormat1Encoding.cs b/src/UglyToad.PdfPig.Fonts/CompactFontFormat/CompactFontFormatFormat1Encoding.cs index 582a03678..220a498d1 100644 --- a/src/UglyToad.PdfPig.Fonts/CompactFontFormat/CompactFontFormatFormat1Encoding.cs +++ b/src/UglyToad.PdfPig.Fonts/CompactFontFormat/CompactFontFormatFormat1Encoding.cs @@ -2,7 +2,7 @@ { using System.Collections.Generic; - internal class CompactFontFormatFormat1Encoding : CompactFontFormatBuiltInEncoding + internal sealed class CompactFontFormatFormat1Encoding : CompactFontFormatBuiltInEncoding { public int NumberOfRanges { get; set; } @@ -10,7 +10,7 @@ public CompactFontFormatFormat1Encoding(int numberOfRanges, IReadOnlyList<(int c { NumberOfRanges = numberOfRanges; - Add(0, 0, ".notdef"); + Add(0, 0, NotDefined); foreach (var value in values) { diff --git a/src/UglyToad.PdfPig.Fonts/CompactFontFormat/Dictionaries/CompactFontFormatTopLevelDictionary.cs b/src/UglyToad.PdfPig.Fonts/CompactFontFormat/Dictionaries/CompactFontFormatTopLevelDictionary.cs index b25745f83..1f55ebc97 100644 --- a/src/UglyToad.PdfPig.Fonts/CompactFontFormat/Dictionaries/CompactFontFormatTopLevelDictionary.cs +++ b/src/UglyToad.PdfPig.Fonts/CompactFontFormat/Dictionaries/CompactFontFormatTopLevelDictionary.cs @@ -2,7 +2,7 @@ { using Core; - internal class CompactFontFormatTopLevelDictionary + internal sealed class CompactFontFormatTopLevelDictionary { public const int UnsetOffset = -1; @@ -30,7 +30,7 @@ internal class CompactFontFormatTopLevelDictionary public CompactFontFormatCharStringType CharStringType { get; set; } = CompactFontFormatCharStringType.Type2; - public TransformationMatrix FontMatrix { get; set; } = TransformationMatrix.FromValues(0.001, 0, 0, 0.001, 0, 0); + public TransformationMatrix? FontMatrix { get; set; } public decimal StrokeWidth { get; set; } @@ -79,7 +79,7 @@ public override string ToString() } } - internal class CidFontOperators + internal sealed class CidFontOperators { public RegistryOrderingSupplement Ros { get; set; } @@ -100,7 +100,7 @@ internal class CidFontOperators public string FontName { get; set; } } - internal class RegistryOrderingSupplement + internal sealed class RegistryOrderingSupplement { public string Registry { get; set; } diff --git a/src/UglyToad.PdfPig.Fonts/Encodings/Encoding.cs b/src/UglyToad.PdfPig.Fonts/Encodings/Encoding.cs index 155721bcb..e36f4b3f5 100644 --- a/src/UglyToad.PdfPig.Fonts/Encodings/Encoding.cs +++ b/src/UglyToad.PdfPig.Fonts/Encodings/Encoding.cs @@ -8,6 +8,11 @@ /// public abstract class Encoding { + /// + /// .notdef. + /// + protected internal const string NotDefined = ".notdef"; + /// /// Mutable code to name map. /// @@ -24,7 +29,7 @@ public abstract class Encoding protected readonly Dictionary NameToCode = new Dictionary(250); /// - /// Maps from names to character cocdes. + /// Maps from names to character codes. /// public IReadOnlyDictionary NameToCodeMap => NameToCode; @@ -48,7 +53,7 @@ public bool ContainsCode(int code) { return CodeToName.ContainsKey(code); } - + /// /// Get the character name corresponding to the given code. /// @@ -56,17 +61,16 @@ public virtual string GetName(int code) { if (!CodeToName.TryGetValue(code, out var name)) { - return ".notdef"; + return NotDefined; } return name; } - /// - /// Get the character code from name + /// Get the character code from name /// - /// Character name (eg. euro, ampersand, A, space) + /// Character name (e.g. euro, ampersand, A, space) /// -1 if not found otherwise the character code public virtual int GetCode(string name) { @@ -90,7 +94,7 @@ protected void Add(int code, string name) NameToCode[name] = code; } } - + /// /// Get a known encoding instance with the given name. /// diff --git a/src/UglyToad.PdfPig.Fonts/GlyphList.cs b/src/UglyToad.PdfPig.Fonts/GlyphList.cs index af1ba7362..57938d3d2 100644 --- a/src/UglyToad.PdfPig.Fonts/GlyphList.cs +++ b/src/UglyToad.PdfPig.Fonts/GlyphList.cs @@ -9,9 +9,12 @@ /// /// A list which maps PostScript glyph names to unicode values. /// - public class GlyphList + public sealed class GlyphList { - private const string NotDefined = ".notdef"; + /// + /// .notdef. + /// + public const string NotDefined = ".notdef"; private readonly IReadOnlyDictionary nameToUnicode; private readonly IReadOnlyDictionary unicodeToName; diff --git a/src/UglyToad.PdfPig.Fonts/TrueType/TrueTypeFont.cs b/src/UglyToad.PdfPig.Fonts/TrueType/TrueTypeFont.cs index d6635310c..201b424c8 100644 --- a/src/UglyToad.PdfPig.Fonts/TrueType/TrueTypeFont.cs +++ b/src/UglyToad.PdfPig.Fonts/TrueType/TrueTypeFont.cs @@ -137,7 +137,7 @@ public bool TryGetBoundingBox(int characterCode, Func characterCodeTo /// public bool TryGetPath(int characterCode, Func characterCodeToGlyphId, out IReadOnlyList path) { - path = EmptyArray.Instance; + path = null; if (!TryGetGlyphIndex(characterCode, characterCodeToGlyphId, out var index) || TableRegister.GlyphTable == null) diff --git a/src/UglyToad.PdfPig.Fonts/Type1/CharStrings/Type1CharstringDecryptedBytes.cs b/src/UglyToad.PdfPig.Fonts/Type1/CharStrings/Type1CharstringDecryptedBytes.cs index 3827d4cf9..7f6d5148d 100644 --- a/src/UglyToad.PdfPig.Fonts/Type1/CharStrings/Type1CharstringDecryptedBytes.cs +++ b/src/UglyToad.PdfPig.Fonts/Type1/CharStrings/Type1CharstringDecryptedBytes.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using System.Globalization; - internal class Type1CharstringDecryptedBytes + internal sealed class Type1CharstringDecryptedBytes { public IReadOnlyList Bytes { get; } @@ -18,7 +18,7 @@ public Type1CharstringDecryptedBytes(IReadOnlyList bytes, int index) { Bytes = bytes ?? throw new ArgumentNullException(nameof(bytes)); Index = index; - Name = ".notdef"; + Name = GlyphList.NotDefined; Source = SourceType.Subroutine; } diff --git a/src/UglyToad.PdfPig.Tests/Integration/Documents/FontMatrix-1.pdf b/src/UglyToad.PdfPig.Tests/Integration/Documents/FontMatrix-1.pdf new file mode 100644 index 000000000..c506334f6 Binary files /dev/null and b/src/UglyToad.PdfPig.Tests/Integration/Documents/FontMatrix-1.pdf differ diff --git a/src/UglyToad.PdfPig.Tests/Integration/Documents/FontMatrix-concat.pdf b/src/UglyToad.PdfPig.Tests/Integration/Documents/FontMatrix-concat.pdf new file mode 100644 index 000000000..560e5a415 Binary files /dev/null and b/src/UglyToad.PdfPig.Tests/Integration/Documents/FontMatrix-concat.pdf differ diff --git a/src/UglyToad.PdfPig.Tests/Integration/Documents/FontMatrix-otf-bad-hmtx.pdf b/src/UglyToad.PdfPig.Tests/Integration/Documents/FontMatrix-otf-bad-hmtx.pdf new file mode 100644 index 000000000..4f406d3ac Binary files /dev/null and b/src/UglyToad.PdfPig.Tests/Integration/Documents/FontMatrix-otf-bad-hmtx.pdf differ diff --git a/src/UglyToad.PdfPig.Tests/Integration/Documents/FontMatrix-otf.pdf b/src/UglyToad.PdfPig.Tests/Integration/Documents/FontMatrix-otf.pdf new file mode 100644 index 000000000..06e1f7d97 Binary files /dev/null and b/src/UglyToad.PdfPig.Tests/Integration/Documents/FontMatrix-otf.pdf differ diff --git a/src/UglyToad.PdfPig.Tests/Integration/Documents/FontMatrix-raw.pdf b/src/UglyToad.PdfPig.Tests/Integration/Documents/FontMatrix-raw.pdf new file mode 100644 index 000000000..3b9f87796 Binary files /dev/null and b/src/UglyToad.PdfPig.Tests/Integration/Documents/FontMatrix-raw.pdf differ diff --git a/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-696171-0.pdf b/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-696171-0.pdf new file mode 100644 index 000000000..9874d7549 Binary files /dev/null and b/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-696171-0.pdf differ diff --git a/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-696547-0.zip-7.pdf b/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-696547-0.zip-7.pdf new file mode 100644 index 000000000..e7bfd139d Binary files /dev/null and b/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-696547-0.zip-7.pdf differ diff --git a/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-696547-0.zip-9.pdf b/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-696547-0.zip-9.pdf new file mode 100644 index 000000000..f1080421c Binary files /dev/null and b/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-696547-0.zip-9.pdf differ diff --git a/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-697507-0.pdf b/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-697507-0.pdf new file mode 100644 index 000000000..efab5c8c6 Binary files /dev/null and b/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-697507-0.pdf differ diff --git a/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-698168-0.pdf b/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-698168-0.pdf new file mode 100644 index 000000000..235396bed Binary files /dev/null and b/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-698168-0.pdf differ diff --git a/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-698721-0.zip-6.pdf b/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-698721-0.zip-6.pdf new file mode 100644 index 000000000..d5747fc8b Binary files /dev/null and b/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-698721-0.zip-6.pdf differ diff --git a/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-698721-1.pdf b/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-698721-1.pdf new file mode 100644 index 000000000..3705559c7 Binary files /dev/null and b/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-698721-1.pdf differ diff --git a/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-699035-0.pdf b/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-699035-0.pdf new file mode 100644 index 000000000..f1ffa9cdb Binary files /dev/null and b/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-699035-0.pdf differ diff --git a/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-699375-5.pdf b/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-699375-5.pdf new file mode 100644 index 000000000..cd8b89313 Binary files /dev/null and b/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-699375-5.pdf differ diff --git a/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-699488-0.pdf b/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-699488-0.pdf new file mode 100644 index 000000000..56fa4782a Binary files /dev/null and b/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-699488-0.pdf differ diff --git a/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-699554-0.zip-4.pdf b/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-699554-0.zip-4.pdf new file mode 100644 index 000000000..342299d5e Binary files /dev/null and b/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-699554-0.zip-4.pdf differ diff --git a/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-700125-1.pdf b/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-700125-1.pdf new file mode 100644 index 000000000..6862624fb Binary files /dev/null and b/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-700125-1.pdf differ diff --git a/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-700139-0.pdf b/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-700139-0.pdf new file mode 100644 index 000000000..457b28336 Binary files /dev/null and b/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-700139-0.pdf differ diff --git a/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-700236-1.pdf b/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-700236-1.pdf new file mode 100644 index 000000000..545843414 Binary files /dev/null and b/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-700236-1.pdf differ diff --git a/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-700370-2.pdf b/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-700370-2.pdf new file mode 100644 index 000000000..df77a4767 Binary files /dev/null and b/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-700370-2.pdf differ diff --git a/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-700931-0.7z-5.pdf b/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-700931-0.7z-5.pdf new file mode 100644 index 000000000..0b9dcd1b6 Binary files /dev/null and b/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-700931-0.7z-5.pdf differ diff --git a/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-702013-1.pdf b/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-702013-1.pdf new file mode 100644 index 000000000..9ce3dc11b Binary files /dev/null and b/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-702013-1.pdf differ diff --git a/src/UglyToad.PdfPig.Tests/Integration/Documents/felltypes-test.pdf b/src/UglyToad.PdfPig.Tests/Integration/Documents/felltypes-test.pdf new file mode 100644 index 000000000..27189951b Binary files /dev/null and b/src/UglyToad.PdfPig.Tests/Integration/Documents/felltypes-test.pdf differ diff --git a/src/UglyToad.PdfPig.Tests/Integration/VisualVerification/GenerateLetterGlyphImages.cs b/src/UglyToad.PdfPig.Tests/Integration/VisualVerification/GenerateLetterGlyphImages.cs index 51b90f242..d6d32f0ad 100644 --- a/src/UglyToad.PdfPig.Tests/Integration/VisualVerification/GenerateLetterGlyphImages.cs +++ b/src/UglyToad.PdfPig.Tests/Integration/VisualVerification/GenerateLetterGlyphImages.cs @@ -1,13 +1,17 @@ namespace UglyToad.PdfPig.Tests.Integration.VisualVerification { + using PdfPig.Core; using SkiaSharp; using System; using System.IO; + using System.Linq; using UglyToad.PdfPig.Tests.Integration.VisualVerification.SkiaHelpers; using Xunit; public class GenerateLetterGlyphImages { + private const bool RenderGlyphRectangle = true; + private const string NonLatinAcrobatDistiller = "Single Page Non Latin - from acrobat distiller"; private const string SingleGoogleDrivePage = "Single Page Simple - from google drive"; private const string SinglePageFormattedType0Content = "Type0 Font"; @@ -23,10 +27,12 @@ public class GenerateLetterGlyphImages private const string OutputPath = "ImagesGlyphs"; - private const float Scale = 2f; + private const float Scale = 2.5f; private static readonly SKMatrix ScaleMatrix = SKMatrix.CreateScale(Scale, Scale); + private static readonly SKPaint redPaint = new SKPaint() { Color = SKColors.Crimson, StrokeWidth = 1 }; + public GenerateLetterGlyphImages() { if (!Directory.Exists(OutputPath)) @@ -56,17 +62,28 @@ private static void Run(string file, int pageNo = 1) document.AddPageFactory(); var page = document.GetPage(pageNo); + var size = new SKSizeI((int)(page.Width * Scale), (int)(page.Height * Scale)); using (var picture = document.GetPage(pageNo)) + using (var image = SKImage.FromPicture(picture, size, ScaleMatrix)) + using (var bmp = SKBitmap.FromImage(image)) + using (var canvas = new SKCanvas(bmp)) { Assert.NotNull(picture); + if (RenderGlyphRectangle) + { + foreach (var letter in page.Letters) + { + DrawRectangle(letter.GlyphRectangle, canvas, redPaint, size.Height, Scale); + } + } + var imageName = $"{file}_{pageNo}.png"; var savePath = Path.Combine(OutputPath, imageName); using (var fs = new FileStream(savePath, FileMode.Create)) - using (var image = SKImage.FromPicture(picture, new SKSizeI((int)(page.Width * Scale), (int)(page.Height * Scale)), ScaleMatrix)) - using (SKData d = image.Encode(SKEncodedImageFormat.Png, 100)) + using (var d = bmp.Encode(SKEncodedImageFormat.Png, 100)) { d.SaveTo(fs); } @@ -74,6 +91,174 @@ private static void Run(string file, int pageNo = 1) } } + private static void DrawRectangle(PdfRectangle rectangle, SKCanvas graphics, SKPaint pen, int imageHeight, double scale) + { + int GetY(PdfPoint p) + { + return imageHeight - (int)(p.Y * scale); + } + + SKPoint GetPoint(PdfPoint p) + { + return new SKPoint((int)(p.X * scale), GetY(p)); + } + + graphics.DrawLine(GetPoint(rectangle.BottomLeft), GetPoint(rectangle.BottomRight), pen); + graphics.DrawLine(GetPoint(rectangle.BottomRight), GetPoint(rectangle.TopRight), pen); + graphics.DrawLine(GetPoint(rectangle.TopRight), GetPoint(rectangle.TopLeft), pen); + graphics.DrawLine(GetPoint(rectangle.TopLeft), GetPoint(rectangle.BottomLeft), pen); + } + + // veraPDF_Issue1010_x -> https://github.com/veraPDF/veraPDF-library/issues/1010 + [Fact(Skip = "Text is in annotations")] + public void veraPDF_Issue1010_1() + { + Run("FontMatrix-1"); + } + + [Fact(Skip = "Skipping for the moment")] + public void veraPDF_Issue1010_2() + { + Run("FontMatrix-otf"); + } + + [Fact(Skip = "Skipping for the moment")] + public void veraPDF_Issue1010_3() + { + Run("FontMatrix-raw"); + } + + [Fact] + public void veraPDF_Issue1010_4() + { + Run("felltypes-test"); + } + + [Fact(Skip = "Skipping for the moment")] + public void veraPDF_Issue1010_5() + { + Run("FontMatrix-otf-bad-hmtx"); + } + + [Fact(Skip = "Text is in annotations")] + public void veraPDF_Issue1010_6() + { + Run("FontMatrix-concat"); + } + + [Fact] + public void EmbeddedCidFont_1() + { + Run("GHOSTSCRIPT-696547-0.zip-7"); + } + + [Fact] + public void EmbeddedCidFont_2() + { + Run("GHOSTSCRIPT-696547-0.zip-9"); + } + + [Fact] + public void EmbeddedCidFont_Music_1() + { + Run("GHOSTSCRIPT-696171-0"); + } + + [Fact] + public void pdf995_1() + { + Run("GHOSTSCRIPT-699035-0"); + } + + [Fact] + public void pdf995_3() + { + Run("GHOSTSCRIPT-699035-0", 3); + } + + [Fact] + public void EmbeddedType1Cid_1() + { + Run("GHOSTSCRIPT-697507-0"); + } + + [Fact(Skip = "Glyphs cannot be rendered")] + public void EmbeddedType1Cid_MatrixIssue() + { + // It seems it's correct that the glyphs cannot be rendered + // Leaving it there just in case + Run("GHOSTSCRIPT-698168-0"); + } + + [Fact] + public void EmbeddedType1Cid_2() + { + Run("GHOSTSCRIPT-698721-0.zip-6"); + } + + [Fact] + public void EmbeddedType1Cid_3() + { + Run("GHOSTSCRIPT-699554-0.zip-4"); + } + + [Fact] + public void EmbeddedType1Cid_4() + { + Run("GHOSTSCRIPT-700931-0.7z-5"); + } + + [Fact] + public void EmbeddedType1_PatternColor_1() + { + Run("GHOSTSCRIPT-698721-1"); + } + + [Fact] + public void RepresentativeWindEnergyDeals() + { + // Colors look wrong but they are correct as we do not support ICC profile color space. + // The colors are the same as when the document is opened in pdf.js (Firefox). + Run("GHOSTSCRIPT-702013-1"); + } + + [Fact] + public void Psion() + { + Run("GHOSTSCRIPT-700236-1"); + } + + [Fact] + public void RabobankWestland() + { + Run("GHOSTSCRIPT-700125-1"); + } + + [Fact] + public void MammaMia() + { + Run("GHOSTSCRIPT-699375-5"); + } + + [Fact] + public void InstallingMuAndPygameZero() + { + // Still an issue with some of the glyphs + Run("GHOSTSCRIPT-700139-0"); + } + + [Fact] + public void FolhaDeLondrina() + { + Run("GHOSTSCRIPT-699488-0"); + } + + [Fact] + public void LithgowHighSchool() + { + Run("GHOSTSCRIPT-700370-2"); + } + [Fact] public void TIKA_1552_0_4() { @@ -83,7 +268,7 @@ public void TIKA_1552_0_4() [Fact] public void TIKA_1552_0_3() { - Run("TIKA-1552-0",3); + Run("TIKA-1552-0", 3); } [Fact] @@ -119,6 +304,7 @@ public void SinglePageWithType1Content() [Fact] public void SinglePageSimpleFromInkscape() { + // Issue with Glyph related to Bézier curves in PdfPig Run(SingleInkscapePage); } diff --git a/src/UglyToad.PdfPig.Tests/Integration/VisualVerification/SkiaHelpers/SkiaExtensions.cs b/src/UglyToad.PdfPig.Tests/Integration/VisualVerification/SkiaHelpers/SkiaExtensions.cs index 7450c184c..b62733556 100644 --- a/src/UglyToad.PdfPig.Tests/Integration/VisualVerification/SkiaHelpers/SkiaExtensions.cs +++ b/src/UglyToad.PdfPig.Tests/Integration/VisualVerification/SkiaHelpers/SkiaExtensions.cs @@ -8,19 +8,20 @@ internal static class SkiaExtensions { - public static SKMatrix ToSkMatrix(this TransformationMatrix transformationMatrix) + public static SKMatrix ToSKMatrix(this TransformationMatrix transformationMatrix) { return new SKMatrix((float)transformationMatrix.A, (float)transformationMatrix.C, (float)transformationMatrix.E, (float)transformationMatrix.B, (float)transformationMatrix.D, (float)transformationMatrix.F, 0, 0, 1); } - public static SKColor ToSKColor(this IColor? pdfColor, decimal alpha) + public static SKColor ToSKColor(this IColor pdfColor, decimal alpha) { var color = SKColors.Black; - if (pdfColor != null) + if (pdfColor != null && pdfColor is not PatternColor) { var (r, g, b) = pdfColor.ToRGBValues(); + color = new SKColor(Convert.ToByte(r * 255), Convert.ToByte(g * 255), Convert.ToByte(b * 255)); } diff --git a/src/UglyToad.PdfPig.Tests/Integration/VisualVerification/SkiaHelpers/SkiaGlyphStreamProcessor.cs b/src/UglyToad.PdfPig.Tests/Integration/VisualVerification/SkiaHelpers/SkiaGlyphStreamProcessor.cs index c1ea5b37f..006e75219 100644 --- a/src/UglyToad.PdfPig.Tests/Integration/VisualVerification/SkiaHelpers/SkiaGlyphStreamProcessor.cs +++ b/src/UglyToad.PdfPig.Tests/Integration/VisualVerification/SkiaHelpers/SkiaGlyphStreamProcessor.cs @@ -103,9 +103,9 @@ private void ShowVectorFontGlyph(SKPath path, TransformationMatrix textMatrix, TransformationMatrix transformationMatrix) { - var transformMatrix = renderingMatrix.ToSkMatrix() - .PostConcat(textMatrix.ToSkMatrix()) - .PostConcat(transformationMatrix.ToSkMatrix()) + var transformMatrix = renderingMatrix.ToSKMatrix() + .PostConcat(textMatrix.ToSKMatrix()) + .PostConcat(transformationMatrix.ToSKMatrix()) .PostConcat(yAxisFlipMatrix); var style = textRenderingMode.ToSKPaintStyle(); diff --git a/src/UglyToad.PdfPig/PdfFonts/CidFonts/ICidFont.cs b/src/UglyToad.PdfPig/PdfFonts/CidFonts/ICidFont.cs index 415f33624..065df8ae4 100644 --- a/src/UglyToad.PdfPig/PdfFonts/CidFonts/ICidFont.cs +++ b/src/UglyToad.PdfPig/PdfFonts/CidFonts/ICidFont.cs @@ -54,6 +54,8 @@ internal interface ICidFont PdfVector GetDisplacementVector(int characterIdentifier); + TransformationMatrix GetFontMatrix(int characterIdentifier); + /// /// Returns the glyph path for the given character code. /// diff --git a/src/UglyToad.PdfPig/PdfFonts/CidFonts/ICidFontProgram.cs b/src/UglyToad.PdfPig/PdfFonts/CidFonts/ICidFontProgram.cs index 4a8e8e4cd..6d92518fd 100644 --- a/src/UglyToad.PdfPig/PdfFonts/CidFonts/ICidFontProgram.cs +++ b/src/UglyToad.PdfPig/PdfFonts/CidFonts/ICidFontProgram.cs @@ -19,10 +19,15 @@ internal interface ICidFontProgram bool TryGetBoundingAdvancedWidth(int characterIdentifier, out double width); - bool TryGetPath(int characterName, out IReadOnlyList path); + bool TryGetPath(int characterCode, out IReadOnlyList path); bool TryGetPath(int characterCode, Func characterCodeToGlyphId, out IReadOnlyList path); int GetFontMatrixMultiplier(); + + /// + /// Try to get the font matrix if available. + /// + bool TryGetFontMatrix(int characterCode, out TransformationMatrix? matrix); } } diff --git a/src/UglyToad.PdfPig/PdfFonts/CidFonts/PdfCidCompactFontFormatFont.cs b/src/UglyToad.PdfPig/PdfFonts/CidFonts/PdfCidCompactFontFormatFont.cs index 23f69da1a..f492d94d1 100644 --- a/src/UglyToad.PdfPig/PdfFonts/CidFonts/PdfCidCompactFontFormatFont.cs +++ b/src/UglyToad.PdfPig/PdfFonts/CidFonts/PdfCidCompactFontFormatFont.cs @@ -3,9 +3,10 @@ using System; using System.Collections.Generic; using Core; + using Fonts; using Fonts.CompactFontFormat; - internal class PdfCidCompactFontFormatFont : ICidFontProgram + internal sealed class PdfCidCompactFontFormatFont : ICidFontProgram { private readonly CompactFontFormatFontCollection fontCollection; @@ -51,13 +52,13 @@ public bool TryGetBoundingBox(int characterIdentifier, out PdfRectangle bounding var font = GetFont(); - if (font.Encoding == null) + var characterName = GetCharacterName(characterIdentifier); + + if (string.Equals(characterName, GlyphList.NotDefined, StringComparison.OrdinalIgnoreCase)) { return false; } - var characterName = GetCharacterName(characterIdentifier); - boundingBox = font.GetCharacterBoundingBox(characterName) ?? new PdfRectangle(0, 0, 500, 0); return true; @@ -83,16 +84,26 @@ public int GetFontMatrixMultiplier() return 1000; } - public string GetCharacterName(int characterCode) + public bool TryGetFontMatrix(int characterCode, out TransformationMatrix? matrix) { var font = GetFont(); - - if (font.Encoding != null) + var name = font.GetCharacterName(characterCode, true); + if (name == null) { - return font.Encoding.GetName(characterCode); + matrix = null; + return false; } + matrix = font.GetFontMatrix(name); + return matrix.HasValue; + } - return ".notdef"; + public string GetCharacterName(int characterCode) + { + var font = GetFont(); + + var name = font.GetCharacterName(characterCode, true); + + return name ?? GlyphList.NotDefined; } private CompactFontFormatFont GetFont() @@ -109,17 +120,17 @@ private CompactFontFormatFont GetFont() public bool TryGetPath(int characterCode, out IReadOnlyList path) { - path = EmptyArray.Instance; + path = null; var font = GetFont(); - if (font.Encoding == null) + var characterName = GetCharacterName(characterCode); + + if (string.Equals(characterName, GlyphList.NotDefined, StringComparison.OrdinalIgnoreCase)) { return false; } - var characterName = GetCharacterName(characterCode); - if (font.TryGetPath(characterName, out path)) { return true; diff --git a/src/UglyToad.PdfPig/PdfFonts/CidFonts/PdfCidTrueTypeFont.cs b/src/UglyToad.PdfPig/PdfFonts/CidFonts/PdfCidTrueTypeFont.cs index 7ad9a0ba1..df891a354 100644 --- a/src/UglyToad.PdfPig/PdfFonts/CidFonts/PdfCidTrueTypeFont.cs +++ b/src/UglyToad.PdfPig/PdfFonts/CidFonts/PdfCidTrueTypeFont.cs @@ -6,7 +6,7 @@ using Fonts.TrueType; using Fonts.TrueType.Tables; - internal class PdfCidTrueTypeFont : ICidFontProgram + internal sealed class PdfCidTrueTypeFont : ICidFontProgram { private readonly TrueTypeFont font; @@ -35,6 +35,13 @@ public bool TryGetBoundingAdvancedWidth(int characterIdentifier, Func public int GetFontMatrixMultiplier() => font.GetUnitsPerEm(); + public bool TryGetFontMatrix(int characterCode, out TransformationMatrix? matrix) + { + // We don't have a matrix here + matrix = null; + return false; + } + public bool TryGetPath(int characterCode, out IReadOnlyList path) => font.TryGetPath(characterCode, out path); public bool TryGetPath(int characterCode, Func characterCodeToGlyphId, out IReadOnlyList path) diff --git a/src/UglyToad.PdfPig/PdfFonts/CidFonts/Type0CidFont.cs b/src/UglyToad.PdfPig/PdfFonts/CidFonts/Type0CidFont.cs index 4b0d76f64..e628c7509 100644 --- a/src/UglyToad.PdfPig/PdfFonts/CidFonts/Type0CidFont.cs +++ b/src/UglyToad.PdfPig/PdfFonts/CidFonts/Type0CidFont.cs @@ -2,6 +2,7 @@ { using System; using System.Collections.Generic; + using System.Linq; using Core; using Geometry; using Tokens; @@ -11,7 +12,7 @@ /// Type 0 CID fonts contain glyph descriptions based on the /// Adobe Type 1 font format. /// - internal class Type0CidFont : ICidFont + internal sealed class Type0CidFont : ICidFont { private readonly ICidFontProgram fontProgram; private readonly VerticalWritingMetrics verticalWritingMetrics; @@ -26,8 +27,8 @@ internal class Type0CidFont : ICidFont public CharacterIdentifierSystemInfo SystemInfo { get; } - public FontDetails Details => fontProgram?.Details ?? Descriptor?.ToDetails(BaseFont?.Data) - ?? FontDetails.GetDefault(BaseFont?.Data); + public FontDetails Details => fontProgram?.Details ?? Descriptor?.ToDetails(BaseFont?.Data) + ?? FontDetails.GetDefault(BaseFont?.Data); public TransformationMatrix FontMatrix { get; } @@ -37,10 +38,13 @@ internal class Type0CidFont : ICidFont public IReadOnlyDictionary Widths { get; } - public Type0CidFont(ICidFontProgram fontProgram, NameToken type, NameToken subType, NameToken baseFont, + public Type0CidFont(ICidFontProgram fontProgram, + NameToken type, + NameToken subType, + NameToken baseFont, CharacterIdentifierSystemInfo systemInfo, - FontDescriptor descriptor, - VerticalWritingMetrics verticalWritingMetrics, + FontDescriptor descriptor, + VerticalWritingMetrics verticalWritingMetrics, IReadOnlyDictionary widths, double? defaultWidth) { @@ -80,7 +84,7 @@ public double GetWidthFromDictionary(int cid) { return defaultWidth.Value; } - + if (Descriptor == null) { return 1000; @@ -132,6 +136,16 @@ public PdfVector GetDisplacementVector(int characterIdentifier) return verticalWritingMetrics.GetDisplacementVector(characterIdentifier); } + public TransformationMatrix GetFontMatrix(int characterIdentifier) + { + if (fontProgram == null) + { + return FontMatrix; + } + + return fontProgram.TryGetFontMatrix(characterIdentifier, out var m) ? m.Value : FontMatrix; + } + public bool TryGetPath(int characterCode, out IReadOnlyList path) { path = null; @@ -142,7 +156,7 @@ public bool TryGetPath(int characterCode, out IReadOnlyList path) return fontProgram.TryGetPath(characterCode, out path); } - + public bool TryGetPath(int characterCode, Func characterCodeToGlyphId, out IReadOnlyList path) { path = null; @@ -156,12 +170,36 @@ public bool TryGetPath(int characterCode, Func characterCodeToGlyphId public bool TryGetNormalisedPath(int characterCode, out IReadOnlyList path) { - return TryGetPath(characterCode, out path); + path = null; + if (fontProgram == null) + { + return false; + } + + if (fontProgram.TryGetPath(characterCode, out path)) + { + path = GetFontMatrix(characterCode).Transform(path).ToArray(); + return true; + } + + return false; } public bool TryGetNormalisedPath(int characterCode, Func characterCodeToGlyphId, out IReadOnlyList path) { - return TryGetPath(characterCode, characterCodeToGlyphId, out path); + path = null; + if (fontProgram == null) + { + return false; + } + + if (fontProgram.TryGetPath(characterCode, characterCodeToGlyphId, out path)) + { + path = GetFontMatrix(characterCode).Transform(path).ToArray(); + return true; + } + + return false; } } } diff --git a/src/UglyToad.PdfPig/PdfFonts/CidFonts/Type2CidFont.cs b/src/UglyToad.PdfPig/PdfFonts/CidFonts/Type2CidFont.cs index ad015f6b7..f84dff540 100644 --- a/src/UglyToad.PdfPig/PdfFonts/CidFonts/Type2CidFont.cs +++ b/src/UglyToad.PdfPig/PdfFonts/CidFonts/Type2CidFont.cs @@ -12,7 +12,7 @@ /// Type 2 CID fonts contains glyph descriptions based on /// the TrueType font format. /// - internal class Type2CidFont : ICidFont + internal sealed class Type2CidFont : ICidFont { private readonly ICidFontProgram fontProgram; private readonly VerticalWritingMetrics verticalWritingMetrics; @@ -35,10 +35,14 @@ internal class Type2CidFont : ICidFont public FontDescriptor Descriptor { get; } public FontDetails Details => fontProgram?.Details ?? Descriptor?.ToDetails(BaseFont?.Data) - ?? FontDetails.GetDefault(BaseFont?.Data); - - public Type2CidFont(NameToken type, NameToken subType, NameToken baseFont, CharacterIdentifierSystemInfo systemInfo, - FontDescriptor descriptor, ICidFontProgram fontProgram, + ?? FontDetails.GetDefault(BaseFont?.Data); + + public Type2CidFont(NameToken type, + NameToken subType, + NameToken baseFont, + CharacterIdentifierSystemInfo systemInfo, + FontDescriptor descriptor, + ICidFontProgram fontProgram, VerticalWritingMetrics verticalWritingMetrics, IReadOnlyDictionary widths, double? defaultWidth, @@ -71,6 +75,7 @@ public double GetWidthFromFont(int characterIdentifier) { return width; } + // TODO: Read the font width from the font program. return GetWidthFromDictionary(characterIdentifier); } @@ -117,6 +122,11 @@ public PdfVector GetDisplacementVector(int characterIdentifier) return verticalWritingMetrics.GetDisplacementVector(characterIdentifier); } + public TransformationMatrix GetFontMatrix(int characterIdentifier) + { + return FontMatrix; + } + public bool TryGetPath(int characterCode, out IReadOnlyList path) => TryGetPath(characterCode, cidToGid.GetGlyphIndex, out path); public bool TryGetPath(int characterCode, Func characterCodeToGlyphId, out IReadOnlyList path) @@ -132,18 +142,24 @@ public bool TryGetPath(int characterCode, Func characterCodeToGlyphId public bool TryGetNormalisedPath(int characterCode, out IReadOnlyList path) { - if (!TryGetPath(characterCode, out path)) + return TryGetNormalisedPath(characterCode, cidToGid.GetGlyphIndex, out path); + } + + public bool TryGetNormalisedPath(int characterCode, Func characterCodeToGlyphId, out IReadOnlyList path) + { + path = null; + if (fontProgram == null) { return false; } - path = FontMatrix.Transform(path).ToList(); - return true; - } + if (fontProgram.TryGetPath(characterCode, characterCodeToGlyphId, out path)) + { + path = GetFontMatrix(characterCode).Transform(path).ToArray(); + return true; + } - public bool TryGetNormalisedPath(int characterCode, Func characterCodeToGlyphId, out IReadOnlyList path) - { - throw new NotImplementedException(); + return false; } } } \ No newline at end of file diff --git a/src/UglyToad.PdfPig/PdfFonts/Composite/Type0Font.cs b/src/UglyToad.PdfPig/PdfFonts/Composite/Type0Font.cs index 2d120aa81..8bf673482 100644 --- a/src/UglyToad.PdfPig/PdfFonts/Composite/Type0Font.cs +++ b/src/UglyToad.PdfPig/PdfFonts/Composite/Type0Font.cs @@ -13,7 +13,7 @@ /// /// Defines glyphs using a CIDFont /// - internal class Type0Font : IFont, IVerticalWritingSupported + internal sealed class Type0Font : IFont, IVerticalWritingSupported { private readonly CMap ucs2CMap; // ReSharper disable once NotAccessedField.Local @@ -117,17 +117,18 @@ public CharacterBoundingBox GetBoundingBox(int characterCode) return cached; } - var matrix = GetFontMatrix(); - - var boundingBox = GetBoundingBoxInGlyphSpace(characterCode); + var characterIdentifier = CMap.ConvertToCid(characterCode); - boundingBox = matrix.Transform(boundingBox); + // Get the bounding box in glyph space + var boundingBox = CidFont.GetBoundingBox(characterIdentifier); - var characterIdentifier = CMap.ConvertToCid(characterCode); + boundingBox = CidFont.GetFontMatrix(characterIdentifier).Transform(boundingBox); var width = CidFont.GetWidthFromFont(characterIdentifier); - var advanceWidth = matrix.TransformX(width); + var advanceWidth = GetFontMatrix().TransformX(width); + // BobLD: Not sure why we don't need CidFont.GetFontMatrix(characterCode) + // Might be related to https://github.com/veraPDF/veraPDF-library/issues/1010 var result = new CharacterBoundingBox(boundingBox, advanceWidth); @@ -136,13 +137,6 @@ public CharacterBoundingBox GetBoundingBox(int characterCode) return result; } - public PdfRectangle GetBoundingBoxInGlyphSpace(int characterCode) - { - var characterIdentifier = CMap.ConvertToCid(characterCode); - - return CidFont.GetBoundingBox(characterIdentifier); - } - public TransformationMatrix GetFontMatrix() { return CidFont.FontMatrix; @@ -165,13 +159,17 @@ public PdfVector GetDisplacementVector(int characterCode) /// public bool TryGetPath(int characterCode, out IReadOnlyList path) { - return CidFont.TryGetPath(characterCode, out path); + var characterIdentifier = CMap.ConvertToCid(characterCode); + + return CidFont.TryGetPath(characterIdentifier, out path); } /// public bool TryGetNormalisedPath(int characterCode, out IReadOnlyList path) { - return CidFont.TryGetNormalisedPath(characterCode, out path); + var characterIdentifier = CMap.ConvertToCid(characterCode); + + return CidFont.TryGetNormalisedPath(characterIdentifier, out path); } } } diff --git a/src/UglyToad.PdfPig/PdfFonts/Simple/TrueTypeSimpleFont.cs b/src/UglyToad.PdfPig/PdfFonts/Simple/TrueTypeSimpleFont.cs index 6b9b09ac2..bf7a87d69 100644 --- a/src/UglyToad.PdfPig/PdfFonts/Simple/TrueTypeSimpleFont.cs +++ b/src/UglyToad.PdfPig/PdfFonts/Simple/TrueTypeSimpleFont.cs @@ -12,7 +12,7 @@ using Tokens; using Util.JetBrains.Annotations; - internal class TrueTypeSimpleFont : IFont + internal sealed class TrueTypeSimpleFont : IFont { private static readonly TransformationMatrix DefaultTransformation = TransformationMatrix.FromValues(1 / 1000.0, 0, 0, 1 / 1000.0, 0, 0); @@ -230,7 +230,7 @@ bool HasFlag(FontDescriptorFlags value, FontDescriptorFlags target) return null; } - if (string.Equals(name, ".notdef", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(name, GlyphList.NotDefined, StringComparison.OrdinalIgnoreCase)) { return 0; } @@ -328,7 +328,7 @@ public bool TryGetPath(int characterCode, out IReadOnlyList path) { if (font == null) { - path = EmptyArray.Instance; + path = null; return false; } @@ -343,7 +343,7 @@ public bool TryGetNormalisedPath(int characterCode, out IReadOnlyList /// Some TrueType fonts use both the Standard 14 descriptor and the TrueType font from disk. /// - internal class TrueTypeStandard14FallbackSimpleFont : IFont + internal sealed class TrueTypeStandard14FallbackSimpleFont : IFont { private static readonly TransformationMatrix DefaultTransformation = TransformationMatrix.FromValues(1 / 1000.0, 0, 0, 1 / 1000.0, 0, 0); @@ -152,7 +152,7 @@ public bool TryGetNormalisedPath(int characterCode, out IReadOnlyList /// A font based on the Adobe Type 1 font format. /// - internal class Type1FontSimple : IFont + internal sealed class Type1FontSimple : IFont { private static readonly TransformationMatrix DefaultTransformationMatrix = TransformationMatrix.FromValues(0.001, 0, 0, 0.001, 0, 0); @@ -197,7 +197,7 @@ private PdfRectangle GetBoundingBoxInGlyphSpace(int characterCode) } else { - characterName = cffFont.GetCharacterName(characterCode); + characterName = cffFont.GetCharacterName(characterCode, false); } rect = first.GetCharacterBoundingBox(characterName); @@ -247,7 +247,7 @@ public bool TryGetPath(int characterCode, out IReadOnlyList path) } else { - characterName = cffFont.GetCharacterName(characterCode); + characterName = cffFont.GetCharacterName(characterCode, false); } tempPath = first.GetCharacterPath(characterName); @@ -267,7 +267,7 @@ public bool TryGetNormalisedPath(int characterCode, out IReadOnlyList /// A font using one of the Adobe Standard 14 fonts. Can use a custom encoding. /// - internal class Type1Standard14Font : IFont + internal sealed class Type1Standard14Font : IFont { private readonly AdobeFontMetrics standardFontMetrics; private readonly Encoding encoding; @@ -49,7 +49,7 @@ public int ReadCharacterCode(IInputBytes bytes, out int codeLength) public bool TryGetUnicode(int characterCode, out string value) { var name = encoding.GetName(characterCode); - if (string.Equals(name, ".notdef", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(name, GlyphList.NotDefined, StringComparison.OrdinalIgnoreCase)) { value = null; return false; diff --git a/src/UglyToad.PdfPig/Writer/Fonts/Standard14WritingFont.cs b/src/UglyToad.PdfPig/Writer/Fonts/Standard14WritingFont.cs index fcf71b306..398d0d3d2 100644 --- a/src/UglyToad.PdfPig/Writer/Fonts/Standard14WritingFont.cs +++ b/src/UglyToad.PdfPig/Writer/Fonts/Standard14WritingFont.cs @@ -10,7 +10,7 @@ using PdfPig.Fonts.Encodings; using Tokens; - internal class Standard14WritingFont : IWritingFont + internal sealed class Standard14WritingFont : IWritingFont { private readonly AdobeFontMetrics metrics; @@ -25,7 +25,6 @@ public Standard14WritingFont(AdobeFontMetrics metrics) public bool TryGetBoundingBox(char character, out PdfRectangle boundingBox) { - boundingBox = default(PdfRectangle); int code = CodeMapIfUnicode(character); @@ -118,7 +117,7 @@ public byte GetValueForCharacter(char character) private int UnicodeToSymbolCode(char character) { var name = GlyphList.AdobeGlyphList.UnicodeCodePointToName(character); - if (string.Equals(name, ".notdef", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(name, GlyphList.NotDefined, StringComparison.OrdinalIgnoreCase)) { return -1; } @@ -134,7 +133,7 @@ private int UnicodeToSymbolCode(char character) private int UnicodeToZapfDingbats(char character) { var name = GlyphList.ZapfDingbats.UnicodeCodePointToName(character); - if (string.Equals(name, ".notdef", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(name, GlyphList.NotDefined, StringComparison.OrdinalIgnoreCase)) { Debug.WriteLine($"Failed to find Unicode character '{character}' (0x{(int)character:X})."); return -1; @@ -147,13 +146,12 @@ private int UnicodeToZapfDingbats(char character) Debug.WriteLine($"Found Unicode point '{character}' (0x{(int)character:X}) but glphy name '{name}' not found in font '{metrics.FontName}' (font specific encoding: ZapfDingbats)."); } return code; - } private int UnicodeToStandardEncoding(char character) { var name = GlyphList.AdobeGlyphList.UnicodeCodePointToName(character); - if (string.Equals(name, ".notdef", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(name, GlyphList.NotDefined, StringComparison.OrdinalIgnoreCase)) { Debug.WriteLine($"Failed to find Unicode character '{character}' (0x{(int)character:X})."); return -1; @@ -167,7 +165,7 @@ private int UnicodeToStandardEncoding(char character) code = standardEncoding.GetCode(nameCapitalisedChange); if (code == -1) { - Debug.WriteLine($"Found Unicode point '{character}' (0x{(int)character:X}) but glphy name '{name}' not found in font '{metrics.FontName}' (StandardEncoding)."); + Debug.WriteLine($"Found Unicode point '{character}' (0x{(int)character:X}) but glyph name '{name}' not found in font '{metrics.FontName}' (StandardEncoding)."); } } return code;