Skip to content

Commit

Permalink
feat: Support PDF cover page (dotnet#9354)
Browse files Browse the repository at this point in the history
  • Loading branch information
yufeih authored and p-kostov committed Jun 28, 2024
1 parent 05094e3 commit 1ef4bb1
Show file tree
Hide file tree
Showing 10 changed files with 115 additions and 21 deletions.
1 change: 1 addition & 0 deletions samples/seed/articles/toc.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pdfFileName: seed.pdf
pdfCoverPage: ../pdf.html
items:
- name: Getting Started
href: docfx_getting_started.md
Expand Down
17 changes: 17 additions & 0 deletions samples/seed/pdf.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<style>
@media print {
@page {
margin: 0 !important;
}
body {
-webkit-print-color-adjust: exact;
-moz-print-color-adjust: exact;
-ms-print-color-adjust: exact;
print-color-adjust: exact;
}
}
</style>
<div style='background-color: green; width: 100%; height: 100%; display: flex; flex-direction: column'>
<p style='flex: 1'></p>
<h1 style='align-self: end; margin: 1rem 2rem; color: antiquewhite'>DOCFX PDF SAMPLE</h1>
</div>
52 changes: 32 additions & 20 deletions src/Docfx.App/PdfBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@ class Outline

public bool pdf { get; init; }
public string? pdfFileName { get; init; }
public string? pdfMargin { get; init; }
public bool pdfPrintBackground { get; init; }
public string? pdfCoverPage { get; init; }
}

public static Task Run(BuildJsonConfig config, string configDirectory, string? outputDirectory = null)
Expand Down Expand Up @@ -111,24 +110,22 @@ static async Task CreatePdf(IBrowser browser, Uri outlineUrl, Outline outline, s
var pageNumbers = new Dictionary<Outline, int>();
var nextPageNumbers = new Dictionary<Outline, int>();
var nextPageNumber = 1;
var margin = outline.pdfMargin ?? "0.4in";

await AnsiConsole.Progress().Columns(new SpinnerColumn(), new TaskDescriptionColumn { Alignment = Justify.Left }).StartAsync(async c =>
{
await Parallel.ForEachAsync(pages, async (item, CancellationToken) =>
{
var task = c.AddTask(item.url.PathAndQuery);
var page = await browser.NewPageAsync();
await page.GotoAsync(item.url.ToString());
var response = await page.GotoAsync(item.url.ToString());
if (response is null || !response.Ok)
throw new InvalidOperationException($"Failed to build PDF page [{response?.Status}]: {item.url}");
await page.AddScriptTagAsync(new() { Content = EnsureHeadingAnchorScript });
await page.WaitForLoadStateAsync(LoadState.NetworkIdle);
var bytes = await page.PdfAsync(new()
{
PrintBackground = outline.pdfPrintBackground,
Margin = new() { Bottom = margin, Top = margin, Left = margin, Right = margin },
});
var bytes = await page.PdfAsync();
File.WriteAllBytes(item.path, bytes);
task.Value = task.MaxValue;
task.StopTask();
});
});

Expand All @@ -137,21 +134,33 @@ await Parallel.ForEachAsync(pages, async (item, CancellationToken) =>

IEnumerable<(string path, Uri url, Outline node)> GetPages(Outline outline)
{
if (!string.IsNullOrEmpty(outline.pdfCoverPage))
{
var url = new Uri(outlineUrl, outline.pdfCoverPage);
if (url.Host == outlineUrl.Host)
yield return (GetFilePath(url), url, new() { href = outline.pdfCoverPage });
}

if (!string.IsNullOrEmpty(outline.href))
{
var url = new Uri(outlineUrl, outline.href);
if (url.Host == outlineUrl.Host)
{
var id = Convert.ToHexString(SHA256.Create().ComputeHash(Encoding.UTF8.GetBytes(url.ToString()))).Substring(0, 6).ToLower();
var name = Regex.Replace(url.PathAndQuery, "\\W", "-").Trim('-');
yield return (Path.Combine(tempDirectory, $"{name}-{id}.pdf"), url, outline);
}
yield return (GetFilePath(url), url, outline);
}

if (outline.items != null)
{
foreach (var item in outline.items)
foreach (var url in GetPages(item))
yield return url;
}
}

string GetFilePath(Uri url)
{
var id = Convert.ToHexString(SHA256.Create().ComputeHash(Encoding.UTF8.GetBytes(url.ToString()))).Substring(0, 6).ToLower();
var name = Regex.Replace(url.PathAndQuery, "\\W", "-").Trim('-');
return Path.Combine(tempDirectory, $"{name}-{id}.pdf");
}

void MergePdf()
Expand Down Expand Up @@ -254,11 +263,14 @@ IEnumerable<BookmarkNode> CreateBookmarks(Outline[]? items, int level = 0)
continue;
}

nextPageNumber = nextPageNumbers[item];
yield return new DocumentBookmarkNode(
item.name, level,
new(pageNumbers[item], ExplicitDestinationType.XyzCoordinates, ExplicitDestinationCoordinates.Empty),
CreateBookmarks(item.items, level + 1).ToArray());
if (!string.IsNullOrEmpty(item.name))
{
nextPageNumber = nextPageNumbers[item];
yield return new DocumentBookmarkNode(
item.name, level,
new(pageNumbers[item], ExplicitDestinationType.XyzCoordinates, ExplicitDestinationCoordinates.Empty),
CreateBookmarks(item.items, level + 1).ToArray());
}
}
}
}
Expand Down
6 changes: 6 additions & 0 deletions templates/modern/src/docfx.scss
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,9 @@ article {
display: none;
}
}

