Skip to content

Commit

Permalink
feat: iTextSharp -> PdfPig (#8664)
Browse files Browse the repository at this point in the history
  • Loading branch information
yufeih authored Apr 24, 2023
1 parent 70e1be7 commit b028406
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 66 deletions.
2 changes: 1 addition & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
<PackageVersion Include="HtmlAgilityPack" Version="1.11.46" />
<PackageVersion Include="ICSharpCode.Decompiler" Version="8.0.0.7246-preview3" />
<PackageVersion Include="IgnoresAccessChecksToGenerator" Version="0.6.0" />
<PackageVersion Include="iTextSharp" Version="5.5.13.3" />
<PackageVersion Include="Jint" Version="3.0.0-beta-2049" />
<PackageVersion Include="JsonSchema.Net" Version="4.0.3" />
<PackageVersion Include="Markdig" Version="0.31.0" />
Expand All @@ -24,6 +23,7 @@
<PackageVersion Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="4.5.0" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="1.1.1" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="PdfPig" Version="0.1.8-alpha-20230423-3898f" />
<PackageVersion Include="Spectre.Console.Cli" Version="0.46.0" />
<PackageVersion Include="Stubble.Core" Version="1.10.8" />
<PackageVersion Include="System.Collections.Immutable" Version="6.0.0" />
Expand Down
2 changes: 1 addition & 1 deletion samples/seed/pdf/toc.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
- name: Articles
href: ../articles/toc.md
href: ../articles/toc.yml
- name: API Documentation
href: ../obj/api/toc.yml
- name: REST API
Expand Down
130 changes: 67 additions & 63 deletions src/Microsoft.DocAsCode.HtmlToPdf/HtmlToPdfConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@
using System.Diagnostics;
using System.Web;

using iTextSharp.text.pdf;

using Microsoft.DocAsCode.Common;
using Microsoft.DocAsCode.Plugins;

using UglyToad.PdfPig;
using UglyToad.PdfPig.Actions;
using UglyToad.PdfPig.Outline;
using UglyToad.PdfPig.Outline.Destinations;
using UglyToad.PdfPig.Writer;

namespace Microsoft.DocAsCode.HtmlToPdf;

public class HtmlToPdfConverter
Expand Down Expand Up @@ -58,7 +62,7 @@ public IDictionary<string, PartialPdfModel> GetPartialPdfModels(IList<string> ht
new ParallelOptions { MaxDegreeOfParallelism = _htmlToPdfOptions.MaxDegreeOfParallelism },
htmlFilePath =>
{
var numberOfPages = Convert($"{WrapQuoteToPath(htmlFilePath)} -", reader => reader.NumberOfPages);
var numberOfPages = Convert($"{WrapQuoteToPath(htmlFilePath)} -");

PartialPdfModel pdfModel = new()
{
Expand Down Expand Up @@ -126,7 +130,7 @@ private void ConvertToStreamCore(string arguments, Stream stream)
Arguments = _htmlToPdfOptions + (_htmlToPdfOptions.IsReadArgsFromStdin ? string.Empty : (" " + arguments)),
}
};
using(new LoggerPhaseScope(Constants.PdfCommandName))
using (new LoggerPhaseScope(Constants.PdfCommandName))
{
Logger.LogVerbose($"Executing {process.StartInfo.FileName} {process.StartInfo.Arguments} ({arguments})");
process.Start();
Expand Down Expand Up @@ -161,77 +165,58 @@ private void ConvertToStream(string arguments, Stream stream)
}
}

private void CreateOutlines(Dictionary<string, object> rootOutline, IList<HtmlModel> htmlModels, IDictionary<string, PartialPdfModel> pdfPages)
private BookmarkNode[] CreateOutlines(IList<HtmlModel> htmlModels, IDictionary<string, PartialPdfModel> pdfPages)
{
if (htmlModels?.Count > 0)
if (htmlModels is null || htmlModels.Count is 0)
return Array.Empty<BookmarkNode>();

return htmlModels.Select<HtmlModel, BookmarkNode>(htmlModel =>
{
foreach (var htmlModel in htmlModels)
if (!string.IsNullOrEmpty(htmlModel.ExternalLink))
{
var outline = new Dictionary<string, object>
{
{ "Title", htmlModel.Title },
{ OutLineKidsName, new List<Dictionary<string, object>>() }
};

if (!string.IsNullOrEmpty(htmlModel.ExternalLink))
{
outline.Add("Action", "URI");
outline.Add("URI", htmlModel.ExternalLink);
}
else
{
int pageNumber = 0;
return new UriBookmarkNode(htmlModel.Title, 0, htmlModel.ExternalLink, CreateOutlines(htmlModel.Children, pdfPages));
}

if (!string.IsNullOrEmpty(htmlModel.HtmlFilePath))
{
string filePath = GetFilePath(htmlModel.HtmlFilePath);
int pageNumber = 0;

if (pdfPages.ContainsKey(filePath))
{
PartialPdfModel pdfModel = pdfPages[filePath];
if (!string.IsNullOrEmpty(htmlModel.HtmlFilePath))
{
string filePath = GetFilePath(htmlModel.HtmlFilePath);

if (!pdfModel.PageNumber.HasValue)
{
pdfModel.PageNumber = _currentNumberOfPages;
_currentNumberOfPages += pdfModel.NumberOfPages;
}
if (pdfPages.ContainsKey(filePath))
{
PartialPdfModel pdfModel = pdfPages[filePath];

pageNumber = pdfModel.PageNumber.Value;
}
}
else
if (!pdfModel.PageNumber.HasValue)
{
// this is a parent node for the next topic
pageNumber = _currentNumberOfPages;
pdfModel.PageNumber = _currentNumberOfPages;
_currentNumberOfPages += pdfModel.NumberOfPages;
}

outline.Add("Action", "GoTo");

// please go to http://api.itextpdf.com/itext/com/itextpdf/text/pdf/PdfDestination.html to find the detail.
outline.Add("Page", $"{pageNumber} FitH");
pageNumber = pdfModel.PageNumber.Value;
}

((List<Dictionary<string, object>>)rootOutline[OutLineKidsName]).Add(outline);
CreateOutlines(outline, htmlModel.Children, pdfPages);
}
}
else
{
// this is a parent node for the next topic
pageNumber = _currentNumberOfPages;
}

return new DocumentBookmarkNode(
htmlModel.Title, 0,
new(pageNumber, ExplicitDestinationType.FitHorizontally, ExplicitDestinationCoordinates.Empty),
CreateOutlines(htmlModel.Children, pdfPages));
}).ToArray();
}

private List<Dictionary<string, object>> ConvertOutlines()
private BookmarkNode[] ConvertOutlines()
{
var pdfFileNumberOfPages = GetPartialPdfModels(new List<string>(_htmlFilePaths));
_currentNumberOfPages = 1;

var rootOutline = new Dictionary<string, object>
{
{ OutLineKidsName, new List<Dictionary<string, object>>() }
};

CreateOutlines(rootOutline, _htmlModels, pdfFileNumberOfPages);
return (List<Dictionary<string, object>>)rootOutline[OutLineKidsName];
return CreateOutlines(_htmlModels, pdfFileNumberOfPages);
}

private IList<Dictionary<string, object>> GetOutlines()
private BookmarkNode[] GetOutlines()
{
switch (_htmlToPdfOptions.OutlineOption)
{
Expand Down Expand Up @@ -275,25 +260,44 @@ private void SaveCore(Stream stream)
{
if (_htmlFilePaths.Count > 0)
{
var outlines = GetOutlines();
using var pdfStream = new MemoryStream();
ConvertToStream($"{string.Join(" ", _htmlFilePaths.Select(WrapQuoteToPath))} -", pdfStream);
pdfStream.Position = 0;

using var pdfReader = new PdfReader(pdfStream);
using var pdfStamper = new PdfStamper(pdfReader, stream);
pdfStamper.Outlines = outlines;
WriteOutlines(pdfStream, stream);
}
}

private T Convert<T>(string arguments, Func<PdfReader, T> readerFunc)
private int Convert(string arguments)
{
using var pdfStream = new MemoryStream();
ConvertToStream(arguments, pdfStream);
pdfStream.Position = 0;

using var pdfReader = new PdfReader(pdfStream);
return readerFunc(pdfReader);
using var document = PdfDocument.Open(pdfStream);
return document.NumberOfPages;
}

private void WriteOutlines(MemoryStream input, Stream output)
{
using var document = PdfDocument.Open(input);
using var builder = new PdfDocumentBuilder(output);

for (var i = 1; i <= document.NumberOfPages; i++)
{
builder.AddPage(document, i, CopyLink);
}

builder.Bookmarks = new(GetOutlines());

PdfAction CopyLink(PdfAction action)
{
return action switch
{
GoToAction link => new GoToAction(new(link.Destination.PageNumber, link.Destination.Type, link.Destination.Coordinates)),
_ => action,
};
}
}

#endregion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="iTextSharp" />
<PackageReference Include="PdfPig" />
</ItemGroup>

<ItemGroup>
Expand Down

0 comments on commit b028406

Please sign in to comment.