Skip to content

feat: HTML-to-PDF layout options and watermark positioning#13

Open
jdrhyne wants to merge 2 commits intomainfrom
feat/html-layout-watermark-positioning
Open

feat: HTML-to-PDF layout options and watermark positioning#13
jdrhyne wants to merge 2 commits intomainfrom
feat/html-layout-watermark-positioning

Conversation

@jdrhyne
Copy link
Contributor

@jdrhyne jdrhyne commented Feb 5, 2026

Summary

Adds schema enhancements for HTML-to-PDF conversion and watermark positioning.

Changes

HTML-to-PDF Layout Options:

  • PageSizePresetSchema — enum with A0-A8, Letter, Legal presets
  • HTMLLayoutSchema — object with orientation, page size, and margin options
  • layout field added to FilePartSchema for HTML input files

Watermark Positioning:

  • Enabled positioning offsets: top, right, bottom, left (using WatermarkDimensionSchema)
  • Enabled font customization: fontFamily, fontSize, fontStyle

Testing

  • Build compiles clean
  • All 43 existing tests pass

Split from #12 per review feedback to keep PRs focused and reviewable.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds schema enhancements to support HTML-to-PDF layout options and watermark positioning customization, extracted from the larger PR #12 for focused review. The changes enable fine-grained control over HTML-to-PDF conversion parameters and expand watermark customization capabilities beyond the basic centered positioning.

Changes:

  • Added HTML-to-PDF layout control: page orientation, size presets (A0-A8, Letter, Legal), custom dimensions, and margins
  • Enabled watermark positioning offsets (top, right, bottom, left) and font customization (family, size, style)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

src/schemas.ts Outdated
bottom: WatermarkDimensionSchema.optional().describe('Offset of the watermark from the bottom edge of a page.'),
left: WatermarkDimensionSchema.optional().describe('Offset of the watermark from the left edge of a page.'),
fontFamily: z.string().optional().describe('The font family to use for text watermarks.'),
fontSize: z.number().optional().describe('Font size in points for text watermarks.'),
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fontSize field should have validation to prevent non-positive values, as font sizes must be greater than zero. Consider adding .positive() or .min(1) validation to ensure valid font sizes.

Suggested change
fontSize: z.number().optional().describe('Font size in points for text watermarks.'),
fontSize: z.number().positive().optional().describe('Font size in points for text watermarks.'),

Copilot uses AI. Check for mistakes.
src/schemas.ts Outdated
Comment on lines +189 to +192
layout: HTMLLayoutSchema.describe(
'Page layout options for HTML-to-PDF conversion. Only applies when the input is an HTML file. ' +
'Supports orientation, page size, and margins.',
),
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new HTML layout options for FilePartSchema (orientation, page size, margins) are not covered by any tests. Consider adding test cases that exercise these new fields to ensure they work correctly with HTML-to-PDF conversion.

Copilot uses AI. Check for mistakes.
Comment on lines +267 to +276
top: WatermarkDimensionSchema.optional().describe('Offset of the watermark from the top edge of a page.'),
right: WatermarkDimensionSchema.optional().describe('Offset of the watermark from the right edge of a page.'),
bottom: WatermarkDimensionSchema.optional().describe('Offset of the watermark from the bottom edge of a page.'),
left: WatermarkDimensionSchema.optional().describe('Offset of the watermark from the left edge of a page.'),
fontFamily: z.string().optional().describe('The font family to use for text watermarks.'),
fontSize: z.number().optional().describe('Font size in points for text watermarks.'),
fontStyle: z
.array(z.enum(['bold', 'italic']))
.optional()
.describe('Font style for text watermarks. Can be italic, bold, or both.'),
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new watermark positioning fields (top, right, bottom, left) and font customization fields (fontFamily, fontSize, fontStyle) are not covered by any tests. Consider adding test cases that use these new fields to verify they work correctly with the watermark action.

Copilot uses AI. Check for mistakes.
src/schemas.ts Outdated
.string()
.optional()
.describe("Used to determine the file type when the file content type is not available and can't be inferred."),
layout: HTMLLayoutSchema.describe(
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The layout field should be optional since it only applies to HTML input files. Without marking it as optional, this field will be required for all file parts, including PDFs and other non-HTML formats. Add .optional() before .describe() to fix this issue, similar to how password, pages, and content_type fields are handled.

Suggested change
layout: HTMLLayoutSchema.describe(
layout: HTMLLayoutSchema.optional().describe(

Copilot uses AI. Check for mistakes.
src/schemas.ts Outdated
Comment on lines +167 to +170
left: z.number().describe('Left margin in millimeters.'),
top: z.number().describe('Top margin in millimeters.'),
right: z.number().describe('Right margin in millimeters.'),
bottom: z.number().describe('Bottom margin in millimeters.'),
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The margin fields (left, top, right, bottom) should have validation to prevent negative values, as negative margins don't make sense in the context of HTML-to-PDF conversion. Consider adding .min(0) validation to each margin field, similar to how opacity is validated on line 255.

Suggested change
left: z.number().describe('Left margin in millimeters.'),
top: z.number().describe('Top margin in millimeters.'),
right: z.number().describe('Right margin in millimeters.'),
bottom: z.number().describe('Bottom margin in millimeters.'),
left: z.number().min(0).describe('Left margin in millimeters.'),
top: z.number().min(0).describe('Top margin in millimeters.'),
right: z.number().min(0).describe('Right margin in millimeters.'),
bottom: z.number().min(0).describe('Bottom margin in millimeters.'),

Copilot uses AI. Check for mistakes.
src/schemas.ts Outdated
Comment on lines +159 to +160
width: z.number().describe('Page width in millimeters.'),
height: z.number().describe('Page height in millimeters.'),
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The custom page size dimensions (width and height) should have validation to prevent non-positive values, as page dimensions must be greater than zero. Consider adding .min(0) or .positive() validation to both width and height fields.

Suggested change
width: z.number().describe('Page width in millimeters.'),
height: z.number().describe('Page height in millimeters.'),
width: z
.number()
.positive()
.describe('Page width in millimeters.'),
height: z
.number()
.positive()
.describe('Page height in millimeters.'),

Copilot uses AI. Check for mistakes.
@jdrhyne
Copy link
Contributor Author

jdrhyne commented Feb 6, 2026

Addressed all 6 Copilot review comments in d1d1521:

Schema validations:

  1. ✅ Custom page size width/height — added .positive() validation
  2. ✅ Margin fields — added .min(0) to all four (left/top/right/bottom)
  3. layout on FilePartSchema — made .optional() (only applies to HTML input)
  4. fontSize on watermark — added .positive() validation

Tests added:
5. ✅ HTML layout tests — valid layout with orientation/size/margin, rejects negative margins, rejects zero page dimensions
6. ✅ Watermark positioning/font tests — valid positioning (top/right/bottom/left), font customization (fontFamily/fontSize/fontStyle), rejects invalid fontSize

Build clean, all tests passing (125 insertions, 9 deletions).

.optional()
.describe('Page margins in millimeters.'),
})
.optional()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: We can remove this optional. It's up to the consumer to decide if the schema is optional, i.e. FilePartSchema.

- Add PageSizePresetSchema (A0-A8, Letter, Legal)
- Add HTMLLayoutSchema with orientation, size, margin options
- Add layout field to FilePartSchema for HTML-to-PDF conversion
- Enable watermark positioning (top/right/bottom/left offsets)
- Enable font customization (fontFamily, fontSize, fontStyle)
@jdrhyne jdrhyne force-pushed the feat/html-layout-watermark-positioning branch from d1d1521 to 7df87f4 Compare February 7, 2026 21:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants