Conversation
Reviewer's GuideThis PR enhances the HTML-to-PDF service by introducing a customizable PaperFormat parameter and expanding PDF output options, implemented via new helper methods to map user settings to PuppeteerSharp’s PDF configuration. Class diagram for updated PdfOptions and PaperFormat handlingclassDiagram
class PdfOptions {
+bool Landscape
+bool PrintBackground
+PaperFormat Format
+MarginOptions MarginOptions
+bool DisplayHeaderFooter
+double Scale
}
class PaperFormat {
+double Width
+double Height
+A0
+A1
+A2
+A3
+A4
+A5
+A6
+Letter
+Legal
+Tabloid
+Ledger
}
class MarginOptions {
+double Top
+double Bottom
+double Left
+double Right
}
PdfOptions --> PaperFormat : uses
PdfOptions --> MarginOptions : uses
Class diagram for DefaultPdfService helper methodsclassDiagram
class DefaultPdfService {
+static PuppeteerSharp.PdfOptions GetOptions(PdfOptions? options)
+static PuppeteerSharp.Media.MarginOptions? GetMarginOptions(MarginOptions options)
+static PuppeteerSharp.Media.PaperFormat GetFormat(PaperFormat format)
}
DefaultPdfService --> PdfOptions : uses
DefaultPdfService --> MarginOptions : uses
DefaultPdfService --> PaperFormat : uses
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey there - I've reviewed your changes - here's some feedback:
- Consider replacing the long if/else chain in GetFormat with a C# switch expression or a dictionary lookup to make the mapping more concise and maintainable.
- Null‐check options.MarginOptions (and any other optional sub‐properties) before calling GetMarginOptions to prevent potential NullReferenceExceptions when those properties are not provided.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Consider replacing the long if/else chain in GetFormat with a C# switch expression or a dictionary lookup to make the mapping more concise and maintainable.
- Null‐check options.MarginOptions (and any other optional sub‐properties) before calling GetMarginOptions to prevent potential NullReferenceExceptions when those properties are not provided.
## Individual Comments
### Comment 1
<location> `src/components/BootstrapBlazor.Html2Pdf/Services/DefaultPdfService.cs:163-166` </location>
<code_context>
+ }
+ else
+ {
+ return new PuppeteerSharp.Media.PaperFormat(format.Width, format.Height);
+ }
+ }
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Validate custom PaperFormat dimensions to prevent invalid PDF output.
Ensure Width and Height are positive and within acceptable limits to prevent runtime errors or malformed PDFs.
```suggestion
else
{
// Validate custom PaperFormat dimensions
const double MinDimension = 1.0; // Minimum allowed dimension in inches
const double MaxDimension = 100.0; // Maximum allowed dimension in inches
if (format.Width < MinDimension || format.Height < MinDimension ||
format.Width > MaxDimension || format.Height > MaxDimension)
{
throw new ArgumentOutOfRangeException(
$"Custom PaperFormat dimensions are invalid. Width and Height must be between {MinDimension} and {MaxDimension} inches. " +
$"Received Width: {format.Width}, Height: {format.Height}");
}
return new PuppeteerSharp.Media.PaperFormat(format.Width, format.Height);
}
```
</issue_to_address>
### Comment 2
<location> `src/components/BootstrapBlazor.Html2Pdf/Services/DefaultPdfService.cs:117` </location>
<code_context>
+ Right = options.Right
+ };
+
+ private static PuppeteerSharp.Media.PaperFormat GetFormat(PaperFormat format)
+ {
+ if (format == PaperFormat.A0)
</code_context>
<issue_to_address>
**issue (complexity):** Consider replacing the long if/else chain in GetFormat with a switch-expression or static lookup for improved clarity and maintainability.
Consider using a switch-expression (or a static lookup) to collapse the long if/else chain:
```csharp
private static PuppeteerSharp.Media.PaperFormat GetFormat(PaperFormat format)
=> format switch
{
PaperFormat.A0 => PuppeteerSharp.Media.PaperFormat.A0,
PaperFormat.A1 => PuppeteerSharp.Media.PaperFormat.A1,
PaperFormat.A2 => PuppeteerSharp.Media.PaperFormat.A2,
PaperFormat.A3 => PuppeteerSharp.Media.PaperFormat.A3,
PaperFormat.A4 => PuppeteerSharp.Media.PaperFormat.A4,
PaperFormat.A5 => PuppeteerSharp.Media.PaperFormat.A5,
PaperFormat.A6 => PuppeteerSharp.Media.PaperFormat.A6,
PaperFormat.Letter => PuppeteerSharp.Media.PaperFormat.Letter,
PaperFormat.Legal => PuppeteerSharp.Media.PaperFormat.Legal,
PaperFormat.Tabloid=> PuppeteerSharp.Media.PaperFormat.Tabloid,
PaperFormat.Ledger => PuppeteerSharp.Media.PaperFormat.Ledger,
_ => new PuppeteerSharp.Media.PaperFormat(format.Width, format.Height)
};
```
Or prebuild a readonly dictionary:
```csharp
private static readonly IReadOnlyDictionary<PaperFormat, PuppeteerSharp.Media.PaperFormat>
_formatMap = new Dictionary<PaperFormat, PuppeteerSharp.Media.PaperFormat>
{
[PaperFormat.A0] = PuppeteerSharp.Media.PaperFormat.A0,
[PaperFormat.A1] = PuppeteerSharp.Media.PaperFormat.A1,
// ...
};
private static PuppeteerSharp.Media.PaperFormat GetFormat(PaperFormat format)
=> _formatMap.TryGetValue(format, out var pf)
? pf
: new PuppeteerSharp.Media.PaperFormat(format.Width, format.Height);
```
Either approach keeps the same behavior but is far more concise and easier to maintain.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| else | ||
| { | ||
| return new PuppeteerSharp.Media.PaperFormat(format.Width, format.Height); | ||
| } |
There was a problem hiding this comment.
suggestion (bug_risk): Validate custom PaperFormat dimensions to prevent invalid PDF output.
Ensure Width and Height are positive and within acceptable limits to prevent runtime errors or malformed PDFs.
| else | |
| { | |
| return new PuppeteerSharp.Media.PaperFormat(format.Width, format.Height); | |
| } | |
| else | |
| { | |
| // Validate custom PaperFormat dimensions | |
| const double MinDimension = 1.0; // Minimum allowed dimension in inches | |
| const double MaxDimension = 100.0; // Maximum allowed dimension in inches | |
| if (format.Width < MinDimension || format.Height < MinDimension || | |
| format.Width > MaxDimension || format.Height > MaxDimension) | |
| { | |
| throw new ArgumentOutOfRangeException( | |
| $"Custom PaperFormat dimensions are invalid. Width and Height must be between {MinDimension} and {MaxDimension} inches. " + | |
| $"Received Width: {format.Width}, Height: {format.Height}"); | |
| } | |
| return new PuppeteerSharp.Media.PaperFormat(format.Width, format.Height); | |
| } |
| Right = options.Right | ||
| }; | ||
|
|
||
| private static PuppeteerSharp.Media.PaperFormat GetFormat(PaperFormat format) |
There was a problem hiding this comment.
issue (complexity): Consider replacing the long if/else chain in GetFormat with a switch-expression or static lookup for improved clarity and maintainability.
Consider using a switch-expression (or a static lookup) to collapse the long if/else chain:
private static PuppeteerSharp.Media.PaperFormat GetFormat(PaperFormat format)
=> format switch
{
PaperFormat.A0 => PuppeteerSharp.Media.PaperFormat.A0,
PaperFormat.A1 => PuppeteerSharp.Media.PaperFormat.A1,
PaperFormat.A2 => PuppeteerSharp.Media.PaperFormat.A2,
PaperFormat.A3 => PuppeteerSharp.Media.PaperFormat.A3,
PaperFormat.A4 => PuppeteerSharp.Media.PaperFormat.A4,
PaperFormat.A5 => PuppeteerSharp.Media.PaperFormat.A5,
PaperFormat.A6 => PuppeteerSharp.Media.PaperFormat.A6,
PaperFormat.Letter => PuppeteerSharp.Media.PaperFormat.Letter,
PaperFormat.Legal => PuppeteerSharp.Media.PaperFormat.Legal,
PaperFormat.Tabloid=> PuppeteerSharp.Media.PaperFormat.Tabloid,
PaperFormat.Ledger => PuppeteerSharp.Media.PaperFormat.Ledger,
_ => new PuppeteerSharp.Media.PaperFormat(format.Width, format.Height)
};Or prebuild a readonly dictionary:
private static readonly IReadOnlyDictionary<PaperFormat, PuppeteerSharp.Media.PaperFormat>
_formatMap = new Dictionary<PaperFormat, PuppeteerSharp.Media.PaperFormat>
{
[PaperFormat.A0] = PuppeteerSharp.Media.PaperFormat.A0,
[PaperFormat.A1] = PuppeteerSharp.Media.PaperFormat.A1,
// ...
};
private static PuppeteerSharp.Media.PaperFormat GetFormat(PaperFormat format)
=> _formatMap.TryGetValue(format, out var pf)
? pf
: new PuppeteerSharp.Media.PaperFormat(format.Width, format.Height);Either approach keeps the same behavior but is far more concise and easier to maintain.
There was a problem hiding this comment.
Pull Request Overview
This PR extends the PDF generation functionality by adding support for additional PDF options including paper format, margins, background printing, headers/footers, and scaling. It also introduces conditional versioning based on Visual Studio version to support both v17.0 and v18.0.
- Added support for paper format conversion between custom and PuppeteerSharp enums
- Added support for margin options, print background, display header/footer, and scale settings
- Implemented conditional package versioning based on Visual Studio version
Reviewed Changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/components/BootstrapBlazor.Html2Pdf/Services/DefaultPdfService.cs | Extended GetOptions method to map additional PDF properties and added helper methods for format and margin conversion |
| src/components/BootstrapBlazor.Html2Pdf/BootstrapBlazor.Html2Pdf.csproj | Introduced conditional versioning for VS 17.0 and 18.0 with corresponding package version updates |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| private static PuppeteerSharp.Media.MarginOptions? GetMarginOptions(MarginOptions options) => new PuppeteerSharp.Media.MarginOptions | ||
| { | ||
| Top = options.Top, | ||
| Bottom = options.Bottom, | ||
| Left = options.Left, | ||
| Right = options.Right | ||
| }; |
There was a problem hiding this comment.
The method does not handle null input. If options.MarginOptions is null when called from GetOptions (line 103), this will throw a NullReferenceException when accessing options.Top, options.Bottom, etc. Consider adding a null check or making the parameter nullable and returning null if the input is null.
| private static PuppeteerSharp.Media.MarginOptions? GetMarginOptions(MarginOptions options) => new PuppeteerSharp.Media.MarginOptions | |
| { | |
| Top = options.Top, | |
| Bottom = options.Bottom, | |
| Left = options.Left, | |
| Right = options.Right | |
| }; | |
| private static PuppeteerSharp.Media.MarginOptions? GetMarginOptions(MarginOptions? options) | |
| { | |
| if (options == null) return null; | |
| return new PuppeteerSharp.Media.MarginOptions | |
| { | |
| Top = options.Top, | |
| Bottom = options.Bottom, | |
| Left = options.Left, | |
| Right = options.Right | |
| }; | |
| } |
| if (format == PaperFormat.A0) | ||
| { | ||
| return PuppeteerSharp.Media.PaperFormat.A0; | ||
| } | ||
| else if (format == PaperFormat.A1) | ||
| { | ||
| return PuppeteerSharp.Media.PaperFormat.A1; | ||
| } | ||
| else if (format == PaperFormat.A2) | ||
| { | ||
| return PuppeteerSharp.Media.PaperFormat.A2; | ||
| } | ||
| else if (format == PaperFormat.A3) | ||
| { | ||
| return PuppeteerSharp.Media.PaperFormat.A3; | ||
| } | ||
| else if (format == PaperFormat.A4) | ||
| { | ||
| return PuppeteerSharp.Media.PaperFormat.A4; | ||
| } | ||
| else if (format == PaperFormat.A5) | ||
| { | ||
| return PuppeteerSharp.Media.PaperFormat.A5; | ||
| } | ||
| else if (format == PaperFormat.A6) | ||
| { | ||
| return PuppeteerSharp.Media.PaperFormat.A6; | ||
| } | ||
| else if (format == PaperFormat.Letter) | ||
| { | ||
| return PuppeteerSharp.Media.PaperFormat.Letter; | ||
| } | ||
| else if (format == PaperFormat.Legal) | ||
| { | ||
| return PuppeteerSharp.Media.PaperFormat.Legal; | ||
| } | ||
| else if (format == PaperFormat.Tabloid) | ||
| { | ||
| return PuppeteerSharp.Media.PaperFormat.Tabloid; | ||
| } | ||
| else if (format == PaperFormat.Ledger) | ||
| { | ||
| return PuppeteerSharp.Media.PaperFormat.Ledger; | ||
| } | ||
| else | ||
| { | ||
| return new PuppeteerSharp.Media.PaperFormat(format.Width, format.Height); | ||
| } |
There was a problem hiding this comment.
The long if-else chain can be refactored using a switch expression for better readability and maintainability. This would make the code more concise and easier to extend in the future.
| if (format == PaperFormat.A0) | |
| { | |
| return PuppeteerSharp.Media.PaperFormat.A0; | |
| } | |
| else if (format == PaperFormat.A1) | |
| { | |
| return PuppeteerSharp.Media.PaperFormat.A1; | |
| } | |
| else if (format == PaperFormat.A2) | |
| { | |
| return PuppeteerSharp.Media.PaperFormat.A2; | |
| } | |
| else if (format == PaperFormat.A3) | |
| { | |
| return PuppeteerSharp.Media.PaperFormat.A3; | |
| } | |
| else if (format == PaperFormat.A4) | |
| { | |
| return PuppeteerSharp.Media.PaperFormat.A4; | |
| } | |
| else if (format == PaperFormat.A5) | |
| { | |
| return PuppeteerSharp.Media.PaperFormat.A5; | |
| } | |
| else if (format == PaperFormat.A6) | |
| { | |
| return PuppeteerSharp.Media.PaperFormat.A6; | |
| } | |
| else if (format == PaperFormat.Letter) | |
| { | |
| return PuppeteerSharp.Media.PaperFormat.Letter; | |
| } | |
| else if (format == PaperFormat.Legal) | |
| { | |
| return PuppeteerSharp.Media.PaperFormat.Legal; | |
| } | |
| else if (format == PaperFormat.Tabloid) | |
| { | |
| return PuppeteerSharp.Media.PaperFormat.Tabloid; | |
| } | |
| else if (format == PaperFormat.Ledger) | |
| { | |
| return PuppeteerSharp.Media.PaperFormat.Ledger; | |
| } | |
| else | |
| { | |
| return new PuppeteerSharp.Media.PaperFormat(format.Width, format.Height); | |
| } | |
| return format switch | |
| { | |
| PaperFormat.A0 => PuppeteerSharp.Media.PaperFormat.A0, | |
| PaperFormat.A1 => PuppeteerSharp.Media.PaperFormat.A1, | |
| PaperFormat.A2 => PuppeteerSharp.Media.PaperFormat.A2, | |
| PaperFormat.A3 => PuppeteerSharp.Media.PaperFormat.A3, | |
| PaperFormat.A4 => PuppeteerSharp.Media.PaperFormat.A4, | |
| PaperFormat.A5 => PuppeteerSharp.Media.PaperFormat.A5, | |
| PaperFormat.A6 => PuppeteerSharp.Media.PaperFormat.A6, | |
| PaperFormat.Letter => PuppeteerSharp.Media.PaperFormat.Letter, | |
| PaperFormat.Legal => PuppeteerSharp.Media.PaperFormat.Legal, | |
| PaperFormat.Tabloid => PuppeteerSharp.Media.PaperFormat.Tabloid, | |
| PaperFormat.Ledger => PuppeteerSharp.Media.PaperFormat.Ledger, | |
| _ => new PuppeteerSharp.Media.PaperFormat(format.Width, format.Height) | |
| }; |
Link issues
fixes #633
Summary By Copilot
Regression?
Risk
Verification
Packaging changes reviewed?
☑️ Self Check before Merge
Summary by Sourcery
Extend DefaultPdfService to support additional PDF settings including paper format, margins, background printing, scale, and header/footer for HTML-to-PDF conversion
New Features:
Enhancements: