@darialan/web-printer 0.0.0-stage → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +106 -2
  3. package/dist/index.cjs +2 -0
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.d.ts +1 -0
  6. package/dist/index.mjs +131 -0
  7. package/dist/index.mjs.map +1 -0
  8. package/dist/page-break-overlay-50796a2a.js +56 -0
  9. package/dist/page-break-overlay-50796a2a.js.map +1 -0
  10. package/dist/page-break-overlay-e5f1b145.mjs +1174 -0
  11. package/dist/page-break-overlay-e5f1b145.mjs.map +1 -0
  12. package/dist/react.cjs +2 -0
  13. package/dist/react.cjs.map +1 -0
  14. package/dist/react.mjs +60 -0
  15. package/dist/react.mjs.map +1 -0
  16. package/dist/types/adapters/canvas/index.d.ts +1 -0
  17. package/dist/types/adapters/canvas/page-capture.d.ts +6 -0
  18. package/dist/types/adapters/dom/index.d.ts +4 -0
  19. package/dist/types/adapters/dom/measurement.d.ts +4 -0
  20. package/dist/types/adapters/dom/snapshot.d.ts +20 -0
  21. package/dist/types/adapters/dom/text-range.d.ts +4 -0
  22. package/dist/types/adapters/dom/traversal.d.ts +3 -0
  23. package/dist/types/adapters/index.d.ts +4 -0
  24. package/dist/types/adapters/pdf/index.d.ts +1 -0
  25. package/dist/types/adapters/pdf/jspdf-writer.d.ts +15 -0
  26. package/dist/types/adapters/resources/index.d.ts +1 -0
  27. package/dist/types/adapters/resources/resource-loader.d.ts +2 -0
  28. package/dist/types/application/cancellation.d.ts +1 -0
  29. package/dist/types/application/index.d.ts +4 -0
  30. package/dist/types/application/print-pipeline.d.ts +53 -0
  31. package/dist/types/application/print-service.d.ts +55 -0
  32. package/dist/types/application/print-session.d.ts +17 -0
  33. package/dist/types/core/geometry/index.d.ts +2 -0
  34. package/dist/types/core/geometry/page-geometry.d.ts +18 -0
  35. package/dist/types/core/geometry/paper.d.ts +83 -0
  36. package/dist/types/core/index.d.ts +4 -0
  37. package/dist/types/core/pagination/index.d.ts +1 -0
  38. package/dist/types/core/pagination/paginator.d.ts +6 -0
  39. package/dist/types/core/rules/index.d.ts +1 -0
  40. package/dist/types/core/rules/print-rules.d.ts +80 -0
  41. package/dist/types/core/types.d.ts +77 -0
  42. package/dist/types/hooks/index.d.ts +3 -0
  43. package/dist/types/hooks/use-pdf-print.d.ts +6 -0
  44. package/dist/types/hooks/use-web-printer.d.ts +46 -0
  45. package/dist/types/index.d.ts +19 -0
  46. package/dist/types/output/image-writer.d.ts +5 -0
  47. package/dist/types/output/index.d.ts +4 -0
  48. package/dist/types/output/page-number.d.ts +1 -0
  49. package/dist/types/output/types.d.ts +17 -0
  50. package/dist/types/output/zip.d.ts +5 -0
  51. package/dist/types/overlay/page-break-overlay.d.ts +8 -0
  52. package/dist/types/react.d.ts +2 -0
  53. package/docs/migration-to-web-printer.md +42 -0
  54. package/docs/output-formats.md +47 -0
  55. package/package.json +140 -4