@media print {
@page {
margin: .4in;
}
}
10 changes: 10 additions & 0 deletions templates/modern/src/layout.scss
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,18 @@ body[data-layout="landing"] {
padding-bottom: $main-padding-bottom;

>.content {
display: flex;
flex-direction: column;
width: 100%;

>:not(article) {
display: none;
}

>article {
flex: 1;
}

@include media-breakpoint-up(md) {
>article [id] {
scroll-margin-top: $header-height;
Expand All @@ -89,6 +95,10 @@ body[data-layout="landing"] {
}

@media print {
>main {
padding: 0 !important;
}

>header, >footer {
display: none;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
}
],
"pdfFileName": "seed.pdf",
"pdfCoverPage": "../pdf.html",
"_appName": "Seed",
"_appTitle": "docfx seed website",
"_enableSearch": true,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"content": "{\"items\":[{\"name\":\"Getting Started\",\"href\":\"docfx_getting_started.html\",\"topicHref\":\"docfx_getting_started.html\"},{\"name\":\"Engineering Docs\",\"items\":[{\"name\":\"Section 1\"},{\"name\":\"Engineering Guidelines\",\"href\":\"engineering_guidelines.html\",\"topicHref\":\"engineering_guidelines.html\"},{\"name\":\"CSharp Coding Standards\",\"href\":\"csharp_coding_standards.html\",\"topicHref\":\"csharp_coding_standards.html\"}],\"expanded\":true},{\"name\":\"Markdown\",\"href\":\"markdown.html\",\"topicHref\":\"markdown.html\"},{\"name\":\"Microsoft Docs\",\"href\":\"https://docs.microsoft.com/en-us/\",\"topicHref\":\"https://docs.microsoft.com/en-us/\"}],\"pdfFileName\":\"seed.pdf\",\"pdf\":true}"
"content": "{\"items\":[{\"name\":\"Getting Started\",\"href\":\"docfx_getting_started.html\",\"topicHref\":\"docfx_getting_started.html\"},{\"name\":\"Engineering Docs\",\"items\":[{\"name\":\"Section 1\"},{\"name\":\"Engineering Guidelines\",\"href\":\"engineering_guidelines.html\",\"topicHref\":\"engineering_guidelines.html\"},{\"name\":\"CSharp Coding Standards\",\"href\":\"csharp_coding_standards.html\",\"topicHref\":\"csharp_coding_standards.html\"}],\"expanded\":true},{\"name\":\"Markdown\",\"href\":\"markdown.html\",\"topicHref\":\"markdown.html\"},{\"name\":\"Microsoft Docs\",\"href\":\"https://docs.microsoft.com/en-us/\",\"topicHref\":\"https://docs.microsoft.com/en-us/\"}],\"pdfFileName\":\"seed.pdf\",\"pdfCoverPage\":\"../pdf.html\",\"pdf\":true}"
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,6 @@
}
],
"pdfFileName": "seed.pdf",
"pdfCoverPage": "../pdf.html",
"pdf": true
}
Original file line number Diff line number Diff line change
Expand Up @@ -809,6 +809,11 @@
"title": "Namespace MRef | docfx seed website",
"keywords": "Namespace MRef Namespaces MRef.Demo"
},
"pdf.html": {
"href": "pdf.html",
"title": "DOCFX PDF SAMPLE | docfx seed website",
"keywords": "@media print { @page { margin: 0 !important; } body { -webkit-print-color-adjust: exact; -moz-print-color-adjust: exact; -ms-print-color-adjust: exact; print-color-adjust: exact; } } DOCFX PDF SAMPLE"
},
"restapi/contacts.html": {
"href": "restapi/contacts.html",
"title": "Contacts | docfx seed website",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"conceptual": "<style> \n@media print {\n @page {\n margin: 0 !important;\n }\n body {\n -webkit-print-color-adjust: exact;\n -moz-print-color-adjust: exact;\n -ms-print-color-adjust: exact;\n print-color-adjust: exact;\n }\n}\n</style>\n<div style='background-color: green; width: 100%; height: 100%; display: flex; flex-direction: column'>\n <p style='flex: 1'></p>\n <h1 style='align-self: end; margin: 1rem 2rem; color: antiquewhite'>DOCFX PDF SAMPLE</h1>\n</div>\n",
"type": "Conceptual",
"source": {
"remote": {
"path": "samples/seed/pdf.md",
"branch": "main",
"repo": "https://github.com/dotnet/docfx"
},
"startLine": 0.0,
"endLine": 0.0
},
"path": "pdf.md",
"documentation": {
"remote": {
"path": "samples/seed/pdf.md",
"branch": "main",
"repo": "https://github.com/dotnet/docfx"
},
"startLine": 0.0,
"endLine": 0.0
},
"_appName": "Seed",
"_appTitle": "docfx seed website",
"_enableSearch": true,
"pdf": true,
"rawTitle": "",
"title": "DOCFX PDF SAMPLE",
"wordCount": 3.0,
"_key": "pdf.md",
"_navKey": "~/toc.yml",
"_navPath": "toc.html",
"_navRel": "toc.html",
"_path": "pdf.html",
"_rel": "",
"_tocKey": "~/toc.yml",
"_tocPath": "toc.html",
"_tocRel": "toc.html",
"_disableToc": true,
"docurl": "https://github.com/dotnet/docfx/blob/main/samples/seed/pdf.md/#L1"
}

0 comments on commit 1ef4bb1

Please sign in to comment.