mirror of
https://github.com/UglyToad/PdfPig.git
synced 2025-06-28 15:30:17 +08:00
Support adding outline bookmarks to existing pdf document (#552)
Support adding outline bookmarks to existing pdf document
This commit is contained in:
parent
a486114c8d
commit
a3a9d1a2b5
@ -208,6 +208,7 @@
|
|||||||
"UglyToad.PdfPig.Outline.BookmarkNode",
|
"UglyToad.PdfPig.Outline.BookmarkNode",
|
||||||
"UglyToad.PdfPig.Outline.DocumentBookmarkNode",
|
"UglyToad.PdfPig.Outline.DocumentBookmarkNode",
|
||||||
"UglyToad.PdfPig.Outline.ExternalBookmarkNode",
|
"UglyToad.PdfPig.Outline.ExternalBookmarkNode",
|
||||||
|
"UglyToad.PdfPig.Outline.UriBookmarkNode",
|
||||||
"UglyToad.PdfPig.Outline.Destinations.ExplicitDestination",
|
"UglyToad.PdfPig.Outline.Destinations.ExplicitDestination",
|
||||||
"UglyToad.PdfPig.Outline.Destinations.ExplicitDestinationCoordinates",
|
"UglyToad.PdfPig.Outline.Destinations.ExplicitDestinationCoordinates",
|
||||||
"UglyToad.PdfPig.Outline.Destinations.ExplicitDestinationType",
|
"UglyToad.PdfPig.Outline.Destinations.ExplicitDestinationType",
|
||||||
|
@ -13,6 +13,8 @@
|
|||||||
using Xunit;
|
using Xunit;
|
||||||
using System;
|
using System;
|
||||||
using UglyToad.PdfPig.Graphics.Operations.InlineImages;
|
using UglyToad.PdfPig.Graphics.Operations.InlineImages;
|
||||||
|
using UglyToad.PdfPig.Outline;
|
||||||
|
using UglyToad.PdfPig.Outline.Destinations;
|
||||||
|
|
||||||
public class PdfDocumentBuilderTests
|
public class PdfDocumentBuilderTests
|
||||||
{
|
{
|
||||||
@ -1179,11 +1181,81 @@
|
|||||||
var exampleCopiedDictionary = dictCopy.FirstOrDefault();
|
var exampleCopiedDictionary = dictCopy.FirstOrDefault();
|
||||||
|
|
||||||
Assert.NotNull(exampleCopiedDictionary);
|
Assert.NotNull(exampleCopiedDictionary);
|
||||||
Assert.True(exampleCopiedDictionary.Count>0);
|
Assert.True(exampleCopiedDictionary.Count > 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CanCreateDocumentWithOutline()
|
||||||
|
{
|
||||||
|
var builder = new PdfDocumentBuilder();
|
||||||
|
builder.Bookmarks = new Bookmarks(new BookmarkNode[]
|
||||||
|
{
|
||||||
|
new DocumentBookmarkNode(
|
||||||
|
"1", 0, new ExplicitDestination(1, ExplicitDestinationType.XyzCoordinates, ExplicitDestinationCoordinates.Empty),
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
new DocumentBookmarkNode("1.1", 0, new ExplicitDestination(2, ExplicitDestinationType.FitPage, ExplicitDestinationCoordinates.Empty), Array.Empty<BookmarkNode>()),
|
||||||
|
}),
|
||||||
|
new DocumentBookmarkNode(
|
||||||
|
"2", 0, new ExplicitDestination(3, ExplicitDestinationType.FitRectangle, ExplicitDestinationCoordinates.Empty),
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
new DocumentBookmarkNode("2.1", 0, new ExplicitDestination(4, ExplicitDestinationType.FitBoundingBox, ExplicitDestinationCoordinates.Empty), Array.Empty<BookmarkNode>()),
|
||||||
|
new DocumentBookmarkNode("2.2", 0, new ExplicitDestination(5, ExplicitDestinationType.FitBoundingBoxHorizontally, ExplicitDestinationCoordinates.Empty), Array.Empty<BookmarkNode>()),
|
||||||
|
new DocumentBookmarkNode("2.3", 0, new ExplicitDestination(6, ExplicitDestinationType.FitBoundingBoxVertically, ExplicitDestinationCoordinates.Empty), Array.Empty<BookmarkNode>()),
|
||||||
|
new DocumentBookmarkNode("2.4", 0, new ExplicitDestination(7, ExplicitDestinationType.FitHorizontally, ExplicitDestinationCoordinates.Empty), Array.Empty<BookmarkNode>()),
|
||||||
|
new DocumentBookmarkNode("2.5", 0, new ExplicitDestination(8, ExplicitDestinationType.FitVertically, ExplicitDestinationCoordinates.Empty), Array.Empty<BookmarkNode>()),
|
||||||
|
}),
|
||||||
|
new UriBookmarkNode("3", 0, "https://github.com", Array.Empty<BookmarkNode>()),
|
||||||
|
});
|
||||||
|
|
||||||
|
var font = builder.AddStandard14Font(Standard14Font.Helvetica);
|
||||||
|
|
||||||
|
foreach (var node in builder.Bookmarks.GetNodes())
|
||||||
|
{
|
||||||
|
builder.AddPage(PageSize.A4).AddText(node.Title, 12, new PdfPoint(25, 800), font);
|
||||||
|
}
|
||||||
|
|
||||||
|
var file = builder.Build();
|
||||||
|
WriteFile(nameof(CanCreateDocumentWithOutline), file);
|
||||||
|
using (var document = PdfDocument.Open(file))
|
||||||
|
{
|
||||||
|
Assert.True(document.TryGetBookmarks(out var bookmarks));
|
||||||
|
|
||||||
|
Assert.Equal(
|
||||||
|
new[] { "1", "1.1", "2", "2.1", "2.2", "2.3", "2.4", "2.5", "3" },
|
||||||
|
bookmarks.GetNodes().Select(node => node.Title));
|
||||||
|
|
||||||
|
Assert.Equal(
|
||||||
|
new[] { 0, 1, 0, 1, 1, 1, 1, 1, 0 },
|
||||||
|
bookmarks.GetNodes().Select(node => node.Level));
|
||||||
|
|
||||||
|
Assert.Equal(
|
||||||
|
new[] { false, true, false, true, true, true, true, true, true },
|
||||||
|
bookmarks.GetNodes().Select(node => node.IsLeaf));
|
||||||
|
|
||||||
|
Assert.Equal(
|
||||||
|
new[] { "https://github.com" },
|
||||||
|
bookmarks.GetNodes().OfType<UriBookmarkNode>().Select(node => node.Uri));
|
||||||
|
|
||||||
|
Assert.Equal(
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
ExplicitDestinationType.XyzCoordinates,
|
||||||
|
ExplicitDestinationType.FitPage,
|
||||||
|
ExplicitDestinationType.FitRectangle,
|
||||||
|
ExplicitDestinationType.FitBoundingBox,
|
||||||
|
ExplicitDestinationType.FitBoundingBoxHorizontally,
|
||||||
|
ExplicitDestinationType.FitBoundingBoxVertically,
|
||||||
|
ExplicitDestinationType.FitHorizontally,
|
||||||
|
ExplicitDestinationType.FitVertically,
|
||||||
|
},
|
||||||
|
bookmarks.GetNodes().OfType<DocumentBookmarkNode>().Select(node => node.Destination.Type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void WriteFile(string name, byte[] bytes, string extension = "pdf")
|
private static void WriteFile(string name, byte[] bytes, string extension = "pdf")
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -105,20 +105,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (nodeDictionary.TryGet(NameToken.A, pdfScanner, out DictionaryToken actionDictionary)
|
else if (nodeDictionary.TryGet(NameToken.A, pdfScanner, out DictionaryToken actionDictionary)
|
||||||
&& TryGetAction(actionDictionary, catalog, pdfScanner, namedDestinations, log, out var actionResult))
|
&& TryGetAction(actionDictionary, catalog, pdfScanner, namedDestinations, log, title, level, children, out var actionResult))
|
||||||
{
|
{
|
||||||
if (actionResult.isExternal)
|
bookmark = actionResult;
|
||||||
{
|
|
||||||
bookmark = new ExternalBookmarkNode(title, level, actionResult.externalFileName, children);
|
|
||||||
}
|
|
||||||
else if (actionResult.destination != null)
|
|
||||||
{
|
|
||||||
bookmark = new DocumentBookmarkNode(title, level, actionResult.destination, children);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -361,10 +350,9 @@
|
|||||||
|
|
||||||
private static bool TryGetAction(DictionaryToken actionDictionary, Catalog catalog, IPdfTokenScanner pdfScanner,
|
private static bool TryGetAction(DictionaryToken actionDictionary, Catalog catalog, IPdfTokenScanner pdfScanner,
|
||||||
IReadOnlyDictionary<string, ExplicitDestination> namedDestinations,
|
IReadOnlyDictionary<string, ExplicitDestination> namedDestinations,
|
||||||
ILog log,
|
ILog log, string title, int level, List<BookmarkNode> children, out BookmarkNode result)
|
||||||
out (bool isExternal, string externalFileName, ExplicitDestination destination) result)
|
|
||||||
{
|
{
|
||||||
result = (false, null, null);
|
result = null;
|
||||||
|
|
||||||
if (!actionDictionary.TryGet(NameToken.S, pdfScanner, out NameToken actionType))
|
if (!actionDictionary.TryGet(NameToken.S, pdfScanner, out NameToken actionType))
|
||||||
{
|
{
|
||||||
@ -376,7 +364,7 @@
|
|||||||
if (actionDictionary.TryGet(NameToken.D, pdfScanner, out ArrayToken destinationArray)
|
if (actionDictionary.TryGet(NameToken.D, pdfScanner, out ArrayToken destinationArray)
|
||||||
&& TryGetExplicitDestination(destinationArray, catalog, log, out var destination))
|
&& TryGetExplicitDestination(destinationArray, catalog, log, out var destination))
|
||||||
{
|
{
|
||||||
result = (false, null, destination);
|
result = new DocumentBookmarkNode(title, level, destination, children);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -384,7 +372,7 @@
|
|||||||
if (actionDictionary.TryGet(NameToken.D, pdfScanner, out IDataToken<string> destinationName)
|
if (actionDictionary.TryGet(NameToken.D, pdfScanner, out IDataToken<string> destinationName)
|
||||||
&& namedDestinations.TryGetValue(destinationName.Data, out destination))
|
&& namedDestinations.TryGetValue(destinationName.Data, out destination))
|
||||||
{
|
{
|
||||||
result = (false, null, destination);
|
result = new DocumentBookmarkNode(title, level, destination, children);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -393,11 +381,22 @@
|
|||||||
{
|
{
|
||||||
if (actionDictionary.TryGetOptionalStringDirect(NameToken.F, pdfScanner, out var filename))
|
if (actionDictionary.TryGetOptionalStringDirect(NameToken.F, pdfScanner, out var filename))
|
||||||
{
|
{
|
||||||
result = (true, filename, null);
|
result = new ExternalBookmarkNode(title, level, filename, children);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = (true, string.Empty, null);
|
result = new ExternalBookmarkNode(title, level, string.Empty, children);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (actionType.Equals(NameToken.Uri))
|
||||||
|
{
|
||||||
|
if (actionDictionary.TryGetOptionalStringDirect(NameToken.Uri, pdfScanner, out var uri))
|
||||||
|
{
|
||||||
|
result = new UriBookmarkNode(title, level, uri, children);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = new UriBookmarkNode(title, level, string.Empty, children);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
33
src/UglyToad.PdfPig/Outline/UriBookmarkNode.cs
Normal file
33
src/UglyToad.PdfPig/Outline/UriBookmarkNode.cs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
namespace UglyToad.PdfPig.Outline
|
||||||
|
{
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
/// <summary>
|
||||||
|
/// A node in the <see cref="Bookmarks" /> of a PDF document which corresponds
|
||||||
|
/// to a uniform resource identifier on the Internet.
|
||||||
|
/// </summary>
|
||||||
|
public class UriBookmarkNode : BookmarkNode
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The uniform resource identifier to resolve.
|
||||||
|
/// </summary>
|
||||||
|
public string Uri { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new <see cref="ExternalBookmarkNode" />.
|
||||||
|
/// </summary>
|
||||||
|
public UriBookmarkNode(string title, int level, string uri, IReadOnlyList<BookmarkNode> children) : base(title, level, children)
|
||||||
|
{
|
||||||
|
Uri = uri ?? throw new ArgumentNullException(nameof(uri));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"URI '{Uri}', {Level}, {Title}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -11,6 +11,8 @@ namespace UglyToad.PdfPig.Writer
|
|||||||
using PdfPig.Fonts.TrueType;
|
using PdfPig.Fonts.TrueType;
|
||||||
using PdfPig.Fonts.Standard14Fonts;
|
using PdfPig.Fonts.Standard14Fonts;
|
||||||
using PdfPig.Fonts.TrueType.Parser;
|
using PdfPig.Fonts.TrueType.Parser;
|
||||||
|
using PdfPig.Outline;
|
||||||
|
using PdfPig.Outline.Destinations;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using Tokenization.Scanner;
|
using Tokenization.Scanner;
|
||||||
using Tokens;
|
using Tokens;
|
||||||
@ -52,6 +54,11 @@ namespace UglyToad.PdfPig.Writer
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public DocumentInformationBuilder DocumentInformation { get; set; } = new DocumentInformationBuilder();
|
public DocumentInformationBuilder DocumentInformation { get; set; } = new DocumentInformationBuilder();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The bookmark nodes to include in the document outline dictionary.
|
||||||
|
/// </summary>
|
||||||
|
public Bookmarks Bookmarks { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The current page builders in the document and the corresponding 1 indexed page numbers. Use <see cref="AddPage(double,double)"/>
|
/// The current page builders in the document and the corresponding 1 indexed page numbers. Use <see cref="AddPage(double,double)"/>
|
||||||
/// or <see cref="AddPage(PageSize,bool)"/> to add a new page.
|
/// or <see cref="AddPage(PageSize,bool)"/> to add a new page.
|
||||||
@ -103,7 +110,7 @@ namespace UglyToad.PdfPig.Writer
|
|||||||
}
|
}
|
||||||
context.InitializePdf(version);
|
context.InitializePdf(version);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether the bytes of the TrueType font file provided can be used in a PDF document.
|
/// Determines whether the bytes of the TrueType font file provided can be used in a PDF document.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -408,7 +415,7 @@ namespace UglyToad.PdfPig.Writer
|
|||||||
}
|
}
|
||||||
val = tk.Data;
|
val = tk.Data;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(val is ArrayToken arr))
|
if (!(val is ArrayToken arr))
|
||||||
{
|
{
|
||||||
// should be array... ignore and remove bad dict
|
// should be array... ignore and remove bad dict
|
||||||
@ -547,6 +554,7 @@ namespace UglyToad.PdfPig.Writer
|
|||||||
}
|
}
|
||||||
|
|
||||||
int leafNum = 0;
|
int leafNum = 0;
|
||||||
|
var pageReferences = new Dictionary<int, IndirectReferenceToken>(pages.Count);
|
||||||
|
|
||||||
foreach (var page in pages)
|
foreach (var page in pages)
|
||||||
{
|
{
|
||||||
@ -589,9 +597,10 @@ namespace UglyToad.PdfPig.Writer
|
|||||||
}
|
}
|
||||||
pageDictionary[NameToken.Contents] = new ArrayToken(streams);
|
pageDictionary[NameToken.Contents] = new ArrayToken(streams);
|
||||||
}
|
}
|
||||||
context.AttemptDeduplication = prev;;
|
context.AttemptDeduplication = prev;
|
||||||
|
|
||||||
leafChildren[leafNum].Add(context.WriteToken(new DictionaryToken(pageDictionary)));
|
pageReferences[page.Key] = context.WriteToken(new DictionaryToken(pageDictionary));
|
||||||
|
leafChildren[leafNum].Add(pageReferences[page.Key]);
|
||||||
|
|
||||||
if (leafChildren[leafNum].Count >= desiredLeafSize)
|
if (leafChildren[leafNum].Count >= desiredLeafSize)
|
||||||
{
|
{
|
||||||
@ -624,6 +633,20 @@ namespace UglyToad.PdfPig.Writer
|
|||||||
catalogDictionary[NameToken.Pages] = rootPageInfo.Ref;
|
catalogDictionary[NameToken.Pages] = rootPageInfo.Ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Bookmarks != null && Bookmarks.Roots.Count > 0)
|
||||||
|
{
|
||||||
|
var bookmarks = CreateBookmarkTree(Bookmarks.Roots, pageReferences, null);
|
||||||
|
var outline = new Dictionary<NameToken, IToken>
|
||||||
|
{
|
||||||
|
{NameToken.Type, NameToken.Outlines},
|
||||||
|
{NameToken.Count, new NumericToken(Bookmarks.Roots.Count)},
|
||||||
|
{NameToken.First, bookmarks[0]},
|
||||||
|
{NameToken.Last, bookmarks[bookmarks.Length - 1]},
|
||||||
|
};
|
||||||
|
|
||||||
|
catalogDictionary[NameToken.Outlines] = context.WriteToken(new DictionaryToken(outline));
|
||||||
|
}
|
||||||
|
|
||||||
if (ArchiveStandard != PdfAStandard.None)
|
if (ArchiveStandard != PdfAStandard.None)
|
||||||
{
|
{
|
||||||
Func<IToken, IndirectReferenceToken> writerFunc = x => context.WriteToken(x);
|
Func<IToken, IndirectReferenceToken> writerFunc = x => context.WriteToken(x);
|
||||||
@ -750,6 +773,138 @@ namespace UglyToad.PdfPig.Writer
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IndirectReferenceToken[] CreateBookmarkTree(IReadOnlyList<BookmarkNode> nodes, Dictionary<int, IndirectReferenceToken> pageReferences, IndirectReferenceToken parent)
|
||||||
|
{
|
||||||
|
var childObjectNumbers = new IndirectReferenceToken[nodes.Count];
|
||||||
|
for (var i = 0; i < nodes.Count; i++)
|
||||||
|
{
|
||||||
|
childObjectNumbers[i] = context.ReserveObjectNumber();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < nodes.Count; i++)
|
||||||
|
{
|
||||||
|
var node = nodes[i];
|
||||||
|
var objectNumber = childObjectNumbers[i];
|
||||||
|
var data = new Dictionary<NameToken, IToken>
|
||||||
|
{
|
||||||
|
{NameToken.Title, new StringToken(node.Title)},
|
||||||
|
{NameToken.Count, new NumericToken(node.Children.Count)}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (parent != null)
|
||||||
|
{
|
||||||
|
data[NameToken.Parent] = parent;
|
||||||
|
}
|
||||||
|
if (i > 0)
|
||||||
|
{
|
||||||
|
data[NameToken.Prev] = childObjectNumbers[i - 1];
|
||||||
|
}
|
||||||
|
if (i < childObjectNumbers.Length - 1)
|
||||||
|
{
|
||||||
|
data[NameToken.Next] = childObjectNumbers[i + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.Children.Count > 0)
|
||||||
|
{
|
||||||
|
var children = CreateBookmarkTree(node.Children, pageReferences, objectNumber);
|
||||||
|
data[NameToken.First] = children[0];
|
||||||
|
data[NameToken.Last] = children[children.Length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (node)
|
||||||
|
{
|
||||||
|
case DocumentBookmarkNode documentBookmarkNode:
|
||||||
|
if (!pageReferences.TryGetValue(documentBookmarkNode.PageNumber, out var pageReference))
|
||||||
|
{
|
||||||
|
throw new KeyNotFoundException($"Page {documentBookmarkNode.PageNumber} was not found in the source document.");
|
||||||
|
}
|
||||||
|
|
||||||
|
data[NameToken.Dest] = CreateExplicitDestinationToken(documentBookmarkNode.Destination, pageReference);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case UriBookmarkNode uriBookmarkNode:
|
||||||
|
data[NameToken.A] = new DictionaryToken(new Dictionary<NameToken, IToken>()
|
||||||
|
{
|
||||||
|
[NameToken.S] = NameToken.Uri,
|
||||||
|
[NameToken.Uri] = new StringToken(uriBookmarkNode.Uri),
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new NotSupportedException($"{node.GetType().Name} is not a supported bookmark node type.");
|
||||||
|
}
|
||||||
|
|
||||||
|
context.WriteToken(new DictionaryToken(data), objectNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
return childObjectNumbers;
|
||||||
|
|
||||||
|
static ArrayToken CreateExplicitDestinationToken(ExplicitDestination destination, IndirectReferenceToken page)
|
||||||
|
{
|
||||||
|
return destination.Type switch
|
||||||
|
{
|
||||||
|
ExplicitDestinationType.XyzCoordinates => new ArrayToken(new IToken[]
|
||||||
|
{
|
||||||
|
page,
|
||||||
|
NameToken.XYZ,
|
||||||
|
new NumericToken(destination.Coordinates.Left ?? 0),
|
||||||
|
new NumericToken(destination.Coordinates.Top ?? 0),
|
||||||
|
}),
|
||||||
|
|
||||||
|
ExplicitDestinationType.FitPage => new ArrayToken(new IToken[]
|
||||||
|
{
|
||||||
|
page,
|
||||||
|
NameToken.Fit,
|
||||||
|
}),
|
||||||
|
|
||||||
|
ExplicitDestinationType.FitHorizontally => new ArrayToken(new IToken[]
|
||||||
|
{
|
||||||
|
page,
|
||||||
|
NameToken.FitH,
|
||||||
|
new NumericToken(destination.Coordinates.Top ?? 0),
|
||||||
|
}),
|
||||||
|
|
||||||
|
ExplicitDestinationType.FitVertically => new ArrayToken(new IToken[]
|
||||||
|
{
|
||||||
|
page,
|
||||||
|
NameToken.FitV,
|
||||||
|
new NumericToken(destination.Coordinates.Left ?? 0),
|
||||||
|
}),
|
||||||
|
|
||||||
|
ExplicitDestinationType.FitRectangle => new ArrayToken(new IToken[]
|
||||||
|
{
|
||||||
|
page,
|
||||||
|
NameToken.FitR,
|
||||||
|
new NumericToken(destination.Coordinates.Left ?? 0),
|
||||||
|
new NumericToken(destination.Coordinates.Top ?? 0),
|
||||||
|
new NumericToken(destination.Coordinates.Right ?? 0),
|
||||||
|
new NumericToken(destination.Coordinates.Bottom ?? 0),
|
||||||
|
}),
|
||||||
|
|
||||||
|
ExplicitDestinationType.FitBoundingBox => new ArrayToken(new IToken[]
|
||||||
|
{
|
||||||
|
page,
|
||||||
|
NameToken.FitB,
|
||||||
|
}),
|
||||||
|
|
||||||
|
ExplicitDestinationType.FitBoundingBoxHorizontally => new ArrayToken(new IToken[]
|
||||||
|
{
|
||||||
|
page,
|
||||||
|
NameToken.FitBH,
|
||||||
|
new NumericToken(destination.Coordinates.Left ?? 0),
|
||||||
|
}),
|
||||||
|
|
||||||
|
ExplicitDestinationType.FitBoundingBoxVertically => new ArrayToken(new IToken[]
|
||||||
|
{
|
||||||
|
page,
|
||||||
|
NameToken.FitBV,
|
||||||
|
new NumericToken(destination.Coordinates.Left ?? 0),
|
||||||
|
}),
|
||||||
|
|
||||||
|
_ => throw new NotSupportedException($"{destination.Type} is not a supported bookmark destination type."),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal class FontStored
|
internal class FontStored
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user