@@ -0,0 +1,55 @@
1
+ import { OutputFormat } from '../output';
2
+ import { Interval, MeasurementStats, PageSlice } from '../core/types';
3
+ import { LayoutOptions, PreparedLayout } from './print-pipeline';
4
+ export interface ExportOptions extends LayoutOptions {
5
+ filename: string;
6
+ download?: boolean;
7
+ onProgress?: (completed: number, total: number) => void;
8
+ }
9
+ export interface DocumentExportOptions extends ExportOptions {
10
+ format?: OutputFormat;
11
+ }
12
+ export interface PreviewPaginationOptions extends Omit<LayoutOptions, 'element'> {
13
+ contentElement: HTMLElement | null;
14
+ }
15
+ export interface PrintInspectionResult {
16
+ pageBreaks: number[];
17
+ pages: PageSlice[];
18
+ totalHeight: number;
19
+ capacity: number;
20
+ protectedRanges: Interval[];
21
+ stats?: MeasurementStats;
22
+ timing: {
23
+ measurementMs: number;
24
+ paginationMs: number;
25
+ totalMs: number;
26
+ };
27
+ }
28
+ /** Share snapshot, pagination and page capture across all output formats. */
29
+ export declare function outputDocument({ format, filename, download, onProgress, ...options }: DocumentExportOptions): Promise<{
30
+ blob: Blob;
31
+ pages: number;
32
+ layoutMs: number;
33
+ totalMs: number;
34
+ bytes: number;
35
+ }>;
36
+ /** Export an already frozen layout. The session owns its cleanup. */
37
+ export declare function exportPreparedLayout(layout: PreparedLayout, options: Omit<DocumentExportOptions, 'element'>, layoutMs?: number): Promise<{
38
+ blob: Blob;
39
+ pages: number;
40
+ layoutMs: number;
41
+ totalMs: number;
42
+ bytes: number;
43
+ }>;
44
+ /** Backward-compatible PDF entry point. */
45
+ export declare function outputPDF(options: ExportOptions): Promise<{
46
+ blob: Blob;
47
+ pages: number;
48
+ layoutMs: number;
49
+ totalMs: number;
50
+ bytes: number;
51
+ }>;
52
+ /** Run full snapshot measurement and pagination validation, returning structured diagnostics. */
53
+ export declare function inspectPrintLayout({ contentElement, ...options }: PreviewPaginationOptions): Promise<PrintInspectionResult>;
54
+ /** Uses the exact snapshot measurement and page planner used by PDF export, with coordinates relative to the prepared print snapshot. */
55
+ export declare function calculatePagesArray(options: PreviewPaginationOptions): Promise<number[]>;
@@ -0,0 +1,17 @@
1
+ import { LayoutOptions } from './print-pipeline';
2
+ import { DocumentExportOptions } from './print-service';
3
+ /** A frozen document shared by preview, inspection and all output formats. */
4
+ export declare function createPrintSession(options: LayoutOptions): Promise<{
5
+ layout: any;
6
+ preparedMs: number;
7
+ thumbnail(index: number, maxWidth?: number): Promise<string>;
8
+ exportDocument(params: Omit<DocumentExportOptions, 'element' | 'settings'>): Promise<{
9
+ blob: Blob;
10
+ pages: number;
11
+ layoutMs: number;
12
+ totalMs: number;
13
+ bytes: number;
14
+ }>;
15
+ dispose(): void;
16
+ }>;
17
+ export type PrintSession = Awaited<ReturnType<typeof createPrintSession>>;
@@ -0,0 +1,2 @@
1
+ export * from './paper';
2
+ export * from './page-geometry';
@@ -0,0 +1,18 @@
1
+ import { ResolvedPaper } from './paper';
2
+ import { PageNumberConfig } from '../types';
3
+ export interface PageGeometry {
4
+ pageWidth: number;
5
+ pageHeight: number;
6
+ contentWidth: number;
7
+ ratio: number;
8
+ headerHeight: number;
9
+ footerHeight: number;
10
+ bodyTop: number;
11
+ left: number;
12
+ headerY: number;
13
+ footerY: number;
14
+ pageNumberY: number;
15
+ capacity: number;
16
+ }
17
+ /** Paper units (pt) and DOM units (CSS px) meet only at this boundary. */
18
+ export declare function calculateGeometry(paper: ResolvedPaper, width: number, headerPx: number | undefined, footerPx: number | undefined, pageNumber?: boolean | PageNumberConfig): PageGeometry;
@@ -0,0 +1,83 @@
1
+ import { Orientation, Paper, PrintSettings } from '../types';
2
+ export type { Orientation, Paper, PrintSettings } from '../types';
3
+ export declare const PT_PER_MM: number;
4
+ export declare const MM_PER_PX: number;
5
+ export declare const PX_PER_MM: number;
6
+ /** 社媒 / 图片画框:以像素定义,边距按画框宽度的内边距给默认值 */
7
+ export declare const IMAGE_PAPER_SIZES: {
8
+ readonly 'card-3x4': {
9
+ readonly widthPx: 1242;
10
+ readonly heightPx: 1656;
11
+ readonly marginPx: 60;
12
+ };
13
+ readonly 'square-1x1': {
14
+ readonly widthPx: 1080;
15
+ readonly heightPx: 1080;
16
+ readonly marginPx: 54;
17
+ };
18
+ readonly 'card-4x5': {
19
+ readonly widthPx: 1080;
20
+ readonly heightPx: 1350;
21
+ readonly marginPx: 54;
22
+ };
23
+ readonly 'story-9x16': {
24
+ readonly widthPx: 1080;
25
+ readonly heightPx: 1920;
26
+ readonly marginPx: 54;
27
+ };
28
+ readonly 'ecom-750x1000': {
29
+ readonly widthPx: 750;
30
+ readonly heightPx: 1000;
31
+ readonly marginPx: 0;
32
+ };
33
+ };
34
+ export type ImagePaper = keyof typeof IMAGE_PAPER_SIZES;
35
+ export declare const IMAGE_PAPERS: ("card-3x4" | "square-1x1" | "card-4x5" | "story-9x16" | "ecom-750x1000")[];
36
+ export declare function isImagePaper(paper: Paper): paper is ImagePaper;
37
+ /** 预设自带的自然单位:纸张用 mm,社媒图片用 px,选中时自动切换,避免出现 328.61 mm 这种数字 */
38
+ export declare function getPresetUnit(paper: Paper): 'mm' | 'px';
39
+ export declare const PAPER_SIZES: Record<Exclude<Paper, 'custom'>, [number, number]>;
40
+ export interface PaperPreset {
41
+ widthMm: number;
42
+ heightMm: number;
43
+ marginTopMm: number;
44
+ marginRightMm: number;
45
+ marginBottomMm: number;
46
+ marginLeftMm: number;
47
+ }
48
+ /**
49
+ * 各纸张预设标准(基准存储单位:mm):
50
+ * - A4、A3、A5、Letter 具有固定的标准物理宽高与统一默认页边距(10mm)
51
+ * - 自定义尺寸(custom):
52
+ * - 宽默认为 A4 纸宽度:210 mm (96 DPI 下对应 793.7 px)
53
+ * - 高度默认为 200 px (对应 200 * 25.4 / 96 ≈ 52.92 mm)
54
+ * - 上下左右边距默认为 2 px (对应 2 * 25.4 / 96 ≈ 0.53 mm)
55
+ */
56
+ export declare const PAPER_PRESETS: Record<Paper, PaperPreset>;
57
+ export declare const DEFAULT_SETTINGS: PrintSettings;
58
+ /**
59
+ * 根据纸张类型、纸张方向以及目标单位获取联动标准数值
60
+ */
61
+ export declare function getPresetDimensions(paper: Paper, orientation?: Orientation, unit?: 'mm' | 'px'): {
62
+ width: number;
63
+ height: number;
64
+ marginTop: number;
65
+ marginRight: number;
66
+ marginBottom: number;
67
+ marginLeft: number;
68
+ };
69
+ export declare function mmToPt(mm: number): number;
70
+ export declare function resolvePaper(settings: PrintSettings): {
71
+ pageWidth: number;
72
+ pageHeight: number;
73
+ left: number;
74
+ right: number;
75
+ top: number;
76
+ bottom: number;
77
+ widthMm: number;
78
+ heightMm: number;
79
+ };
80
+ export type ResolvedPaper = ReturnType<typeof resolvePaper>;
81
+ export declare const UNIT_FIELDS: readonly ["widthMm", "heightMm", "marginTopMm", "marginRightMm", "marginBottomMm", "marginLeftMm"];
82
+ /** Storage stays in mm; px means CSS pixels at 96 DPI, independent of devicePixelRatio. */
83
+ export declare function convertFields<T extends PrintSettings>(value: T, factor: number): T;
@@ -0,0 +1,4 @@
1
+ export * from './types';
2
+ export * from './rules';
3
+ export * from './pagination';
4
+ export * from './geometry';
@@ -0,0 +1 @@
1
+ export * from './paginator';
@@ -0,0 +1,6 @@
1
+ import { Interval, PageSlice } from '../types';
2
+ export type { Interval, PageSlice } from '../types';
3
+ /** CSS-pixel intervals relative to the print root. No DOM or UI dependencies. */
4
+ export declare function paginate(height: number, capacity: number, protectedRanges: Interval[], maxPages?: number): PageSlice[];
5
+ /** Verify that planned page breaks do not cut through protected intervals. */
6
+ export declare function validatePageBreaks(pages: PageSlice[], ranges: Interval[], tolerance?: number): void;
@@ -0,0 +1 @@
1
+ export * from './print-rules';
@@ -0,0 +1,80 @@
1
+ import { ElementRule, PrintRule, PrintRuleConfig } from '../types';
2
+ export type { ElementRule, PrintRule, PrintRuleConfig } from '../types';
3
+ export interface ClassifiableNode {
4
+ nodeType: number;
5
+ textContent?: string | null;
6
+ matches?: (selector: string) => boolean;
7
+ }
8
+ /**
9
+ * 打印系统三大标准属性协议 (Data Attributes & Classes)
10
+ * 纯原生技术无关的标准协议,业务方自写组件或页面时直接绑定:
11
+ * 1. atomic: 最小原子单元,不可分割,跨页整块推移
12
+ * 2. splittable: 可分割流式内容,允许分页线从中穿透
13
+ * 3. ignore: 不打印,完全排除在打印纸张外
14
+ */
15
+ export declare const PRINT_ATTRIBUTES: {
16
+ /** 不可分割:最小原子化单元,严禁跨页切断,当前页放不下整块换到下一页 */
17
+ readonly ATOMIC: "data-print-atomic=\"true\"";
18
+ /** 可分割:声明为流式内容,允许分页线直接穿透内部 */
19
+ readonly SPLITTABLE: "data-print-splittable=\"true\"";
20
+ /** 打印忽略:不参与测量、快照与打印渲染 */
21
+ readonly IGNORE: "data-print-ignore=\"true\"";
22
+ readonly KEEP_TOGETHER: "data-print-keep-together=\"true\"";
23
+ readonly KEEP_OR_SPLIT: "data-print-keep-together=\"split\"";
24
+ readonly FORCE_SPLIT: "data-print-break=\"split\"";
25
+ };
26
+ export declare const PRINT_CLASSES: {
27
+ readonly ATOMIC: "print-atomic";
28
+ readonly SPLITTABLE: "print-splittable";
29
+ readonly IGNORE: "print-ignore";
30
+ readonly KEEP_TOGETHER: "print-keep-together";
31
+ readonly KEEP_OR_SPLIT: "print-keep-or-split";
32
+ readonly FORCE_SPLIT: "print-force-split";
33
+ };
34
+ /** @deprecated Use PRINT_ATTRIBUTES. Legacy attributes remain accepted. */
35
+ export declare const PDF_PRINT_ATTRIBUTES: {
36
+ readonly ATOMIC: "data-pdf-atomic=\"true\"";
37
+ readonly SPLITTABLE: "data-pdf-splittable=\"true\"";
38
+ readonly IGNORE: "data-pdf-ignore=\"true\"";
39
+ readonly KEEP_TOGETHER: "data-pdf-keep-together=\"true\"";
40
+ readonly KEEP_OR_SPLIT: "data-pdf-keep-together=\"split\"";
41
+ readonly FORCE_SPLIT: "data-pdf-break=\"split\"";
42
+ };
43
+ /** @deprecated Use PRINT_CLASSES. Legacy classes remain accepted. */
44
+ export declare const PDF_PRINT_CLASSES: {
45
+ readonly ATOMIC: "pdf-atomic";
46
+ readonly SPLITTABLE: "pdf-splittable";
47
+ readonly IGNORE: "pdf-ignore";
48
+ readonly KEEP_TOGETHER: "pdf-keep-together";
49
+ readonly KEEP_OR_SPLIT: "pdf-keep-or-split";
50
+ readonly FORCE_SPLIT: "pdf-force-split";
51
+ };
52
+ /**
53
+ * 核心基础规则(BASE_PRINT_RULES)
54
+ * 遵循技术无关、纯原生标准原则:只包含 HTML 标准元素与通用打印协议,绝不硬编码任何具体前端组件库类名
55
+ */
56
+ export declare const BASE_PRINT_RULES: readonly PrintRule[];
57
+ export declare const DEFAULT_PRINT_RULES: readonly PrintRule[];
58
+ /**
59
+ * 根据基础规则与配置构造最终生效的规则列表
60
+ */
61
+ export declare function buildPrintRules(config?: PrintRuleConfig): PrintRule[];
62
+ /**
63
+ * 全局统一配置打印规则(业务层通过此处注册各自技术栈/组件库的选择器)
64
+ */
65
+ export declare function configurePrint(config: PrintRuleConfig): void;
66
+ /**
67
+ * 获取当前全局生效的打印规则列表
68
+ */
69
+ export declare function getPrintRules(): readonly PrintRule[];
70
+ /**
71
+ * 重置全局规则为原生基础规则
72
+ */
73
+ export declare function resetPrintRules(): void;
74
+ /**
75
+ * 对节点进行分类匹配,首个匹配的选择器生效
76
+ */
77
+ export declare function classifyPrintNode(node: ClassifiableNode, rules?: readonly PrintRule[]): ElementRule;
78
+ export declare function resolveAtomicOverflow(rule: Extract<ElementRule, {
79
+ kind: 'atomic';
80
+ }>, height: number, capacity: number, customLabel?: string | null): 'protect' | 'descend';
@@ -0,0 +1,77 @@
1
+ /** Geometry and rule types shared by pure core algorithms and external adapters. */
2
+ export interface Interval {
3
+ top: number;
4
+ bottom: number;
5
+ }
6
+ export interface PageSlice {
7
+ start: number;
8
+ end: number;
9
+ }
10
+ export type Orientation = 'portrait' | 'landscape';
11
+ export type Paper = 'a4' | 'a3' | 'a5' | 'letter' | 'card-3x4' | 'square-1x1' | 'card-4x5' | 'story-9x16' | 'ecom-750x1000' | 'custom';
12
+ /** 声明式页眉/页脚基础配置(开发者代码配置) */
13
+ export interface DeclarativeHeaderFooterConfig {
14
+ enabled?: boolean;
15
+ text?: string;
16
+ align?: 'left' | 'center' | 'right';
17
+ showDivider?: boolean;
18
+ }
19
+ /** 独立页码系统配置(可由设置面板 UI 控制,亦可代码配置) */
20
+ export interface PageNumberConfig {
21
+ enabled: boolean;
22
+ position?: 'bottom-right' | 'bottom-center' | 'top-right' | 'top-center';
23
+ format?: 'slash' | 'chinese' | 'hyphen';
24
+ }
25
+ export interface PrintSettings {
26
+ paper: Paper;
27
+ unit?: 'mm' | 'px';
28
+ widthMm: number;
29
+ heightMm: number;
30
+ orientation: Orientation;
31
+ marginTopMm: number;
32
+ marginRightMm: number;
33
+ marginBottomMm: number;
34
+ marginLeftMm: number;
35
+ header?: DeclarativeHeaderFooterConfig;
36
+ footer?: DeclarativeHeaderFooterConfig;
37
+ pageNumber?: PageNumberConfig;
38
+ }
39
+ export type ElementRule = {
40
+ kind: 'ignore';
41
+ } | {
42
+ kind: 'container';
43
+ } | {
44
+ kind: 'text';
45
+ } | {
46
+ kind: 'atomic';
47
+ label: string;
48
+ overflow: 'split' | 'error';
49
+ };
50
+ export interface PrintRule {
51
+ selector: string;
52
+ result: ElementRule;
53
+ }
54
+ export interface PrintRuleConfig {
55
+ /** 不可分割的选择器列表(最小原子化单元,严禁中间切断,当前页放不下则整块推入下一页) */
56
+ atomic?: string[];
57
+ /** 可分割的选择器列表(流式文本、段落容器,允许被物理分页线切断) */
58
+ splittable?: string[];
59
+ /** 不打印的选择器列表(排除在打印渲染之外的交互控件,如分页器、操作按钮) */
60
+ ignore?: string[];
61
+ keepTogether?: string[];
62
+ keepOrSplit?: string[];
63
+ forceSplit?: string[];
64
+ /** 自定义高级规则列表(前置优先匹配) */
65
+ customRules?: PrintRule[];
66
+ }
67
+ export interface MeasurementStats {
68
+ visitedNodes: number;
69
+ atomicElements: number;
70
+ textNodes: number;
71
+ textFragments: number;
72
+ }
73
+ export interface DocumentMeasurement {
74
+ height: number;
75
+ ranges: Interval[];
76
+ stats?: MeasurementStats;
77
+ }
@@ -0,0 +1,3 @@
1
+ export * from './use-web-printer';
2
+ export { usePdfPrint } from './use-pdf-print';
3
+ export type { UsePdfPrintState, UsePdfPrintProgress } from './use-pdf-print';
@@ -0,0 +1,6 @@
1
+ /** @deprecated Import useWebPrinter from @darialan/web-printer/react. */
2
+ export { useWebPrinter as usePdfPrint } from './use-web-printer';
3
+ export type { UseWebPrinterProgress as UsePdfPrintProgress, ExportPdfParams, InspectLayoutParams } from './use-web-printer';
4
+ export type UsePdfPrintState = Omit<import('./use-web-printer').UseWebPrinterState, 'isExporting'> & {
5
+ isPrinting: boolean;
6
+ };
@@ -0,0 +1,46 @@
1
+ import { DocumentExportOptions, ExportOptions, PreviewPaginationOptions, PrintInspectionResult } from '../application/print-service';
2
+ export interface UseWebPrinterProgress {
3
+ completed: number;
4
+ total: number;
5
+ percent: number;
6
+ }
7
+ export interface UseWebPrinterState {
8
+ isExporting: boolean;
9
+ isInspecting: boolean;
10
+ progress: UseWebPrinterProgress;
11
+ error: Error | null;
12
+ inspection: PrintInspectionResult | null;
13
+ }
14
+ export type ExportDocumentParams = Omit<DocumentExportOptions, 'signal' | 'onProgress'>;
15
+ export interface ExportPdfParams extends Omit<ExportOptions, 'signal' | 'onProgress'> {
16
+ showOverlay?: boolean;
17
+ }
18
+ export interface InspectLayoutParams extends PreviewPaginationOptions {
19
+ showOverlay?: boolean;
20
+ }
21
+ /** React adapter for all output formats. The core package does not load React. */
22
+ export declare function useWebPrinter(): {
23
+ isPrinting: boolean;
24
+ exportDocument: (params: ExportDocumentParams) => Promise<{
25
+ blob: Blob;
26
+ pages: number;
27
+ layoutMs: number;
28
+ totalMs: number;
29
+ bytes: number;
30
+ }>;
31
+ exportPDF: (params: ExportPdfParams) => Promise<{
32
+ blob: Blob;
33
+ pages: number;
34
+ layoutMs: number;
35
+ totalMs: number;
36
+ bytes: number;
37
+ }>;
38
+ inspectLayout: ({ showOverlay, ...params }: InspectLayoutParams) => Promise<PrintInspectionResult>;
39
+ clearVisualOverlay: () => void;
40
+ cancel: () => void;
41
+ isExporting: boolean;
42
+ isInspecting: boolean;
43
+ progress: UseWebPrinterProgress;
44
+ error: Error | null;
45
+ inspection: PrintInspectionResult | null;
46
+ };
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Web Printer (@darialan/web-printer)
3
+ * High-performance, pixel-accurate client-side HTML pagination engine with PDF and PNG output.
4
+ */
5
+ export * from './core/types';
6
+ export * from './core/geometry';
7
+ export * from './core/pagination';
8
+ export * from './core/rules';
9
+ export * from './adapters/dom';
10
+ export * from './adapters/resources';
11
+ export * from './adapters/canvas';
12
+ export * from './adapters/pdf';
13
+ export * from './application/cancellation';
14
+ export * from './application/print-pipeline';
15
+ export * from './application/print-service';
16
+ export { visualizePageBreaks, clearPageBreaks, } from './overlay/page-break-overlay';
17
+ export type { ObservabilityStats } from './overlay/page-break-overlay';
18
+ export * from './output';
19
+ export * from "./application/print-session";
@@ -0,0 +1,5 @@
1
+ import { OutputWriter, OutputWriterOptions } from './types';
2
+ /** Same paper composition for PNG files and extension thumbnails. Caller releases the canvas. */
3
+ export declare function composePageCanvas(body: HTMLCanvasElement, heightPt: number, index: number, { geometry: g, settings, headerCanvas, footerCanvas, totalPages }: OutputWriterOptions): HTMLCanvasElement;
4
+ /** Compose full paper pages, encode each immediately, and release its canvas before the next page. */
5
+ export declare function createImageWriter({ geometry: g, settings, headerCanvas, footerCanvas, totalPages, signal }: OutputWriterOptions): OutputWriter;
@@ -0,0 +1,4 @@
1
+ import { OutputFormat, OutputWriter, OutputWriterOptions } from './types';
2
+ export * from './types';
3
+ export declare function validateOutputFormat(format: OutputFormat): void;
4
+ export declare function createOutputWriter(format: OutputFormat, options: OutputWriterOptions): Promise<OutputWriter> | OutputWriter;
@@ -0,0 +1 @@
1
+ export declare function formatPageNumber(pageIndex: number, totalPages: number, format?: 'slash' | 'chinese' | 'hyphen'): string;
@@ -0,0 +1,17 @@
1
+ import { PageGeometry } from '../core/geometry/page-geometry';
2
+ import { PrintSettings } from '../core/types';
3
+ export type OutputFormat = 'pdf' | 'png';
4
+ /** Writers consume the same rendered page slices; pagination is format-independent. */
5
+ export interface OutputWriterOptions {
6
+ geometry: PageGeometry;
7
+ settings?: Partial<PrintSettings>;
8
+ headerCanvas?: HTMLCanvasElement;
9
+ footerCanvas?: HTMLCanvasElement;
10
+ totalPages: number;
11
+ signal?: AbortSignal;
12
+ }
13
+ export interface OutputWriter {
14
+ addPageSlice(index: number, canvas: HTMLCanvasElement, heightPt: number): void | Promise<void>;
15
+ finalize(filename: string, download?: boolean): Promise<Blob>;
16
+ dispose?(): void;
17
+ }
@@ -0,0 +1,5 @@
1
+ /** ZIP STORE: PNG is already compressed. Keeping entries as Blobs avoids copying image buffers. */
2
+ export declare function createZipArchive(entries: {
3
+ name: string;
4
+ data: Uint8Array;
5
+ }[]): Blob;
@@ -0,0 +1,8 @@
1
+ import { PrintRule } from '../core/types';
2
+ export interface ObservabilityStats {
3
+ atomic: number;
4
+ splittable: number;
5
+ ignore: number;
6
+ }
7
+ export declare function clearPageBreaks(owner?: Document | HTMLElement): void;
8
+ export declare function visualizePageBreaks(pages: number[], element: HTMLElement, rules?: readonly PrintRule[]): ObservabilityStats;
@@ -0,0 +1,2 @@
1
+ /** Optional React integration. Install React in the host application. */
2
+ export * from './hooks';
@@ -0,0 +1,42 @@
1
+ # 从 WebPDF Printer 迁移至 Web Printer
2
+
3
+ 新品牌为 **Web Printer**;新包为 `@darialan/web-printer`。原 `@darialan/web-pdf-printer@1.0.2` 保持可下载。
4
+
5
+ ```sh
6
+ npm uninstall @darialan/web-pdf-printer
7
+ npm install @darialan/web-printer
8
+ ```
9
+
10
+ ## 导出 API
11
+
12
+ `outputDocument({ format: 'pdf' | 'png' })` 是通用接口。`outputPDF()` 仍是仅输出 PDF 的兼容接口;真正与 PDF 格式有关的名称不作无意义替换。
13
+
14
+ ```ts
15
+ import { outputDocument, outputPDF } from '@darialan/web-printer';
16
+ await outputDocument({ element: document.getElementById('content'), filename: 'report', format: 'png' });
17
+ ```
18
+
19
+ React 集成改从独立入口导入,保证普通 JS/Vue 项目导入核心库时不必安装 React:
20
+
21
+ ```tsx
22
+ import { useWebPrinter } from '@darialan/web-printer/react';
23
+ const { exportDocument, isExporting, progress, cancel } = useWebPrinter();
24
+ await exportDocument({ element: document.getElementById('content'), filename: 'report', format: 'png' });
25
+ ```
26
+
27
+ 旧 Hook 名称 `usePdfPrint`、`UsePdfPrintState`、`UsePdfPrintProgress` 在 `/react` 子入口保留;返回值仍有 `exportPDF`、`isPrinting`。不要从新包根入口导入 React Hook。旧包自己的入口不变。
28
+
29
+ ## HTML 标记
30
+
31
+ | 推荐 | 仍可识别的旧标记 |
32
+ | --- | --- |
33
+ | `data-print-atomic="true"` / `.print-atomic` | `data-pdf-atomic="true"` / `.pdf-atomic` |
34
+ | `data-print-splittable="true"` / `.print-splittable` | `data-pdf-splittable="true"` / `.pdf-splittable` |
35
+ | `data-print-ignore="true"` / `.print-ignore` | `data-pdf-ignore="true"` / `.pdf-ignore` |
36
+ | `data-print-keep-together="true"` / `.print-keep-together` | 对应 `data-pdf-*` / `.pdf-*` |
37
+ | `data-print-keep-together="split"` / `.print-keep-or-split` | 对应旧属性 / `.pdf-keep-or-split` |
38
+ | `data-print-break="split"` / `.print-force-split` | 对应旧属性 / `.pdf-force-split` |
39
+
40
+ 推荐常量为 `PRINT_ATTRIBUTES`、`PRINT_CLASSES`;`PDF_PRINT_ATTRIBUTES`、`PDF_PRINT_CLASSES` 保留旧值。业务选择器配置优先级与分页算法不变。检查事件使用 `print-inspection`;旧 `pdf-print-inspection` 事件继续发送。内部快照/调试属性改为 `data-print-snapshot` / `data-print-debug`,它们属于内部实现,不应由业务代码生成。
41
+
42
+ GitHub 源码仓库和现有站点 URL 保持稳定;产品名称与 npm 安装入口已更名。工作区目录和 Vercel 项目 ID 属于部署基础设施,不改变内容或下载能力。
@@ -0,0 +1,47 @@
1
+ # 分页输出层:PDF 与 PNG
2
+
3
+ 以当前四个 playground 场景为基础。分页是核心能力,PDF 和分页图片是同一份分页结果的两种输出;本次不调整测量、保护规则或分页算法。
4
+
5
+ ## 使用
6
+
7
+ 工作台通过导出按钮右侧的箭头选择格式,按钮主体直接导出当前格式。默认 PDF;「PNG 分页图片」将每一页保存为一张完整纸张图片,并统一打包下载,避免多文件自动下载被浏览器拦截。文件按 `page-001.png`、`page-002.png` 排序。图片包含当前纸张尺寸、方向、边距、页眉页脚与页码,末页保留完整纸张高度。切换输出格式不改变分页或关闭预览。输出设置提供 A4/A3/A5/Letter、常见社媒图片比例(3:4、4:5、1:1、9:16)、电商长图和自定义尺寸;社媒图片预设使用像素单位,页面宽高和内边距仍统一换算为毫米保存。设置区同时显示扣除边距后的正文可用尺寸。
8
+
9
+ ```ts
10
+ import { outputDocument, outputPDF } from '@darialan/web-printer';
11
+
12
+ const controller = new AbortController();
13
+ const result = await outputDocument({
14
+ element: document.getElementById('content'),
15
+ filename: '报告',
16
+ format: 'png', // 'pdf' | 'png',默认 'pdf'
17
+ download: false, // true 时直接下载;false 时由调用方处理 Blob
18
+ signal: controller.signal,
19
+ onProgress: (completed, total) => console.log(completed, total),
20
+ });
21
+ // PNG 模式:result.blob 是 application/zip,内含逐页 PNG。
22
+ // PDF 模式:result.blob 是 application/pdf。
23
+ // result.pages / bytes / layoutMs / totalMs 两种格式均可用。
24
+
25
+ // 原有 PDF 调用保持兼容:
26
+ await outputPDF({ element: document.getElementById('content'), filename: '报告' });
27
+ ```
28
+
29
+ 图片采用与 PDF 正文相同的逐页截图,图片尺寸取决于源区域宽度及截图像素预算,纸张宽高比来自输出设置;不是额外设置的固定 DPI。PNG 是无损光栅图片。页内不含可搜索的文本层。
30
+
31
+ ## 模块边界
32
+
33
+ - `application/print-service.ts`:共享 prepareLayout、逐页 capture、进度、取消和资源清理。
34
+ - `output/types.ts`:输出格式与 `OutputWriter` 协议。writer 接收页截图与纸张坐标,不测量 DOM、不重新分页。
35
+ - `output/index.ts`:格式分发。PDF 使用原有 jsPDF adapter;PNG 使用 image writer。
36
+ - `output/image-writer.ts`:按共享 geometry 合成完整纸张,逐页编码 PNG 后立即释放合成 canvas。
37
+ - `output/zip.ts`:PNG 已压缩,ZIP 使用 STORE,无新增运行时依赖;写入 CRC32 与中央目录。页码文件名固定 ASCII,文档标题仅用于下载包名称。
38
+
39
+ 扩展格式时实现 `OutputWriter`,注册到格式工厂,并增加工具栏选项即可;分页算法、快照和截图链路不需要增加格式判断。React 项目可从 `@darialan/web-printer/react` 导入 `useWebPrinter()`,用 `exportDocument()` 选择格式;`usePdfPrint().exportPDF()` 作为兼容接口保留。非 React 和 React 调用方均可直接调用核心入口的 `outputDocument()`。
40
+
41
+ ## 失败与取消
42
+
43
+ 任一页截图或编码失败,整个任务失败,不下载不完整文件。导出前及每页处理后检查取消状态;编码中的原生 canvas 操作结束后停止,不开始下一页或最终下载。所有路径释放快照、截图 canvas 和输出 writer。PNG 字节保留到 ZIP 完成,因此总内存仍随压缩图片总量增长;整篇正文不会合成一张超长 canvas。ZIP 不支持 ZIP64,超出标准 ZIP 容量会明确报错。
44
+
45
+ ## 验证
46
+
47
+ 单测覆盖 ZIP CRC32/目录偏移、纸张合成坐标、页眉页脚与页码、编码失败/取消清理和共享导出链路。浏览器自动化覆盖四个场景的真实 ZIP 下载,逐页解码 PNG、检查非空像素、纸张比例、连续文件名,以及与预览一致的页数;原有 PDF 自动化作为回归继续执行。