Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: PDF Header/Footer pages size #10100

Merged
merged 1 commit into from
Jul 10, 2024
Merged
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
84 changes: 79 additions & 5 deletions src/Docfx.App/PdfBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

using UglyToad.PdfPig;
using UglyToad.PdfPig.Actions;
using UglyToad.PdfPig.Content;
using UglyToad.PdfPig.Graphics.Operations.SpecialGraphicsState;
using UglyToad.PdfPig.Outline;
using UglyToad.PdfPig.Outline.Destinations;
Expand Down Expand Up @@ -184,7 +185,7 @@ IResult TocPage(string url)
}
}

Task<byte[]> PrintHeaderFooter(Outline toc, int pageNumber, int totalPages)
Task<byte[]> PrintHeaderFooter(Outline toc, int pageNumber, int totalPages, Page contentPage)
{
var headerTemplate = ExpandTemplate(toc.pdfHeaderTemplate, pageNumber, totalPages);
var footerTemplate = ExpandTemplate(toc.pdfFooterTemplate ?? DefaultFooterTemplate, pageNumber, totalPages);
Expand All @@ -200,12 +201,27 @@ async Task<byte[]> PrintHeaderFooterCore()
{
await page.GotoAsync("about:blank");

return await page.PdfAsync(new()
PagePdfOptions options = new PagePdfOptions
{
DisplayHeaderFooter = true,
HeaderTemplate = headerTemplate,
FooterTemplate = footerTemplate,
});
};

if (TryGetPlaywrightPageFormat(contentPage.Size, out var pageFormat))
{
options.Format = pageFormat;
options.Landscape = contentPage.Width > contentPage.Height;
}
else
{
var customPageSize = GetPageSizeSettings(contentPage);
options.Width = customPageSize.Width;
options.Height = customPageSize.Height;
options.Landscape = customPageSize.Landscape;
}

return await page.PdfAsync(options);
}
finally
{
Expand All @@ -226,7 +242,7 @@ static string ExpandTemplate(string? pdfTemplate, int pageNumber, int totalPages
}

static async Task CreatePdf(
Func<Outline, Uri, Task<byte[]?>> printPdf, Func<Outline, int, int, Task<byte[]>> printHeaderFooter, ProgressTask task,
Func<Outline, Uri, Task<byte[]?>> printPdf, Func<Outline, int, int, Page, Task<byte[]>> printHeaderFooter, ProgressTask task,
Uri outlineUrl, Outline outline, string outputPath, Action<Dictionary<Outline, int>> updatePageNumbers)
{
var tempDirectory = Path.Combine(Path.GetTempPath(), ".docfx", "pdf", "pages");
Expand Down Expand Up @@ -349,7 +365,7 @@ async Task MergePdf()
if (isTocPage)
continue;

var headerFooter = await printHeaderFooter(outline, pageNumber, numberOfPages);
var headerFooter = await printHeaderFooter(outline, pageNumber, numberOfPages, document.GetPage(i));
using var headerFooterDocument = PdfDocument.Open(headerFooter);

pageBuilder.NewContentStreamBefore();
Expand Down Expand Up @@ -528,4 +544,62 @@ static HtmlTemplate TocHtmlTemplate(Uri baseUrl, Outline node, Dictionary<Outlin
</div>
</div>
""";

/// <summary>
/// Gets playwright page format from PdfPig's PageSize.
/// </summary>
private static bool TryGetPlaywrightPageFormat(PageSize pageSize, out string? pageFormat)
{
// List of supported formats: https://playwright.dev/dotnet/docs/api/class-page#page-pdf
switch (pageSize)
{
case PageSize.Letter:
case PageSize.Legal:
case PageSize.Tabloid:
case PageSize.Ledger:
case PageSize.A0:
case PageSize.A1:
case PageSize.A2:
case PageSize.A3:
case PageSize.A4:
case PageSize.A5:
case PageSize.A6:
pageFormat = pageSize.ToString();
return true;

// Following format is not supported format by playwright.
// It need to use Width/Height settings.
case PageSize.A7:
case PageSize.A8:
case PageSize.A9:
case PageSize.A10:
case PageSize.Custom:
case PageSize.Executive:
default:
pageFormat = null;
return false;
}
}

/// <summary>
/// Gets page size settings from PdfPig's Page object.
/// </summary>
private static (string Width, string Height, bool Landscape) GetPageSizeSettings(Page contentPage)
{
var isLandscape = contentPage.Width > contentPage.Height;
var width = getMillimeter(contentPage.Width);
var height = getMillimeter(contentPage.Height);

return isLandscape
? (height, width, true) // On Landscape mode. It need to swap width/height.
: (width, height, false);

// Gets millimeter string representation from `pt` value.
static string getMillimeter(double pt)
{
const double MillimeterPerInch = 25.4d;
const double Dpi = 72d; // Use Default DPI of PDF.
return $"{Math.Round(pt * MillimeterPerInch / Dpi)}mm";
}
}
}