feat: add customizable page number format

This commit is contained in:
Walid 2026-07-10 00:58:12 +06:00
parent 40226d244f
commit e87cffe278
3 changed files with 29 additions and 315 deletions

View file

@ -33,7 +33,7 @@ If this plugin saves you time, consider **[supporting the project](https://shrek
- **Full layout control** — margins, font family/size/line height, paragraph spacing, heading scale, colors
- **Page frame** — optional border drawn around the outer edge of every page (solid, dashed, dotted, double, groove, or ridge)
- **Background** — solid color or a background image with fit, scope (full page or content area only), and opacity controls
- **Header & footer** — custom text, page numbers (X / Y), alignment, font size/color, optional banner image, and first-page suppression
- **Header & footer** — custom text, customizable page numbers (`{{page_current}}` / `{{page_total}}` template placeholders), alignment, font size/color, optional banner image, and first-page suppression
- **PDF outline (bookmarks)** — heading-based bookmark tree embedded in the exported PDF; most PDF readers display it in a side panel
## Screenshots
@ -187,6 +187,7 @@ All settings take effect after closing the settings panel.
| Image left/right margin | Insets the banner from the left and right page edges (px) |
| Show page numbers | Toggle *Page X / Y* display |
| Page number position | Left, Center, or Right |
| Page number format | Custom template using `{{page_current}}` and `{{page_total}}` placeholders, e.g. `Page {{page_current}} of {{page_total}}`, `{{page_current}}/{{page_total}}`, or just `{{page_current}}` (default: `{{page_current}} / {{page_total}}`) |
| Page number start | Number assigned to the first visible page number (default 1) |
### Behaviour

313
main.js

File diff suppressed because one or more lines are too long

28
main.ts
View file

@ -114,6 +114,9 @@ interface PDFExportSettings extends DocStyle {
showPageNumbers: boolean;
pageNumberPosition: "center" | "left" | "right";
pageNumberStart: number;
/** Template for rendering the page number string. Supports {{page_current}}
* and {{page_total}} placeholders, e.g. "Page {{page_current}} of {{page_total}}". */
pageNumberFormat: string;
showHeaderOnFirstPage: boolean;
showFooterOnFirstPage: boolean;
headerAlignment: "left" | "center" | "right";
@ -384,6 +387,7 @@ const DEFAULT_SETTINGS: PDFExportSettings = {
showPageNumbers: true,
pageNumberPosition: "right",
pageNumberStart: 1,
pageNumberFormat: "{{page_current}} / {{page_total}}",
showHeaderOnFirstPage: true,
showFooterOnFirstPage: true,
headerAlignment: "right",
@ -1654,6 +1658,16 @@ function paginateEl(
// ─── Page layout builder ──────────────────────────────────────────────────────
/** Resolves a page-number format template by substituting the {{page_current}}
* and {{page_total}} placeholders with the given values. Falls back to the
* default "current / total" template when the format is empty. */
function resolvePageNumberFormat(format: string, current: number, total: number): string {
const template = format && format.trim() ? format : "{{page_current}} / {{page_total}}";
return template
.replace(/\{\{\s*page_current\s*\}\}/g, String(current))
.replace(/\{\{\s*page_total\s*\}\}/g, String(total));
}
/** Converts paginated page-node arrays into fully-resolved PageLayout objects,
* computing header/footer text and page number strings for each page. */
function buildPageLayouts(allPages: HTMLElement[][], s: PDFExportSettings): PageLayout[] {
@ -1666,7 +1680,7 @@ function buildPageLayouts(allPages: HTMLElement[][], s: PDFExportSettings): Page
// Page-number offset: when footer is hidden on page 1 the numbering shifts by 1.
const displayNum = s.showFooterOnFirstPage ? s.pageNumberStart + i : s.pageNumberStart + (i - 1);
const displayTotal = s.showFooterOnFirstPage ? s.pageNumberStart + totalPages - 1 : s.pageNumberStart + totalPages - 2;
const numStr = `${displayNum} / ${displayTotal}`;
const numStr = resolvePageNumberFormat(s.pageNumberFormat, displayNum, displayTotal);
let footerLeft = "", footerRight = "", footerCenter = "";
let headerLeft = "", headerCenter = "", headerRight = "";
@ -1987,6 +2001,11 @@ export default class MarkdownPDFPlugin extends Plugin {
// Page-number start must be at least 1.
s.pageNumberStart = Math.max(1, s.pageNumberStart);
// Page-number format: fall back to default when cleared.
if (!s.pageNumberFormat || !s.pageNumberFormat.trim()) {
s.pageNumberFormat = DEFAULT_SETTINGS.pageNumberFormat;
}
}
async saveSettings() {
@ -3202,6 +3221,13 @@ class PDFExportSettingTab extends PluginSettingTab {
.setValue(s.pageNumberPosition)
.onChange((v) => { s.pageNumberPosition = v as "left"|"center"|"right"; void this.markDirty(); }),
);
new Setting(containerEl)
.setName("Page number format")
.setDesc("Use {{page_current}} and {{page_total}} as placeholders, e.g. \"Page {{page_current}} of {{page_total}}\", \"{{page_current}}/{{page_total}}\", or just \"{{page_current}}\".")
.addText((t) =>
t.setPlaceholder("{{page_current}} / {{page_total}}").setValue(s.pageNumberFormat)
.onChange((v) => { s.pageNumberFormat = v; void this.markDirty(); }),
);
new Setting(containerEl)
.setName("Page number start")
.setDesc("Number assigned to the first visible page number.")