Repository navigation
Generate order and shipment PDFs with PdfPig instead of Scryber - #982
Merged
Merged
Conversation
Scryber renders the Razor PDF templates as HTML, and its latest release still pins SixLabors.ImageSharp 3.1.12, which carries known advisories. PDFsharp/MigraDoc is not an option either: since 6.5.0 it is AGPL-3.0, which would put every GPL-licensed GrandNode shop under the network clause. PdfPig (Apache-2.0, no dependencies) only writes PDF objects; layout lives in PdfComposer (wrapping, tables, page breaks, footer, PNG logo), and PdfService builds the invoice and packaging slip from the same resources the templates used. Noto Sans (OFL-1.1) is embedded so the output does not depend on the host's fonts; characters it lacks print as "?". The pdf_payment_method_below widget zone cannot reach a PDF that is not a view, so OrderPdfPaymentLinesEvent takes its place. Without Scryber, elFinder.Net.Core resolves ImageSharp 2.0.0, so Grand.Web.AdminShared now lifts it to 3.1.12 explicitly.
Comment on lines
+370
to
+374
| catch (Exception ex) | ||
| { | ||
| _logger.LogWarning(ex, "PDF logo {PictureId} could not be loaded", _pdfSettings.LogoPictureId); | ||
| return null; | ||
| } |
Comment on lines
+260
to
+265
| foreach (var usage in await _giftVoucherService.GetAllGiftVoucherUsageHistory(order.Id)) | ||
| { | ||
| if (usage.UsedValue <= 0) continue; | ||
| var giftVoucher = await _giftVoucherService.GetGiftVoucherById(usage.GiftVoucherId); | ||
| Add(Format("PDFInvoice.GiftVoucherInfo", lang, giftVoucher?.Code), usage.UsedValue); | ||
| } |
|
|
||
| var fileName = $"order_{order.OrderGuid}_{CommonHelper.GenerateRandomDigitCode(4)}.pdf"; | ||
|
|
||
| var dir = Path.Combine(_webHostEnvironment.WebRootPath, "assets/files/exportimport"); |
|
|
||
| if (!System.IO.Directory.Exists(dir)) System.IO.Directory.CreateDirectory(dir); | ||
|
|
||
| var filePath = Path.Combine(dir, fileName); |
added 2 commits
October 23, 2026 19:21
Every order in a bulk invoice starts a page with the logo, and PdfPig's AddPng writes a new image object each time, so 100 invoices grew to about 1 MB. The composer now reuses the image added on the first page.
- A table row taller than a page is split between its lines instead of running over the footer and off the page. - A document with no orders or shipments gets one empty page; PdfPig failed to build a PDF without pages. - Header text wraps beside the right-aligned logo, and the cursor moves below the logo before the address table. - A logo PNG that PdfPig cannot decode is converted again, or left out, before the layout starts instead of failing the whole invoice. - The footer is cut to a quarter of the page, so a very long footer no longer starts a new page for every line. - Table cells in header/footer HTML keep a space between them; style, script and comments are dropped; hr breaks the line. - Wrapping measures each word once and splits long words with a binary search. - PrintPackagingSlipsToPdf no longer loads a language it never used.
| /// </summary> | ||
| public void ClearImage() | ||
| { | ||
| if (_asideBottom == double.MaxValue) return; |
| } | ||
|
|
||
| //not even one line fits at the top of a page: stop rather than add pages forever | ||
| if (used == 0 && room >= pageRoom - 2 * CellPadding) break; |
| var space = Measure(" ", size, font); | ||
| foreach (var paragraph in Supported(text, font).Split('\n')) | ||
| { | ||
| var line = new StringBuilder(); |
Comment on lines
+245
to
+248
| catch (Exception) | ||
| { | ||
| return false; | ||
| } |
added 2 commits
October 24, 2026 06:21
…rom Razor views HtmlToPdfService was its only consumer. Plugins that rendered a view to a string through it need their own IRazorViewEngine call.
…inear time A vendor's copy of an invoice listed only that vendor's lines but no totals. It now ends with the subtotal, tax and total of those lines. Shipping, order discounts, gift vouchers and the payment fee belong to the whole order and stay off it, as does the checkout attribute text. A word wider than its column was measured again for every piece cut off it, so a long unbroken customer text took quadratic time: a 50,000 character attribute value held the request for over a minute. The word is now measured once and cut at the letter positions.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Type: feature
Issue
Order invoices and packaging slips are rendered from Razor templates to HTML and converted to PDF by Scryber. Its latest release (9.7.5) still pins SixLabors.ImageSharp 3.1.12, so three ImageSharp advisories stay open in every scan and can only be suppressed in
.trivyignore. Scryber's HTML subset has also produced several past bugs (logo paths, entity parsing, junk characters in header/footer).The obvious replacement, PDFsharp/MigraDoc, changed its license to AGPL-3.0 in 6.5.0. GrandNode is GPL-3.0, and the AGPL network clause would oblige every shop running it to publish its source, including custom plugins. The last MIT version (6.2.4) is frozen.
Solution
PdfComposerdoes text wrapping, tables with a header repeated after a page break, page breaks, a footer on every page and a PNG logo.PdfServicereplacesHtmlToPdfServicebehind the unchangedIPdfService. It prints the same content with the same resource keys as the removed templates.Grand.Business.Common, so output does not depend on the fonts installed on the host. Characters the font lacks print as?instead of failing the document.PdfHtmlText. Markup is dropped, never rendered.Shipment.VendorId. Neither reads the ambient work context any more.OrderPdfPaymentLinesEventreplaces thepdf_payment_method_belowwidget zone. A handler adds plain-text lines that are printed below the payment method.elFinder.Net.Core1.5.0 resolves ImageSharp 2.0.0.Grand.Web.AdminSharednow lifts it to 3.1.12 explicitly, following the existingMicrosoft.AspNetCore.Httplift. The three.trivyignoreentries are kept and now name elFinder as the source..aidocs are updated: PDF is code, not views, and the dependency table now lists PdfPig.Breaking changes
Views/PdfTemplates/OrderPdfTemplate.cshtmlandShipmentPdfTemplate.cshtmlare removed, in Grand.Web and in Grand.Web.Vendor. Installations that edited them lose those edits; the layout now lives inPdfService.pdf_payment_method_belowwidget zone is removed. Plugins that rendered into it need to handleOrderPdfPaymentLinesEventinstead.?, and right-to-left text is not supported.HtmlToPdfServiceis replaced byPdfService, with different constructor dependencies. Code that resolved it throughIPdfServiceis unaffected.IViewRenderServiceandViewRenderServiceare removed; the PDF templates were their only consumer. A plugin that rendered a Razor view to a string through them needs its ownIRazorViewEnginecall.Testing
dotnet test src/Tests/Grand.Business.Common.Tests --filter "FullyQualifiedName~Services.Pdf". 18 tests generate PDFs and read them back with PdfPig. They cover Polish characters, totals, the vendor filter, markup removal, missing glyphs, page breaks with a footer on every page, packaging-slip filtering, payment lines from the event, cancellation, the logo written once per document, an empty document, a row taller than a page, an over-long footer, header text beside the logo, a logo PNG PdfPig cannot decode, table cells in footer HTML, a vendor's own totals, and a very long unbroken word.<p>Bank: <b>…</b></p>.Zażółć Gęślą).dotnet list src/Web/Grand.Web/Grand.Web.csproj package --include-transitive. NeitherScryber.*norSixLabors.ImageSharp 2.0.0is listed.