Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion samples/seed/docfx.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@
"_appTitle": "docfx seed website",
"_appName": "Seed",
"_enableSearch": true,
"pdf": true
"pdf": true,
"pdfTocPage": true
},
"output": "_site",
"exportViewModel": true,
Expand Down
50 changes: 43 additions & 7 deletions src/Docfx.App/PdfBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
using Docfx.Build;
using Docfx.Plugins;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Playwright;
using Spectre.Console;
Expand All @@ -19,6 +21,8 @@
using UglyToad.PdfPig.Outline.Destinations;
using UglyToad.PdfPig.Writer;

using static Docfx.Build.HtmlTemplate;

#nullable enable

namespace Docfx.Pdf;
Expand All @@ -34,6 +38,7 @@ class Outline
public bool pdf { get; init; }
public string? pdfFileName { get; init; }
public string? pdfCoverPage { get; init; }
public bool pdfTocPage { get; init; }
}

public static Task Run(BuildJsonConfig config, string configDirectory, string? outputDirectory = null)
Expand All @@ -47,8 +52,8 @@ public static Task Run(BuildJsonConfig config, string configDirectory, string? o

public static async Task CreatePdf(string outputFolder)
{
var pdfTocs = GetPdfTocs().ToArray();
if (pdfTocs.Length == 0)
var pdfTocs = GetPdfTocs().ToDictionary(p => p.url, p => p.toc);
if (pdfTocs.Count == 0)
return;

AnsiConsole.Status().Start("Installing Chromium...", _ => Program.Main(new[] { "install", "chromium" }));
Expand All @@ -58,10 +63,14 @@ public static async Task CreatePdf(string outputFolder)
builder.Logging.ClearProviders();
builder.WebHost.UseUrls("http://127.0.0.1:0");

Uri? baseUrl = null;

using var app = builder.Build();
app.UseServe(outputFolder);
app.MapGet("/_pdftoc/{*id}", (string id) => Results.Content(TocHtmlTemplate(new Uri(baseUrl!, id), pdfTocs[id]).ToString(), "text/html"));
await app.StartAsync();
var baseUrl = new Uri(app.Urls.First());

baseUrl = new Uri(app.Urls.First());

using var playwright = await Playwright.CreateAsync();
var browser = await playwright.Chromium.LaunchAsync();
Expand All @@ -73,7 +82,7 @@ public static async Task CreatePdf(string outputFolder)
await CreatePdf(browser, new(baseUrl, url), toc, outputPath);
}

IEnumerable<(string, Outline)> GetPdfTocs()
IEnumerable<(string url, Outline toc)> GetPdfTocs()
{
var manifestPath = Path.Combine(outputFolder, "manifest.json");
var manifest = Newtonsoft.Json.JsonConvert.DeserializeObject<Manifest>(File.ReadAllText(manifestPath));
Expand Down Expand Up @@ -141,6 +150,13 @@ await Parallel.ForEachAsync(pages, async (item, CancellationToken) =>
yield return (GetFilePath(url), url, new() { href = outline.pdfCoverPage });
}

if (outline.pdfTocPage)
{
var href = $"/_pdftoc{outlineUrl.AbsolutePath}";
var url = new Uri(outlineUrl, href);
yield return (GetFilePath(url), url, new() { href = href });
}

if (!string.IsNullOrEmpty(outline.href))
{
var url = new Uri(outlineUrl, outline.href);
Expand Down Expand Up @@ -229,7 +245,7 @@ PdfAction HandleUriAction(UriAction url)
AnsiConsole.MarkupLine($"[yellow]Failed to resolve named dest: {name}[/]");
}

return new GoToAction(new(pageNumbers[pages[0].node], ExplicitDestinationType.XyzCoordinates, ExplicitDestinationCoordinates.Empty));
return new GoToAction(new(pageNumbers[pages[0].node], ExplicitDestinationType.FitHorizontally, ExplicitDestinationCoordinates.Empty));
}
}

Expand All @@ -249,7 +265,7 @@ IEnumerable<BookmarkNode> CreateBookmarks(Outline[]? items, int level = 0)
{
yield return new DocumentBookmarkNode(
item.name, level,
new(nextPageNumber, ExplicitDestinationType.XyzCoordinates, ExplicitDestinationCoordinates.Empty),
new(nextPageNumber, ExplicitDestinationType.FitHorizontally, ExplicitDestinationCoordinates.Empty),
CreateBookmarks(item.items, level + 1).ToArray());
continue;
}
Expand All @@ -268,13 +284,33 @@ IEnumerable<BookmarkNode> CreateBookmarks(Outline[]? items, int level = 0)
nextPageNumber = nextPageNumbers[item];
yield return new DocumentBookmarkNode(
item.name, level,
new(pageNumbers[item], ExplicitDestinationType.XyzCoordinates, ExplicitDestinationCoordinates.Empty),
new(pageNumbers[item], ExplicitDestinationType.FitHorizontally, ExplicitDestinationCoordinates.Empty),
CreateBookmarks(item.items, level + 1).ToArray());
}
}
}
}

