garrytan / garrytan/gstack

make-pdf: spurious blank first page when content (frontmatter or leading <style>) precedes the first H1

Open
#1,904 3 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
133k
Forks
19.9k
Avg merge
18h 46m
Merged PRs (30d)
26

Description

## Bug

`make-pdf` emits a spurious blank/near-blank **first page** whenever the markdown has *any* content before the first `

`. Two common triggers:

1. **YAML frontmatter** — a leading `---\n...\n---` block is not parsed/stripped, so `marked` renders it as a literal paragraph of body text on its own page (`title: ... type: ... tags: [...]`).
2. **A leading top-of-file `` block** — even though it renders nothing visible, it still produces an empty first page.

## Root cause

In `make-pdf/src/render.ts`, `wrapChaptersByH1()` treats everything before the first `<h1>` as a **preamble chapter**:

```ts
const preamble = html.slice(0, matches[0]);
if (preamble.trim().length > 0) {
chunks.push(`<section class="chapter">${preamble}</section>`);
}
```

Combined with the chapter CSS:

```css
.chapter { break-before: page; page-break-before: always; }
.chapter:first-of-type { break-before: auto; page-break-before: auto; }
```

When a preamble chapter exists it becomes `:first-of-type`, so the first *real* `<h1>` chapter no longer gets the `break-before: auto` exception — it inherits `break-before: page` and starts on page 2. The preamble (frontmatter text, or an invisible `<style>` block) occupies page 1. `preamble.trim().length > 0` is true even when the preamble contains only non-rendering markup (`<style>…`), so an invisible preamble still forces the break.

## Repro

```bash
printf 'h1{color:#000}\n\n# Hello\n\nBody.\n' > t.md
pdf generate t.md t.pdf
pdfinfo t.pdf # => Pages: 2 (page 1 blank)

printf '# Hello\n\nh1{color:#000}\n\nBody.\n' > t2.md
pdf generate t2.md t2.pdf
pdfinfo t2.pdf # => Pages: 1 (correct)
```

## Suggested fix (either/both)

1. **Strip YAML frontmatter** before `marked.parse` — a leading `^---\n[\s\S]*?\n---\n?` block.
2. **Don't create a preamble chapter for non-rendering content** — strip `/<script>/<link>/<meta>` from the preamble slice before the `preamble.trim().length > 0` check (or hoist a leading `<style>` into `<head>`), so an invisible preamble doesn't force a page break / empty first page.

## Workaround

Strip frontmatter caller-side and inject the width-fit `<style>` block *after* the first heading rather than prepending it. Confirmed to produce the correct page count.

Contributor guide

Open the contributing guide

Research direction

Start in make-pdf/src/render.ts and inspect wrapChaptersByH1(), then reproduce the issue with the provided printf, pdf generate, and pdfinfo commands. Ensure frontmatter and non-rendering leading markup do not create a preamble chapter or blank first page, while content after the first heading still renders correctly; verify the affected examples produce one page.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
cli
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.