2018-12-21 02:18:32 +08:00
|
|
|
|
namespace UglyToad.PdfPig.Annotations
|
|
|
|
|
{
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
2018-12-22 23:54:32 +08:00
|
|
|
|
using System.Linq;
|
2020-01-05 06:39:13 +08:00
|
|
|
|
using Core;
|
2018-12-21 02:18:32 +08:00
|
|
|
|
using Parser.Parts;
|
|
|
|
|
using Tokenization.Scanner;
|
|
|
|
|
using Tokens;
|
|
|
|
|
using Util;
|
|
|
|
|
|
|
|
|
|
internal class AnnotationProvider
|
|
|
|
|
{
|
|
|
|
|
private readonly IPdfTokenScanner tokenScanner;
|
|
|
|
|
private readonly DictionaryToken pageDictionary;
|
|
|
|
|
private readonly bool isLenientParsing;
|
|
|
|
|
|
|
|
|
|
public AnnotationProvider(IPdfTokenScanner tokenScanner, DictionaryToken pageDictionary, bool isLenientParsing)
|
|
|
|
|
{
|
|
|
|
|
this.tokenScanner = tokenScanner ?? throw new ArgumentNullException(nameof(tokenScanner));
|
|
|
|
|
this.pageDictionary = pageDictionary ?? throw new ArgumentNullException(nameof(pageDictionary));
|
|
|
|
|
this.isLenientParsing = isLenientParsing;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public IEnumerable<Annotation> GetAnnotations()
|
|
|
|
|
{
|
|
|
|
|
if (!pageDictionary.TryGet(NameToken.Annots, out IToken annotationsToken)
|
|
|
|
|
|| !DirectObjectFinder.TryGet(annotationsToken, tokenScanner, out ArrayToken annotationsArray))
|
|
|
|
|
{
|
|
|
|
|
yield break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach (var token in annotationsArray.Data)
|
|
|
|
|
{
|
|
|
|
|
if (!DirectObjectFinder.TryGet(token, tokenScanner, out DictionaryToken annotationDictionary))
|
|
|
|
|
{
|
|
|
|
|
if (isLenientParsing)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
throw new PdfDocumentFormatException($"The annotations dictionary contained an annotation which wasn't a dictionary: {token}.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!isLenientParsing && annotationDictionary.TryGet(NameToken.Type, out NameToken dictionaryType))
|
|
|
|
|
{
|
|
|
|
|
if (dictionaryType != NameToken.Annot)
|
|
|
|
|
{
|
|
|
|
|
throw new PdfDocumentFormatException($"The annotations dictionary contained a non-annotation type dictionary: {annotationDictionary}.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var type = annotationDictionary.Get<NameToken>(NameToken.Subtype, tokenScanner);
|
|
|
|
|
|
|
|
|
|
var annotationType = type.ToAnnotationType();
|
|
|
|
|
var rectangle = annotationDictionary.Get<ArrayToken>(NameToken.Rect, tokenScanner).ToRectangle();
|
|
|
|
|
|
2018-12-22 23:54:32 +08:00
|
|
|
|
var contents = GetNamedString(NameToken.Contents, annotationDictionary);
|
|
|
|
|
var name = GetNamedString(NameToken.Nm, annotationDictionary);
|
|
|
|
|
var modifiedDate = GetNamedString(NameToken.M, annotationDictionary);
|
|
|
|
|
|
|
|
|
|
var flags = (AnnotationFlags) 0;
|
|
|
|
|
if (annotationDictionary.TryGet(NameToken.F, out var flagsToken) && DirectObjectFinder.TryGet(flagsToken, tokenScanner, out NumericToken flagsNumericToken))
|
2018-12-21 02:18:32 +08:00
|
|
|
|
{
|
2018-12-22 23:54:32 +08:00
|
|
|
|
flags = (AnnotationFlags) flagsNumericToken.Int;
|
2018-12-21 02:18:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-12-22 23:54:32 +08:00
|
|
|
|
var border = AnnotationBorder.Default;
|
|
|
|
|
if (annotationDictionary.TryGet(NameToken.Border, out var borderToken) && DirectObjectFinder.TryGet(borderToken, tokenScanner, out ArrayToken borderArray)
|
|
|
|
|
&& borderArray.Length >= 3)
|
|
|
|
|
{
|
|
|
|
|
var horizontal = borderArray.GetNumeric(0).Data;
|
|
|
|
|
var vertical = borderArray.GetNumeric(1).Data;
|
|
|
|
|
var width = borderArray.GetNumeric(2).Data;
|
|
|
|
|
var dashes = default(IReadOnlyList<decimal>);
|
2018-12-21 02:18:32 +08:00
|
|
|
|
|
2018-12-22 23:54:32 +08:00
|
|
|
|
if (borderArray.Length == 4 && borderArray.Data[4] is ArrayToken dashArray)
|
|
|
|
|
{
|
|
|
|
|
dashes = dashArray.Data.OfType<NumericToken>().Select(x => x.Data).ToList();
|
|
|
|
|
}
|
2018-12-21 02:18:32 +08:00
|
|
|
|
|
2018-12-22 23:54:32 +08:00
|
|
|
|
border = new AnnotationBorder(horizontal, vertical, width, dashes);
|
|
|
|
|
}
|
2018-12-21 02:18:32 +08:00
|
|
|
|
|
2018-12-22 23:54:32 +08:00
|
|
|
|
yield return new Annotation(annotationDictionary, annotationType, rectangle, contents, name, modifiedDate, flags, border);
|
|
|
|
|
}
|
2018-12-21 02:18:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-12-22 23:54:32 +08:00
|
|
|
|
private string GetNamedString(NameToken name, DictionaryToken dictionary)
|
2018-12-21 02:18:32 +08:00
|
|
|
|
{
|
2018-12-22 23:54:32 +08:00
|
|
|
|
string content = null;
|
|
|
|
|
if (dictionary.TryGet(name, out var contentToken))
|
2018-12-21 02:18:32 +08:00
|
|
|
|
{
|
2018-12-22 23:54:32 +08:00
|
|
|
|
if (contentToken is StringToken contentString)
|
|
|
|
|
{
|
|
|
|
|
content = contentString.Data;
|
|
|
|
|
}
|
|
|
|
|
else if (contentToken is HexToken contentHex)
|
|
|
|
|
{
|
|
|
|
|
content = contentHex.Data;
|
|
|
|
|
}
|
|
|
|
|
else if (DirectObjectFinder.TryGet(contentToken, tokenScanner, out StringToken indirectContentString))
|
|
|
|
|
{
|
|
|
|
|
content = indirectContentString.Data;
|
|
|
|
|
}
|
2018-12-21 02:18:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-12-22 23:54:32 +08:00
|
|
|
|
return content;
|
2018-12-21 02:18:32 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-12-22 23:54:32 +08:00
|
|
|
|
}
|