Compare commits

..

5 Commits

Author SHA1 Message Date
Eliot Jones
256c2833ab 0.1.2-alpha002 2020-05-10 16:36:14 +01:00
BobLd
bb33741552 Fix KdTree.FindNearestNeighbours(k) returning the pivot itself 2020-05-10 15:49:00 +01:00
Eliot Jones
0512bb1e4f handle indirect references appearing in cid font widths array #174 2020-05-10 15:46:38 +01:00
BobLd
f91acefcfa Set ClipPaths to false if no ParsingOptions given (consistent behaviour) 2020-04-27 17:21:52 +01:00
Eliot Jones
09b951f667 expose font details on individual letters
also fixes a regression for image extraction
2020-04-25 17:15:26 +01:00
35 changed files with 234 additions and 38 deletions

View File

@@ -81,6 +81,7 @@
/// <summary>
/// Rotation angle of the rectangle. Counterclockwise, in degrees.
/// <para>-180 ≤ θ ≤ 180</para>
/// </summary>
public double Rotation
{
@@ -173,6 +174,9 @@
BottomLeft.Translate(dx, dy), BottomRight.Translate(dx, dy));
}
/// <summary>
/// -π ≤ θ ≤ π
/// </summary>
private double GetT()
{
if (!BottomRight.Equals(BottomLeft))

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net45;net451;net452;net46;net461;net462;net47</TargetFrameworks>
<LangVersion>latest</LangVersion>
<Version>0.1.2-alpha001</Version>
<Version>0.1.2-alpha002</Version>
<IsTestProject>False</IsTestProject>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<SignAssembly>true</SignAssembly>

View File

@@ -49,12 +49,13 @@
/// <summary>
/// The angle in degrees between the horizontal axis and the line between two points.
/// <para>-180 ≤ θ ≤ 180</para>
/// </summary>
/// <param name="point1">The first point.</param>
/// <param name="point2">The second point.</param>
public static double Angle(PdfPoint point1, PdfPoint point2)
/// <param name="startPoint">The first point.</param>
/// <param name="endPoint">The second point.</param>
public static double Angle(PdfPoint startPoint, PdfPoint endPoint)
{
return Math.Atan2(point2.Y - point1.Y, point2.X - point1.X) * 57.29577951;
return Math.Atan2(endPoint.Y - startPoint.Y, endPoint.X - startPoint.X) * 180 / Math.PI;
}
/// <summary>

View File

@@ -250,7 +250,7 @@
var point = pivotPointFunc(pivot);
var currentNearestNode = node;
var currentDistance = distance(node.Value, point);
if (!queue.IsFull || currentDistance <= queue.LastDistance)
if ((!queue.IsFull || currentDistance <= queue.LastDistance) && !node.Element.Equals(pivot))
{
queue.Add(currentDistance, currentNearestNode);
currentDistance = queue.LastDistance;

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net45;net451;net452;net46;net461;net462;net47</TargetFrameworks>
<LangVersion>latest</LangVersion>
<Version>0.1.2-alpha001</Version>
<Version>0.1.2-alpha002</Version>
<IsTestProject>False</IsTestProject>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<SignAssembly>true</SignAssembly>

View File

@@ -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)

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net45;net451;net452;net46;net461;net462;net47</TargetFrameworks>
<LangVersion>latest</LangVersion>
<Version>0.1.2-alpha001</Version>
<Version>0.1.2-alpha002</Version>
<IsTestProject>False</IsTestProject>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<SignAssembly>true</SignAssembly>

View File

@@ -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",

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net45;net451;net452;net46;net461;net462;net47</TargetFrameworks>
<LangVersion>latest</LangVersion>
<Version>0.1.2-alpha001</Version>
<Version>0.1.2-alpha002</Version>
<IsTestProject>False</IsTestProject>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<SignAssembly>true</SignAssembly>

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net45;net451;net452;net46;net461;net462;net47</TargetFrameworks>
<LangVersion>latest</LangVersion>
<Version>0.1.2-alpha001</Version>
<Version>0.1.2-alpha002</Version>
<IsTestProject>False</IsTestProject>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<SignAssembly>true</SignAssembly>

View File

@@ -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;

View File

@@ -333,33 +333,33 @@
// Find the orientation of the OBB, using the baseline angle
var firstLetter = letters[0];
var lastLetter = letters[letters.Count - 1];
var baseLineAngle = Math.Atan2(
lastLetter.EndBaseLine.Y - firstLetter.StartBaseLine.Y,
lastLetter.EndBaseLine.X - firstLetter.StartBaseLine.X) * 180 / Math.PI;
var bbox = obb;
var deltaAngle = Math.Abs(baseLineAngle - angleRad * 180 / Math.PI);
double deltaAngle = Math.Abs(baseLineAngle - obb.Rotation);
double deltaAngle1 = Math.Abs(baseLineAngle - obb1.Rotation);
if (deltaAngle1 < deltaAngle)
{
deltaAngle = deltaAngle1;
bbox = obb1;
obb = obb1;
}
double deltaAngle2 = Math.Abs(baseLineAngle - obb2.Rotation);
if (deltaAngle2 < deltaAngle)
{
deltaAngle = deltaAngle2;
bbox = obb2;
obb = obb2;
}
double deltaAngle3 = Math.Abs(baseLineAngle - obb3.Rotation);
if (deltaAngle3 < deltaAngle)
{
bbox = obb3;
obb = obb3;
}
return new Tuple<string, PdfRectangle>(builder.ToString(), bbox);
return new Tuple<string, PdfRectangle>(builder.ToString(), obb);
}
#endregion