static HtmlTemplate TocHtmlTemplate(Uri baseUrl, Outline node)
{
return Html($"""
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/public/docfx.min.css">
<link rel="stylesheet" href="/public/main.css">
</head>
<body class="pdftoc">
<h1>Table of Contents</h1>
<ul>{node.items?.Select(TocNode)}</ul>
</body>
</html>
""");

HtmlTemplate TocNode(Outline node) => string.IsNullOrEmpty(node.name) ? default :
Html($"<li><a href='{(string.IsNullOrEmpty(node.href) ? null : new Uri(baseUrl, node.href))}'>{node.name}</a>{(node.items?.Length > 0 ? Html($"<ul>{node.items.Select(TocNode)}</ul>") : null)}</li>");
}

/// <summary>
/// Adds hidden links to headings to ensure Chromium saves heading anchors to named dests
/// for cross page bookmark reference.
Expand Down
4 changes: 4 additions & 0 deletions templates/modern/src/docfx.scss
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,7 @@ article {
margin: .4in;
}
}

.pdftoc ul {
list-style: none;
}
Original file line number Diff line number Diff line change
Expand Up @@ -825,6 +825,7 @@
"_appTitle": "docfx seed website",
"_enableSearch": true,
"pdf": true,
"pdfTocPage": true,
"_key": "obj/api/BuildFromAssembly.Class1.yml",
"_navKey": "~/toc.yml",
"_navPath": "toc.html",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@
"_appTitle": "docfx seed website",
"_enableSearch": true,
"pdf": true,
"pdfTocPage": true,
"_key": "obj/api/BuildFromAssembly.yml",
"_navKey": "~/toc.yml",
"_navPath": "toc.html",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,7 @@
"_appTitle": "docfx seed website",
"_enableSearch": true,
"pdf": true,
"pdfTocPage": true,
"_key": "obj/api/BuildFromCSharpSourceCode.CSharp.yml",
"_navKey": "~/toc.yml",
"_navPath": "toc.html",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@
"_appTitle": "docfx seed website",
"_enableSearch": true,
"pdf": true,
"pdfTocPage": true,
"_key": "obj/api/BuildFromCSharpSourceCode.yml",
"_navKey": "~/toc.yml",
"_navPath": "toc.html",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@
"_appTitle": "docfx seed website",
"_enableSearch": true,
"pdf": true,
"pdfTocPage": true,
"_key": "obj/api/BuildFromProject.Class1.IIssue8948.yml",
"_navKey": "~/toc.yml",
"_navPath": "toc.html",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1922,6 +1922,7 @@
"_appTitle": "docfx seed website",
"_enableSearch": true,
"pdf": true,
"pdfTocPage": true,
"_key": "obj/api/BuildFromProject.Class1.Issue8665.yml",
"_navKey": "~/toc.yml",
"_navPath": "toc.html",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2743,6 +2743,7 @@
"_appTitle": "docfx seed website",
"_enableSearch": true,
"pdf": true,
"pdfTocPage": true,
"_key": "obj/api/BuildFromProject.Class1.Issue8696Attribute.yml",
"_navKey": "~/toc.yml",
"_navPath": "toc.html",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -795,6 +795,7 @@
"_appTitle": "docfx seed website",
"_enableSearch": true,
"pdf": true,
"pdfTocPage": true,
"_key": "obj/api/BuildFromProject.Class1.Issue8948.yml",
"_navKey": "~/toc.yml",
"_navPath": "toc.html",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,7 @@
"_appTitle": "docfx seed website",
"_enableSearch": true,
"pdf": true,
"pdfTocPage": true,
"_key": "obj/api/BuildFromProject.Class1.Issue9260.yml",
"_navKey": "~/toc.yml",
"_navPath": "toc.html",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,7 @@
"_appTitle": "docfx seed website",
"_enableSearch": true,
"pdf": true,
"pdfTocPage": true,
"_key": "obj/api/BuildFromProject.Class1.Test-1.yml",
"_navKey": "~/toc.yml",
"_navPath": "toc.html",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2258,6 +2258,7 @@
"_appTitle": "docfx seed website",
"_enableSearch": true,
"pdf": true,
"pdfTocPage": true,
"_key": "obj/api/BuildFromProject.Class1.yml",
"_navKey": "~/toc.yml",
"_navPath": "toc.html",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,7 @@
"_appTitle": "docfx seed website",
"_enableSearch": true,
"pdf": true,
"pdfTocPage": true,
"_key": "obj/api/BuildFromProject.IInheritdoc.yml",
"_navKey": "~/toc.yml",
"_navPath": "toc.html",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -867,6 +867,7 @@
"_appTitle": "docfx seed website",
"_enableSearch": true,
"pdf": true,
"pdfTocPage": true,
"_key": "obj/api/BuildFromProject.Inheritdoc.Issue6366.Class1-1.yml",
"_navKey": "~/toc.yml",
"_navPath": "toc.html",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -936,6 +936,7 @@
"_appTitle": "docfx seed website",
"_enableSearch": true,
"pdf": true,
"pdfTocPage": true,
"_key": "obj/api/BuildFromProject.Inheritdoc.Issue6366.Class2.yml",
"_navKey": "~/toc.yml",
"_navPath": "toc.html",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,7 @@
"_appTitle": "docfx seed website",
"_enableSearch": true,
"pdf": true,
"pdfTocPage": true,
"_key": "obj/api/BuildFromProject.Inheritdoc.Issue6366.yml",
"_navKey": "~/toc.yml",
"_navPath": "toc.html",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -832,6 +832,7 @@
"_appTitle": "docfx seed website",
"_enableSearch": true,
"pdf": true,
"pdfTocPage": true,
"_key": "obj/api/BuildFromProject.Inheritdoc.Issue7035.yml",
"_navKey": "~/toc.yml",
"_navPath": "toc.html",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1123,6 +1123,7 @@
"_appTitle": "docfx seed website",
"_enableSearch": true,
"pdf": true,
"pdfTocPage": true,
"_key": "obj/api/BuildFromProject.Inheritdoc.Issue7484.yml",
"_navKey": "~/toc.yml",
"_navPath": "toc.html",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1306,6 +1306,7 @@
"_appTitle": "docfx seed website",
"_enableSearch": true,
"pdf": true,
"pdfTocPage": true,
"_key": "obj/api/BuildFromProject.Inheritdoc.Issue8101.yml",
"_navKey": "~/toc.yml",
"_navPath": "toc.html",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,7 @@
"_appTitle": "docfx seed website",
"_enableSearch": true,
"pdf": true,
"pdfTocPage": true,
"_key": "obj/api/BuildFromProject.Inheritdoc.Issue8129.yml",
"_navKey": "~/toc.yml",
"_navPath": "toc.html",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1140,6 +1140,7 @@
"_appTitle": "docfx seed website",
"_enableSearch": true,
"pdf": true,
"pdfTocPage": true,
"_key": "obj/api/BuildFromProject.Inheritdoc.yml",
"_navKey": "~/toc.yml",
"_navPath": "toc.html",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,7 @@
"_appTitle": "docfx seed website",
"_enableSearch": true,
"pdf": true,
"pdfTocPage": true,
"_key": "obj/api/BuildFromProject.Issue8540.A.A.yml",
"_navKey": "~/toc.yml",
"_navPath": "toc.html",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@
"_appTitle": "docfx seed website",
"_enableSearch": true,
"pdf": true,
"pdfTocPage": true,
"_key": "obj/api/BuildFromProject.Issue8540.A.yml",
"_navKey": "~/toc.yml",
"_navPath": "toc.html",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,7 @@
"_appTitle": "docfx seed website",
"_enableSearch": true,
"pdf": true,
"pdfTocPage": true,
"_key": "obj/api/BuildFromProject.Issue8540.B.B.yml",
"_navKey": "~/toc.yml",
"_navPath": "toc.html",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@
"_appTitle": "docfx seed website",
"_enableSearch": true,
"pdf": true,
"pdfTocPage": true,
"_key": "obj/api/BuildFromProject.Issue8540.B.yml",
"_navKey": "~/toc.yml",
"_navPath": "toc.html",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@
"_appTitle": "docfx seed website",
"_enableSearch": true,
"pdf": true,
"pdfTocPage": true,
"_key": "obj/api/BuildFromProject.Issue8540.yml",
"_navKey": "~/toc.yml",
"_navPath": "toc.html",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1093,6 +1093,7 @@
"_appTitle": "docfx seed website",
"_enableSearch": true,
"pdf": true,
"pdfTocPage": true,
"_key": "obj/api/BuildFromProject.yml",
"_navKey": "~/toc.yml",
"_navPath": "toc.html",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -889,6 +889,7 @@
"_appTitle": "docfx seed website",
"_enableSearch": true,
"pdf": true,
"pdfTocPage": true,
"_key": "obj/api/BuildFromVBSourceCode.BaseClass1.yml",
"_navKey": "~/toc.yml",
"_navPath": "toc.html",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1491,6 +1491,7 @@
"_appTitle": "docfx seed website",
"_enableSearch": true,
"pdf": true,
"pdfTocPage": true,
"_key": "obj/api/BuildFromVBSourceCode.Class1.yml",
"_navKey": "~/toc.yml",
"_navPath": "toc.html",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@
"_appTitle": "docfx seed website",
"_enableSearch": true,
"pdf": true,
"pdfTocPage": true,
"_key": "obj/api/BuildFromVBSourceCode.yml",
"_navKey": "~/toc.yml",
"_navPath": "toc.html",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4277,6 +4277,7 @@
"_appTitle": "docfx seed website",
"_enableSearch": true,
"pdf": true,
"pdfTocPage": true,
"_key": "obj/api/CatLibrary.Cat-2.yml",
"_navKey": "~/toc.yml",
"_navPath": "toc.html",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1199,6 +1199,7 @@
"_appTitle": "docfx seed website",
"_enableSearch": true,
"pdf": true,
"pdfTocPage": true,
"_key": "obj/api/CatLibrary.CatException-1.yml",
"_navKey": "~/toc.yml",
"_navPath": "toc.html",
Expand Down
Loading