support looking up in reply to value for annotation #362

This commit is contained in:
Eliot Jones
2023-05-27 13:40:01 +01:00
parent fba1cbc13c
commit 211b27062d
5 changed files with 97 additions and 8 deletions

View File

@@ -0,0 +1,43 @@
namespace UglyToad.PdfPig.Tests.Integration;
using Annotations;
using System.Linq;
using Xunit;
public class AnnotationReplyToTests
{
private static string GetFilename()
{
return IntegrationHelpers.GetDocumentPath("annotation-comments.pdf");
}
[Fact]
public void HasCorrectNumberOfAnnotations()
{
using var document = PdfDocument.Open(GetFilename());
var page = document.GetPage(1);
var annotations = page.ExperimentalAccess.GetAnnotations().ToList();
Assert.Equal(4, annotations.Count);
Assert.Equal(AnnotationType.Text, annotations[0].Type);
Assert.Equal(AnnotationType.Popup, annotations[1].Type);
Assert.Equal(AnnotationType.Text, annotations[2].Type);
Assert.Equal(AnnotationType.Popup, annotations[3].Type);
}
[Fact]
public void SecondTextReplyToFirst()
{
using var document = PdfDocument.Open(GetFilename());
var page = document.GetPage(1);
var annotations = page.ExperimentalAccess.GetAnnotations().ToList();
Assert.Equal(annotations[0], annotations[2].InReplyTo);
}
}

View File

@@ -290,6 +290,7 @@
public static readonly NameToken Inklist = new NameToken("InkList"); public static readonly NameToken Inklist = new NameToken("InkList");
public static readonly NameToken Intent = new NameToken("Intent"); public static readonly NameToken Intent = new NameToken("Intent");
public static readonly NameToken Interpolate = new NameToken("Interpolate"); public static readonly NameToken Interpolate = new NameToken("Interpolate");
public static readonly NameToken Irt = new NameToken("IRT");
public static readonly NameToken It = new NameToken("IT"); public static readonly NameToken It = new NameToken("IT");
public static readonly NameToken ItalicAngle = new NameToken("ItalicAngle"); public static readonly NameToken ItalicAngle = new NameToken("ItalicAngle");
// J // J

View File

@@ -88,15 +88,30 @@
/// </summary> /// </summary>
public bool HasDownAppearance => downAppearanceStream != null; public bool HasDownAppearance => downAppearanceStream != null;
/// <summary>
/// The <see cref="Annotation"/> this annotation was in reply to. Can be <see langword="null" />
/// </summary>
public Annotation InReplyTo { 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, public Annotation(
string content, string name, string modifiedDate, DictionaryToken annotationDictionary,
AnnotationFlags flags, AnnotationBorder border, IReadOnlyList<QuadPointsQuadrilateral> quadPoints, AnnotationType type,
PdfRectangle rectangle,
string content,
string name,
string modifiedDate,
AnnotationFlags flags,
AnnotationBorder border,
IReadOnlyList<QuadPointsQuadrilateral> quadPoints,
PdfAction action, PdfAction action,
AppearanceStream normalAppearanceStream, AppearanceStream rollOverAppearanceStream, AppearanceStream normalAppearanceStream,
AppearanceStream downAppearanceStream, string appearanceState) AppearanceStream rollOverAppearanceStream,
AppearanceStream downAppearanceStream,
string appearanceState,
Annotation inReplyTo)
{ {
AnnotationDictionary = annotationDictionary ?? throw new ArgumentNullException(nameof(annotationDictionary)); AnnotationDictionary = annotationDictionary ?? throw new ArgumentNullException(nameof(annotationDictionary));
Type = type; Type = type;
@@ -112,6 +127,7 @@
this.rollOverAppearanceStream = rollOverAppearanceStream; this.rollOverAppearanceStream = rollOverAppearanceStream;
this.downAppearanceStream = downAppearanceStream; this.downAppearanceStream = downAppearanceStream;
this.appearanceState = appearanceState; this.appearanceState = appearanceState;
InReplyTo = inReplyTo;
} }
/// <inheritdoc /> /// <inheritdoc />

View File

@@ -33,6 +33,8 @@
public IEnumerable<Annotation> GetAnnotations() public IEnumerable<Annotation> GetAnnotations()
{ {
var lookupAnnotations = new Dictionary<IndirectReference, Annotation>();
if (!pageDictionary.TryGet(NameToken.Annots, tokenScanner, out ArrayToken annotationsArray)) if (!pageDictionary.TryGet(NameToken.Annots, tokenScanner, out ArrayToken annotationsArray))
{ {
yield break; yield break;
@@ -45,6 +47,13 @@
continue; continue;
} }
Annotation replyTo = null;
if (annotationDictionary.TryGet(NameToken.Irt, out IndirectReferenceToken referencedAnnotation)
&& lookupAnnotations.TryGetValue(referencedAnnotation.Data, out var linkedAnnotation))
{
replyTo = linkedAnnotation;
}
var type = annotationDictionary.Get<NameToken>(NameToken.Subtype, tokenScanner); var type = annotationDictionary.Get<NameToken>(NameToken.Subtype, tokenScanner);
var annotationType = type.ToAnnotationType(); var annotationType = type.ToAnnotationType();
var action = GetAction(annotationDictionary); var action = GetAction(annotationDictionary);
@@ -136,9 +145,29 @@
appearanceState = appearanceStateToken.Data; appearanceState = appearanceStateToken.Data;
} }
yield return new Annotation(annotationDictionary, annotationType, rectangle, var annotation = new Annotation(
contents, name, modifiedDate, flags, border, quadPointRectangles, action, annotationDictionary,
normalAppearanceStream, rollOverAppearanceStream, downAppearanceStream, appearanceState); annotationType,
rectangle,
contents,
name,
modifiedDate,
flags,
border,
quadPointRectangles,
action,
normalAppearanceStream,
rollOverAppearanceStream,
downAppearanceStream,
appearanceState,
replyTo);
if (token is IndirectReferenceToken indirectReference)
{
lookupAnnotations[indirectReference.Data] = annotation;
}
yield return annotation;
} }
} }