mirror of
https://github.com/UglyToad/PdfPig.git
synced 2025-09-18 18:27:55 +08:00
expose font details on individual letters
also fixes a regression for image extraction
This commit is contained in:
@@ -29,6 +29,16 @@
|
||||
/// </summary>
|
||||
public TransformationMatrix FontMatrix => TopDictionary.FontMatrix;
|
||||
|
||||
/// <summary>
|
||||
/// The value of Weight from the top dictionary or <see langword="null"/>.
|
||||
/// </summary>
|
||||
public string Weight => TopDictionary?.Weight;
|
||||
|
||||
/// <summary>
|
||||
/// The value of Italic Angle from the top dictionary or 0.
|
||||
/// </summary>
|
||||
public decimal ItalicAngle => TopDictionary?.ItalicAngle ?? 0;
|
||||
|
||||
internal CompactFontFormatFont(CompactFontFormatTopLevelDictionary topDictionary, CompactFontFormatPrivateDictionary privateDictionary,
|
||||
ICompactFontFormatCharset charset,
|
||||
Union<Type1CharStrings, Type2CharStrings> charStrings, Encoding fontEncoding)
|
||||
|
@@ -91,6 +91,7 @@
|
||||
"UglyToad.PdfPig.PdfFonts.DescriptorFontFile",
|
||||
"UglyToad.PdfPig.PdfFonts.FontDescriptor",
|
||||
"UglyToad.PdfPig.PdfFonts.FontDescriptorFlags",
|
||||
"UglyToad.PdfPig.PdfFonts.FontDetails",
|
||||
"UglyToad.PdfPig.PdfFonts.FontStretch",
|
||||
"UglyToad.PdfPig.Geometry.GeometryExtensions",
|
||||
"UglyToad.PdfPig.Graphics.Colors.CMYKColor",
|
||||
|
@@ -2,6 +2,7 @@
|
||||
{
|
||||
using Core;
|
||||
using Graphics.Colors;
|
||||
using PdfFonts;
|
||||
|
||||
/// <summary>
|
||||
/// A glyph or combination of glyphs (characters) drawn by a PDF content stream.
|
||||
@@ -53,7 +54,12 @@
|
||||
/// <summary>
|
||||
/// The name of the font.
|
||||
/// </summary>
|
||||
public string FontName { get; }
|
||||
public string FontName => Font?.Name;
|
||||
|
||||
/// <summary>
|
||||
/// Details about the font for this letter.
|
||||
/// </summary>
|
||||
public FontDetails Font { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The color of the letter.
|
||||
@@ -79,7 +85,7 @@
|
||||
PdfPoint endBaseLine,
|
||||
double width,
|
||||
double fontSize,
|
||||
string fontName,
|
||||
FontDetails font,
|
||||
IColor color,
|
||||
double pointSize,
|
||||
int textSequence)
|
||||
@@ -90,7 +96,7 @@
|
||||
EndBaseLine = endBaseLine;
|
||||
Width = width;
|
||||
FontSize = fontSize;
|
||||
FontName = fontName;
|
||||
Font = font;
|
||||
Color = color ?? GrayColor.Black;
|
||||
PointSize = pointSize;
|
||||
TextSequence = textSequence;
|
||||
|
@@ -251,7 +251,7 @@
|
||||
transformedPdfBounds.BottomRight,
|
||||
transformedPdfBounds.Width,
|
||||
fontSize,
|
||||
font.Name.Data,
|
||||
font.Details,
|
||||
color,
|
||||
pointSize,
|
||||
textSequence);
|
||||
|
@@ -34,6 +34,8 @@
|
||||
/// </summary>
|
||||
CharacterIdentifierSystemInfo SystemInfo { get; }
|
||||
|
||||
FontDetails Details { get; }
|
||||
|
||||
TransformationMatrix FontMatrix { get; }
|
||||
|
||||
CidFontType CidFontType { get; }
|
||||
|
@@ -8,6 +8,8 @@
|
||||
/// </summary>
|
||||
internal interface ICidFontProgram
|
||||
{
|
||||
FontDetails Details { get; }
|
||||
|
||||
bool TryGetBoundingBox(int characterIdentifier, out PdfRectangle boundingBox);
|
||||
|
||||
bool TryGetBoundingBox(int characterIdentifier, Func<int, int?> characterCodeToGlyphId, out PdfRectangle boundingBox);
|
||||
|
@@ -8,9 +8,36 @@
|
||||
{
|
||||
private readonly CompactFontFormatFontCollection fontCollection;
|
||||
|
||||
public FontDetails Details { get; }
|
||||
|
||||
public PdfCidCompactFontFormatFont(CompactFontFormatFontCollection fontCollection)
|
||||
{
|
||||
this.fontCollection = fontCollection;
|
||||
Details = GetDetails(fontCollection?.FirstFont);
|
||||
}
|
||||
|
||||
private static FontDetails GetDetails(CompactFontFormatFont font)
|
||||
{
|
||||
if (font == null)
|
||||
{
|
||||
return FontDetails.GetDefault();
|
||||
}
|
||||
|
||||
FontDetails WithWeightValues(bool isbold, int weight) => new FontDetails(null, isbold, weight, font.ItalicAngle != 0);
|
||||
|
||||
switch (font.Weight?.ToLowerInvariant())
|
||||
{
|
||||
case "light":
|
||||
return WithWeightValues(false, 300);
|
||||
case "semibold":
|
||||
return WithWeightValues(true, 600);
|
||||
case "bold":
|
||||
return WithWeightValues(true, FontDetails.BoldWeight);
|
||||
case "black":
|
||||
return WithWeightValues(true, 900);
|
||||
default:
|
||||
return WithWeightValues(false, FontDetails.DefaultWeight);
|
||||
}
|
||||
}
|
||||
|
||||
public TransformationMatrix GetFontTransformationMatrix() => fontCollection.GetFirstTransformationMatrix();
|
||||
|
@@ -3,14 +3,23 @@
|
||||
using System;
|
||||
using Core;
|
||||
using Fonts.TrueType;
|
||||
using Fonts.TrueType.Tables;
|
||||
|
||||
internal class PdfCidTrueTypeFont : ICidFontProgram
|
||||
{
|
||||
private readonly TrueTypeFont font;
|
||||
|
||||
public FontDetails Details { get; }
|
||||
|
||||
public PdfCidTrueTypeFont(TrueTypeFont font)
|
||||
{
|
||||
this.font = font ?? throw new ArgumentNullException(nameof(font));
|
||||
|
||||
var header = font.TableRegister.HeaderTable;
|
||||
var isBold = header.MacStyle.HasFlag(HeaderTable.HeaderMacStyle.Bold);
|
||||
Details = new FontDetails(font.Name, isBold,
|
||||
isBold ? FontDetails.BoldWeight : FontDetails.DefaultWeight,
|
||||
header.MacStyle.HasFlag(HeaderTable.HeaderMacStyle.Italic));
|
||||
}
|
||||
|
||||
public bool TryGetBoundingBox(int characterIdentifier, out PdfRectangle boundingBox) => TryGetBoundingBox(characterIdentifier, null, out boundingBox);
|
||||
|
@@ -25,6 +25,9 @@
|
||||
|
||||
public CharacterIdentifierSystemInfo SystemInfo { get; }
|
||||
|
||||
public FontDetails Details => fontProgram?.Details ?? Descriptor?.ToDetails(BaseFont?.Data)
|
||||
?? FontDetails.GetDefault(BaseFont?.Data);
|
||||
|
||||
public TransformationMatrix FontMatrix { get; }
|
||||
|
||||
public CidFontType CidFontType => CidFontType.Type0;
|
||||
|
@@ -32,6 +32,9 @@
|
||||
|
||||
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,
|
||||
VerticalWritingMetrics verticalWritingMetrics,
|
||||
|
@@ -36,6 +36,8 @@
|
||||
|
||||
public bool IsVertical => CMap.WritingMode == WritingMode.Vertical;
|
||||
|
||||
public FontDetails Details { get; }
|
||||
|
||||
public Type0Font(NameToken baseFont, ICidFont cidFont, CMap cmap, CMap toUnicodeCMap,
|
||||
CMap ucs2CMap,
|
||||
bool isChineseJapaneseOrKorean)
|
||||
@@ -47,6 +49,8 @@
|
||||
CidFont = cidFont ?? throw new ArgumentNullException(nameof(cidFont));
|
||||
CMap = cmap ?? throw new ArgumentNullException(nameof(cmap));
|
||||
ToUnicode = new ToUnicodeCMap(toUnicodeCMap);
|
||||
Details = cidFont.Details?.WithName(Name.Data)
|
||||
?? FontDetails.GetDefault(Name.Data);
|
||||
}
|
||||
|
||||
public int ReadCharacterCode(IInputBytes bytes, out int codeLength)
|
||||
|
@@ -179,6 +179,14 @@
|
||||
CharSet = builder.CharSet;
|
||||
}
|
||||
|
||||
internal FontDetails ToDetails(string name = null)
|
||||
{
|
||||
return new FontDetails(name ?? FontName ?? string.Empty,
|
||||
FontWeight > 500,
|
||||
(int)FontWeight,
|
||||
Flags.HasFlag(FontDescriptorFlags.Italic));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides a mutable way to construct a <see cref="FontDescriptor"/>.
|
||||
/// </summary>
|
||||
|
66
src/UglyToad.PdfPig/PdfFonts/FontDetails.cs
Normal file
66
src/UglyToad.PdfPig/PdfFonts/FontDetails.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
namespace UglyToad.PdfPig.PdfFonts
|
||||
{
|
||||
/// <summary>
|
||||
/// Summary details of the font used to draw a glyph.
|
||||
/// </summary>
|
||||
public class FontDetails
|
||||
{
|
||||
/// <summary>
|
||||
/// The normal weight for a font.
|
||||
/// </summary>
|
||||
public const int DefaultWeight = 500;
|
||||
|
||||
/// <summary>
|
||||
/// The bold weight for a font.
|
||||
/// </summary>
|
||||
public const int BoldWeight = 700;
|
||||
|
||||
/// <summary>
|
||||
/// The font name.
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the font is bold.
|
||||
/// </summary>
|
||||
public bool IsBold { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The font weight, values above 500 represent bold.
|
||||
/// </summary>
|
||||
public int Weight { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the font is italic.
|
||||
/// </summary>
|
||||
public bool IsItalic { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="FontDetails"/>.
|
||||
/// </summary>
|
||||
public FontDetails(string name, bool isBold, int weight, bool isItalic)
|
||||
{
|
||||
Name = name ?? string.Empty;
|
||||
IsBold = isBold;
|
||||
Weight = weight;
|
||||
IsItalic = isItalic;
|
||||
}
|
||||
|
||||
internal static FontDetails GetDefault(string name = null) => new FontDetails(name ?? string.Empty,
|
||||
false,
|
||||
DefaultWeight,
|
||||
false);
|
||||
|
||||
internal FontDetails WithName(string name) => name != null
|
||||
? new FontDetails(name, IsBold, Weight, IsItalic)
|
||||
: this;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
var boldString = IsBold ? " (bold)" : string.Empty;
|
||||
var italicString = IsItalic ? " (italic)" : string.Empty;
|
||||
return $"{Name}{boldString}{italicString}";
|
||||
}
|
||||
}
|
||||
}
|
@@ -9,6 +9,8 @@
|
||||
|
||||
bool IsVertical { get; }
|
||||
|
||||
FontDetails Details { get; }
|
||||
|
||||
int ReadCharacterCode(IInputBytes bytes, out int codeLength);
|
||||
|
||||
bool TryGetUnicode(int characterCode, out string value);
|
||||
|
@@ -35,6 +35,8 @@
|
||||
|
||||
public bool IsVertical { get; }
|
||||
|
||||
public FontDetails Details { get; }
|
||||
|
||||
[NotNull]
|
||||
public ToUnicodeCMap ToUnicode { get; set; }
|
||||
|
||||
@@ -55,6 +57,8 @@
|
||||
Name = name;
|
||||
IsVertical = false;
|
||||
ToUnicode = new ToUnicodeCMap(toUnicodeCMap);
|
||||
Details = descriptor?.ToDetails(Name?.Data)
|
||||
?? FontDetails.GetDefault(Name?.Data);
|
||||
}
|
||||
|
||||
public int ReadCharacterCode(IInputBytes bytes, out int codeLength)
|
||||
|
@@ -26,6 +26,8 @@
|
||||
|
||||
public bool IsVertical { get; } = false;
|
||||
|
||||
public FontDetails Details { get; set; }
|
||||
|
||||
public TrueTypeStandard14FallbackSimpleFont(NameToken name, AdobeFontMetrics fontMetrics, Encoding encoding, TrueTypeFont font,
|
||||
MetricOverrides overrides)
|
||||
{
|
||||
@@ -34,6 +36,10 @@
|
||||
this.font = font;
|
||||
this.overrides = overrides;
|
||||
Name = name;
|
||||
Details = fontMetrics == null ? FontDetails.GetDefault(Name?.Data) : new FontDetails(Name?.Data,
|
||||
fontMetrics.Weight == "Bold",
|
||||
fontMetrics.Weight == "Bold" ? 700 : FontDetails.DefaultWeight,
|
||||
fontMetrics.ItalicAngle != 0);
|
||||
}
|
||||
|
||||
public int ReadCharacterCode(IInputBytes bytes, out int codeLength)
|
||||
|
@@ -41,6 +41,8 @@
|
||||
|
||||
public bool IsVertical { get; } = false;
|
||||
|
||||
public FontDetails Details { get; }
|
||||
|
||||
public Type1FontSimple(NameToken name, int firstChar, int lastChar, double[] widths, FontDescriptor fontDescriptor, Encoding encoding,
|
||||
CMap toUnicodeCMap,
|
||||
Union<Type1Font, CompactFontFormatFontCollection> fontProgram)
|
||||
@@ -70,6 +72,8 @@
|
||||
fontMatrix = matrix;
|
||||
|
||||
Name = name;
|
||||
Details = fontDescriptor?.ToDetails(name?.Data)
|
||||
?? FontDetails.GetDefault(name?.Data);
|
||||
}
|
||||
|
||||
public int ReadCharacterCode(IInputBytes bytes, out int codeLength)
|
||||
|
@@ -17,8 +17,11 @@ namespace UglyToad.PdfPig.PdfFonts.Simple
|
||||
private readonly Encoding encoding;
|
||||
|
||||
public NameToken Name { get; }
|
||||
|
||||
public bool IsVertical { get; }
|
||||
|
||||
public FontDetails Details { get; }
|
||||
|
||||
private readonly TransformationMatrix fontMatrix = TransformationMatrix.FromValues(0.001, 0, 0, 0.001, 0, 0);
|
||||
|
||||
public Type1Standard14Font(AdobeFontMetrics standardFontMetrics, Encoding overrideEncoding = null)
|
||||
@@ -29,6 +32,10 @@ namespace UglyToad.PdfPig.PdfFonts.Simple
|
||||
Name = NameToken.Create(standardFontMetrics.FontName);
|
||||
|
||||
IsVertical = false;
|
||||
Details = new FontDetails(Name.Data,
|
||||
standardFontMetrics.Weight == "Bold",
|
||||
standardFontMetrics.Weight == "Bold" ? 700 : FontDetails.DefaultWeight,
|
||||
standardFontMetrics.ItalicAngle != 0);
|
||||
}
|
||||
|
||||
public int ReadCharacterCode(IInputBytes bytes, out int codeLength)
|
||||
|
@@ -24,6 +24,8 @@
|
||||
|
||||
public bool IsVertical { get; } = false;
|
||||
|
||||
public FontDetails Details { get; }
|
||||
|
||||
public Type3Font(NameToken name, PdfRectangle boundingBox, TransformationMatrix fontMatrix,
|
||||
Encoding encoding, int firstChar, int lastChar, double[] widths,
|
||||
CMap toUnicodeCMap)
|
||||
@@ -37,6 +39,7 @@
|
||||
this.lastChar = lastChar;
|
||||
this.widths = widths;
|
||||
this.toUnicodeCMap = new ToUnicodeCMap(toUnicodeCMap);
|
||||
Details = FontDetails.GetDefault(name?.Data);
|
||||
}
|
||||
|
||||
public int ReadCharacterCode(IInputBytes bytes, out int codeLength)
|
||||
|
@@ -16,8 +16,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using PdfFonts;
|
||||
using Tokens;
|
||||
using UglyToad.PdfPig.Graphics.Operations.PathPainting;
|
||||
using Graphics.Operations.PathPainting;
|
||||
|
||||
/// <summary>
|
||||
/// A builder used to add construct a page in a PDF document.
|
||||
@@ -388,7 +389,7 @@
|
||||
|
||||
var documentSpace = textMatrix.Transform(renderingMatrix.Transform(fontMatrix.Transform(rect)));
|
||||
|
||||
var letter = new Letter(c.ToString(), documentSpace, advanceRect.BottomLeft, advanceRect.BottomRight, width, (double)fontSize, font.Name,
|
||||
var letter = new Letter(c.ToString(), documentSpace, advanceRect.BottomLeft, advanceRect.BottomRight, width, (double)fontSize, FontDetails.GetDefault(font.Name),
|
||||
GrayColor.Black,
|
||||
(double)fontSize,
|
||||
textSequence);
|
||||
|
@@ -1,4 +1,6 @@
|
||||
namespace UglyToad.PdfPig.XObjects
|
||||
using UglyToad.PdfPig.Parser.Parts;
|
||||
|
||||
namespace UglyToad.PdfPig.XObjects
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -66,14 +68,35 @@
|
||||
var interpolate = dictionary.TryGet(NameToken.Interpolate, pdfScanner, out BooleanToken interpolateToken)
|
||||
&& interpolateToken.Data;
|
||||
|
||||
var filters = filterProvider.GetFilters(xObject.Stream.StreamDictionary);
|
||||
var supportsFilters = true;
|
||||
foreach (var filter in filters)
|
||||
DictionaryToken filterDictionary = xObject.Stream.StreamDictionary;
|
||||
if (xObject.Stream.StreamDictionary.TryGet(NameToken.Filter, out var filterToken)
|
||||
&& filterToken is IndirectReferenceToken)
|
||||
{
|
||||
if (!filter.IsSupported)
|
||||
if (filterDictionary.TryGet(NameToken.Filter, pdfScanner, out ArrayToken filterArray))
|
||||
{
|
||||
supportsFilters = false;
|
||||
break;
|
||||
filterDictionary = filterDictionary.With(NameToken.Filter, filterArray);
|
||||
}
|
||||
else if (filterDictionary.TryGet(NameToken.Filter, pdfScanner, out NameToken filterNameToken))
|
||||
{
|
||||
filterDictionary = filterDictionary.With(NameToken.Filter, filterNameToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
filterDictionary = null;
|
||||
}
|
||||
}
|
||||
|
||||
var supportsFilters = filterDictionary != null;
|
||||
if (filterDictionary != null)
|
||||
{
|
||||
var filters = filterProvider.GetFilters(filterDictionary);
|
||||
foreach (var filter in filters)
|
||||
{
|
||||
if (!filter.IsSupported)
|
||||
{
|
||||
supportsFilters = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user