add support for quadpoints to annotations

highlight, link, strikeout, squiggly and underline annotation types may define a set of quadrilaterals using the quadpoints entry. this defines the regions to show/activate the annotation. the order of points in the quadpoints array does not match the specification so we provide a convenience class to access the point data rather than interpreting it as a rectangle: https://stackoverflow.com/questions/9855814/pdf-spec-vs-acrobat-creation-quadpoints.
This commit is contained in:
Eliot Jones
2020-01-05 16:23:07 +00:00
parent e064d39671
commit 00bd285262
5 changed files with 96 additions and 4 deletions
@@ -1,6 +1,7 @@
namespace UglyToad.PdfPig.Tests.Integration namespace UglyToad.PdfPig.Tests.Integration
{ {
using System.Linq; using System.Linq;
using Annotations;
using Xunit; using Xunit;
public class CatGeneticsTests public class CatGeneticsTests
@@ -31,6 +32,13 @@
var annotations = page.ExperimentalAccess.GetAnnotations().ToList(); var annotations = page.ExperimentalAccess.GetAnnotations().ToList();
Assert.NotEmpty(annotations); Assert.NotEmpty(annotations);
var highlights = annotations.Where(x => x.Type == AnnotationType.Highlight);
foreach (var highlight in highlights)
{
Assert.NotEmpty(highlight.QuadPoints);
}
} }
} }
} }
@@ -63,6 +63,7 @@
"UglyToad.PdfPig.Annotations.AnnotationBorder", "UglyToad.PdfPig.Annotations.AnnotationBorder",
"UglyToad.PdfPig.Annotations.AnnotationFlags", "UglyToad.PdfPig.Annotations.AnnotationFlags",
"UglyToad.PdfPig.Annotations.AnnotationType", "UglyToad.PdfPig.Annotations.AnnotationType",
"UglyToad.PdfPig.Annotations.QuadPointsQuadrilateral",
"UglyToad.PdfPig.Content.Catalog", "UglyToad.PdfPig.Content.Catalog",
"UglyToad.PdfPig.Content.CropBox", "UglyToad.PdfPig.Content.CropBox",
"UglyToad.PdfPig.Content.DocumentInformation", "UglyToad.PdfPig.Content.DocumentInformation",
+10 -1
View File
@@ -1,6 +1,7 @@
namespace UglyToad.PdfPig.Annotations namespace UglyToad.PdfPig.Annotations
{ {
using System; using System;
using System.Collections.Generic;
using Core; using Core;
using Tokens; using Tokens;
using Util.JetBrains.Annotations; using Util.JetBrains.Annotations;
@@ -54,11 +55,18 @@
/// </summary> /// </summary>
public AnnotationBorder Border { get; } public AnnotationBorder Border { get; }
/// <summary>
/// Rectangles defined using QuadPoints, for <see cref="AnnotationType.Link"/> these are the regions used to activate the link,
/// for text markup annotations these are the text regions to apply the markup to.
/// See <see cref="QuadPointsQuadrilateral.Points"/> for more information regarding the order of the points.
/// </summary>
public IReadOnlyList<QuadPointsQuadrilateral> QuadPoints { get; }
/// <summary> /// <summary>
/// Create a new <see cref="Annotation"/>. /// Create a new <see cref="Annotation"/>.
/// </summary> /// </summary>
public Annotation(DictionaryToken annotationDictionary, AnnotationType type, PdfRectangle rectangle, string content, string name, string modifiedDate, public Annotation(DictionaryToken annotationDictionary, AnnotationType type, PdfRectangle rectangle, string content, string name, string modifiedDate,
AnnotationFlags flags, AnnotationBorder border) AnnotationFlags flags, AnnotationBorder border, IReadOnlyList<QuadPointsQuadrilateral> quadPoints)
{ {
AnnotationDictionary = annotationDictionary ?? throw new ArgumentNullException(nameof(annotationDictionary)); AnnotationDictionary = annotationDictionary ?? throw new ArgumentNullException(nameof(annotationDictionary));
Type = type; Type = type;
@@ -68,6 +76,7 @@
ModifiedDate = modifiedDate; ModifiedDate = modifiedDate;
Flags = flags; Flags = flags;
Border = border; Border = border;
QuadPoints = quadPoints ?? EmptyArray<QuadPointsQuadrilateral>.Instance;
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -82,7 +82,36 @@
border = new AnnotationBorder(horizontal, vertical, width, dashes); border = new AnnotationBorder(horizontal, vertical, width, dashes);
} }
yield return new Annotation(annotationDictionary, annotationType, rectangle, contents, name, modifiedDate, flags, border); var quadPointRectangles = new List<QuadPointsQuadrilateral>();
if (annotationDictionary.TryGet(NameToken.Quadpoints, tokenScanner, out ArrayToken quadPointsArray))
{
var values = new List<decimal>();
for (var i = 0; i < quadPointsArray.Length; i++)
{
if (!(quadPointsArray[i] is NumericToken value))
{
continue;
}
values.Add(value.Data);
if (values.Count == 8)
{
quadPointRectangles.Add(new QuadPointsQuadrilateral(new[]
{
new PdfPoint(values[0], values[1]),
new PdfPoint(values[2], values[3]),
new PdfPoint(values[4], values[5]),
new PdfPoint(values[6], values[7])
}));
values.Clear();
}
}
}
yield return new Annotation(annotationDictionary, annotationType, rectangle, contents, name, modifiedDate, flags, border,
quadPointRectangles);
} }
} }
@@ -0,0 +1,45 @@
namespace UglyToad.PdfPig.Annotations
{
using System;
using System.Collections.Generic;
using Core;
/// <summary>
/// A QuadPoints quadrilateral is four points defining the region for an annotation to use.
/// An annotation may cover multiple quadrilaterals.
/// </summary>
public class QuadPointsQuadrilateral
{
/// <summary>
/// The 4 points defining this quadrilateral.
/// The PDF specification defines these as being in anti-clockwise order starting from the lower-left corner, however
/// Adobe's implementation doesn't obey the specification and points seem to go in the order: top-left, top-right,
/// bottom-left, bottom-right. See: https://stackoverflow.com/questions/9855814/pdf-spec-vs-acrobat-creation-quadpoints.
/// </summary>
public IReadOnlyList<PdfPoint> Points { get; }
/// <summary>
/// Create a new <see cref="QuadPointsQuadrilateral"/>.
/// </summary>
public QuadPointsQuadrilateral(IReadOnlyList<PdfPoint> points)
{
if (points == null)
{
throw new ArgumentNullException(nameof(points));
}
if (points.Count != 4)
{
throw new ArgumentException($"Quadpoints quadrilateral should only contain 4 points, instead got {points.Count} points.");
}
Points = points;
}
/// <inheritdoc />
public override string ToString()
{
return $"[ {Points[0]}, {Points[1]}, {Points[2]}, {Points[3]} ]";
}
}
}