View File

@@ -230,7 +230,7 @@
var slope = sumProduct / sumDiffSquaredX;
// Rotate the points to build the axis-aligned bounding box (AABB)
var angleRad = Math.Atan(slope);
var angleRad = Math.Atan(slope); // -π/2 ≤ θ ≤ π/2
var cos = Math.Cos(angleRad);
var sin = Math.Sin(angleRad);

View File

@@ -251,7 +251,7 @@
transformedPdfBounds.BottomRight,
transformedPdfBounds.Width,
fontSize,
font.Name.Data,
font.Details,
color,
pointSize,
textSequence);

View File

@@ -58,7 +58,7 @@
var passwords = new List<string>();
var clipPaths = options?.ClipPaths ?? true;
var clipPaths = options?.ClipPaths ?? false;
if (options?.Password != null)
{

View File

@@ -34,6 +34,8 @@
/// </summary>
CharacterIdentifierSystemInfo SystemInfo { get; }
FontDetails Details { get; }
TransformationMatrix FontMatrix { get; }
CidFontType CidFontType { get; }

View File

@@ -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);

View File

@@ -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();

View File

@@ -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);

View File

@@ -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;

View File

@@ -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,

View File

@@ -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)

View File

@@ -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>

View 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}";
}
}
}

View File

@@ -9,6 +9,8 @@
bool IsVertical { get; }
FontDetails Details { get; }
int ReadCharacterCode(IInputBytes bytes, out int codeLength);
bool TryGetUnicode(int characterCode, out string value);

View File

@@ -166,7 +166,7 @@
var counter = 0;
while (counter < size)
{
var firstCode = (NumericToken)widthArray.Data[counter++];
var firstCode = DirectObjectFinder.Get<NumericToken>(widthArray.Data[counter++], pdfScanner);
var next = widthArray.Data[counter++];
if (DirectObjectFinder.TryGet(next, pdfScanner, out ArrayToken array))
{
@@ -175,14 +175,14 @@
for (var i = 0; i < arraySize; i++)
{
var width = (NumericToken)array.Data[i];
var width = DirectObjectFinder.Get<NumericToken>(array.Data[i], pdfScanner);
widths[startRange + i] = width.Double;
}
}
else
{
var secondCode = (NumericToken)next;
var rangeWidth = (NumericToken)widthArray.Data[counter++];
var secondCode = DirectObjectFinder.Get<NumericToken>(next, pdfScanner);
var rangeWidth = DirectObjectFinder.Get<NumericToken>(widthArray.Data[counter++], pdfScanner);
var startRange = firstCode.Int;
var endRange = secondCode.Int;
var width = rangeWidth.Double;

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net45;net451;net452;net46;net461;net462;net47</TargetFrameworks>
<LangVersion>latest</LangVersion>
<Version>0.1.2-alpha001</Version>
<Version>0.1.2-alpha002</Version>
<IsTestProject>False</IsTestProject>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<SignAssembly>true</SignAssembly>

View File

@@ -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);

View File

@@ -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;
}
}
}

View File

@@ -12,7 +12,7 @@
<PackageTags>PDF;Reader;Document;Adobe;PDFBox;PdfPig;pdf-extract</PackageTags>
<RepositoryUrl>https://github.com/UglyToad/PdfPig</RepositoryUrl>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<Version>0.1.2-alpha001</Version>
<Version>0.1.2-alpha002</Version>
<AssemblyVersion>0.1.1.0</AssemblyVersion>
<FileVersion>0.1.1.0</FileVersion>
<PackageIconUrl>https://raw.githubusercontent.com/UglyToad/PdfPig/master/documentation/pdfpig.png</PackageIconUrl>