takumi-pdf 0.4.1 → 0.5.0

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.
package/dist/export.cjs CHANGED
@@ -2,8 +2,10 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
+ let _takumi_rs_helpers_html = require("@takumi-rs/helpers/html");
5
6
  let _takumi_rs_helpers_jsx = require("@takumi-rs/helpers/jsx");
6
7
  let _takumi_rs_helpers_renderer = require("@takumi-rs/helpers/renderer");
8
+ let _takumi_rs_helpers = require("@takumi-rs/helpers");
7
9
  //#region pkg/takumi_pdf_wasm.js
8
10
  /**
9
11
  * A PDF renderer holding registered fonts and a decoded-resource cache.
@@ -496,28 +498,40 @@ async function __wbg_init(module_or_path) {
496
498
  }
497
499
  //#endregion
498
500
  //#region src/export.ts
501
+ /** Every character a page counter can produce, so subsetting keeps the fonts a band needs. */
502
+ const COUNTER_DIGITS = "0123456789";
499
503
  function isNode(value) {
500
504
  return typeof value === "object" && value !== null && "type" in value && !("$$typeof" in value);
501
505
  }
502
506
  async function resolveNode(input) {
503
- return isNode(input) ? {
507
+ if (isNode(input)) return {
504
508
  node: input,
505
509
  stylesheets: []
506
- } : (0, _takumi_rs_helpers_jsx.fromJsx)(input);
510
+ };
511
+ if (typeof input === "string") return (0, _takumi_rs_helpers_html.fromHtml)(input);
512
+ return (0, _takumi_rs_helpers_jsx.fromJsx)(input);
507
513
  }
508
514
  /** A PDF renderer holding registered fonts. Reuse one instance across renders. */
509
515
  var PdfRenderer = class {
510
516
  inner = new PdfRenderer$1();
511
517
  fonts = new _takumi_rs_helpers_renderer.FontRegistry((font) => this.inner.registerFont(font));
512
- /** Renders a node tree or JSX to PDF bytes. See {@link RenderOptions}. */
518
+ /** Renders a node tree, JSX, or an HTML string to PDF bytes. See {@link RenderOptions}. */
513
519
  async render(node, options = {}) {
514
520
  const { fonts, images, header, footer, stylesheets, fontFamilies, ...rest } = options;
515
- const [main, headerResult, footerResult, resources] = await Promise.all([
521
+ const [main, headerResult, footerResult] = await Promise.all([
516
522
  resolveNode(node),
517
523
  header === void 0 ? void 0 : resolveNode(header),
518
- footer === void 0 ? void 0 : resolveNode(footer),
519
- this.fonts.resolveResources(fonts, images, fontFamilies)
524
+ footer === void 0 ? void 0 : resolveNode(footer)
520
525
  ]);
526
+ const bands = [headerResult?.node, footerResult?.node].filter((band) => band !== void 0);
527
+ const resources = await this.fonts.resolveResources(fonts && (0, _takumi_rs_helpers.subsetFonts)({
528
+ fonts,
529
+ source: bands.length > 0 ? [
530
+ main.node,
531
+ ...bands,
532
+ COUNTER_DIGITS
533
+ ] : main.node
534
+ }), images, fontFamilies);
521
535
  const sheets = [
522
536
  ...stylesheets ?? [],
523
537
  ...main.stylesheets,
@@ -536,14 +550,21 @@ var PdfRenderer = class {
536
550
  /**
537
551
  * Lays out a node tree without rendering and returns its size in CSS px.
538
552
  *
539
- * With page options the tree lays out at the full page width with unbounded
540
- * height, exactly how {@link render} measures a header or footer band
541
- * (`pageNumber` / `totalPages` hooks are filled with three-digit counters),
542
- * so the height tells you how much margin a band needs.
553
+ * With page options the tree lays out against the full page width with
554
+ * unbounded height, exactly how {@link render} measures a header or footer
555
+ * band (`pageNumber` / `totalPages` hooks are filled with three-digit
556
+ * counters), so the height tells you how much margin a band needs.
557
+ *
558
+ * The returned size is the tree's own, not the space it was laid out
559
+ * against: a box with `width: 100px` measures 100 wide on any page.
543
560
  */
544
561
  async measure(node, options = {}) {
545
562
  const { fonts, images, stylesheets, fontFamilies, ...rest } = options;
546
- const [main, resources] = await Promise.all([resolveNode(node), this.fonts.resolveResources(fonts, images, fontFamilies)]);
563
+ const main = await resolveNode(node);
564
+ const resources = await this.fonts.resolveResources(fonts && (0, _takumi_rs_helpers.subsetFonts)({
565
+ fonts,
566
+ source: [main.node, COUNTER_DIGITS]
567
+ }), images, fontFamilies);
547
568
  const sheets = [...stylesheets ?? [], ...main.stylesheets];
548
569
  return this.inner.measure(main.node, {
549
570
  ...rest,
package/dist/export.d.cts CHANGED
@@ -46,8 +46,8 @@ declare module "react" {
46
46
  tw?: string;
47
47
  }
48
48
  }
49
- /** A document input: a takumi node tree or JSX. */
50
- type NodeInput = Node | ReactNode | ReactElementLike;
49
+ /** A document input: a takumi node tree, JSX, or an HTML string. */
50
+ type NodeInput = Node | ReactNode | ReactElementLike | string;
51
51
  /** Explicit dimensions in CSS px (96 dpi). */
52
52
  type Dimensions = {
53
53
  width: number;
@@ -211,14 +211,15 @@ type ArchivalAttachment = Attachment & {
211
211
  };
212
212
  /**
213
213
  * Standards conformance. Invalid combinations are type errors: the `a` levels
214
- * imply a structure tree so `tagged: false` is rejected, PDF/A-4 is PDF 2.0
215
- * while PDF/UA-1 is PDF 1.7-only so they cannot combine, and only the
216
- * PDF/A-3 levels, PDF/A-4f (or plain PDF) accept attachments.
214
+ * imply a structure tree so `tagged: false` is rejected, PDF/UA-1 is PDF
215
+ * 1.7-only and PDF/UA-2 is PDF 2.0-only so each pairs with its own PDF/A
216
+ * levels, and only the PDF/A-3 levels, PDF/A-4f (or plain PDF) accept
217
+ * attachments.
217
218
  */
218
219
  type ConformanceOptions = {
219
220
  pdfa?: never;
220
- /** Structure tree: off, on (default), or validated against PDF/UA-1. */
221
- tagged?: boolean | "ua1";
221
+ /** Structure tree: off, on (default), or validated against PDF/UA. */
222
+ tagged?: boolean | "ua1" | "ua2";
222
223
  /** Files attached to the document. */
223
224
  attachments?: Attachment[];
224
225
  } | {
@@ -240,11 +241,11 @@ type ConformanceOptions = {
240
241
  attachments?: ArchivalAttachment[];
241
242
  } | {
242
243
  pdfa: "4";
243
- tagged?: boolean;
244
+ tagged?: boolean | "ua2";
244
245
  attachments?: never;
245
246
  } | {
246
247
  pdfa: "4f";
247
- tagged?: boolean;
248
+ tagged?: boolean | "ua2";
248
249
  attachments?: ArchivalAttachment[];
249
250
  };
250
251
  type RenderOptions = (PagedOptions | ViewportOptions) & ConformanceOptions & {
@@ -270,15 +271,18 @@ type RenderOptions = (PagedOptions | ViewportOptions) & ConformanceOptions & {
270
271
  declare class PdfRenderer {
271
272
  private inner;
272
273
  private fonts;
273
- /** Renders a node tree or JSX to PDF bytes. See {@link RenderOptions}. */
274
+ /** Renders a node tree, JSX, or an HTML string to PDF bytes. See {@link RenderOptions}. */
274
275
  render(node: NodeInput, options?: RenderOptions): Promise<Uint8Array>;
275
276
  /**
276
277
  * Lays out a node tree without rendering and returns its size in CSS px.
277
278
  *
278
- * With page options the tree lays out at the full page width with unbounded
279
- * height, exactly how {@link render} measures a header or footer band
280
- * (`pageNumber` / `totalPages` hooks are filled with three-digit counters),
281
- * so the height tells you how much margin a band needs.
279
+ * With page options the tree lays out against the full page width with
280
+ * unbounded height, exactly how {@link render} measures a header or footer
281
+ * band (`pageNumber` / `totalPages` hooks are filled with three-digit
282
+ * counters), so the height tells you how much margin a band needs.
283
+ *
284
+ * The returned size is the tree's own, not the space it was laid out
285
+ * against: a box with `width: 100px` measures 100 wide on any page.
282
286
  */
283
287
  measure(node: NodeInput, options?: MeasureOptions): Promise<MeasuredSize>;
284
288
  /** Registers a font ahead of time, deduped against earlier registrations. */
package/dist/export.d.mts CHANGED
@@ -46,8 +46,8 @@ declare module "react" {
46
46
  tw?: string;
47
47
  }
48
48
  }
49
- /** A document input: a takumi node tree or JSX. */
50
- type NodeInput = Node | ReactNode | ReactElementLike;
49
+ /** A document input: a takumi node tree, JSX, or an HTML string. */
50
+ type NodeInput = Node | ReactNode | ReactElementLike | string;
51
51
  /** Explicit dimensions in CSS px (96 dpi). */
52
52
  type Dimensions = {
53
53
  width: number;
@@ -211,14 +211,15 @@ type ArchivalAttachment = Attachment & {
211
211
  };
212
212
  /**
213
213
  * Standards conformance. Invalid combinations are type errors: the `a` levels
214
- * imply a structure tree so `tagged: false` is rejected, PDF/A-4 is PDF 2.0
215
- * while PDF/UA-1 is PDF 1.7-only so they cannot combine, and only the
216
- * PDF/A-3 levels, PDF/A-4f (or plain PDF) accept attachments.
214
+ * imply a structure tree so `tagged: false` is rejected, PDF/UA-1 is PDF
215
+ * 1.7-only and PDF/UA-2 is PDF 2.0-only so each pairs with its own PDF/A
216
+ * levels, and only the PDF/A-3 levels, PDF/A-4f (or plain PDF) accept
217
+ * attachments.
217
218
  */
218
219
  type ConformanceOptions = {
219
220
  pdfa?: never;
220
- /** Structure tree: off, on (default), or validated against PDF/UA-1. */
221
- tagged?: boolean | "ua1";
221
+ /** Structure tree: off, on (default), or validated against PDF/UA. */
222
+ tagged?: boolean | "ua1" | "ua2";
222
223
  /** Files attached to the document. */
223
224
  attachments?: Attachment[];
224
225
  } | {
@@ -240,11 +241,11 @@ type ConformanceOptions = {
240
241
  attachments?: ArchivalAttachment[];
241
242
  } | {
242
243
  pdfa: "4";
243
- tagged?: boolean;
244
+ tagged?: boolean | "ua2";
244
245
  attachments?: never;
245
246
  } | {
246
247
  pdfa: "4f";
247
- tagged?: boolean;
248
+ tagged?: boolean | "ua2";
248
249
  attachments?: ArchivalAttachment[];
249
250
  };
250
251
  type RenderOptions = (PagedOptions | ViewportOptions) & ConformanceOptions & {
@@ -270,15 +271,18 @@ type RenderOptions = (PagedOptions | ViewportOptions) & ConformanceOptions & {
270
271
  declare class PdfRenderer {
271
272
  private inner;
272
273
  private fonts;
273
- /** Renders a node tree or JSX to PDF bytes. See {@link RenderOptions}. */
274
+ /** Renders a node tree, JSX, or an HTML string to PDF bytes. See {@link RenderOptions}. */
274
275
  render(node: NodeInput, options?: RenderOptions): Promise<Uint8Array>;
275
276
  /**
276
277
  * Lays out a node tree without rendering and returns its size in CSS px.
277
278
  *
278
- * With page options the tree lays out at the full page width with unbounded
279
- * height, exactly how {@link render} measures a header or footer band
280
- * (`pageNumber` / `totalPages` hooks are filled with three-digit counters),
281
- * so the height tells you how much margin a band needs.
279
+ * With page options the tree lays out against the full page width with
280
+ * unbounded height, exactly how {@link render} measures a header or footer
281
+ * band (`pageNumber` / `totalPages` hooks are filled with three-digit
282
+ * counters), so the height tells you how much margin a band needs.
283
+ *
284
+ * The returned size is the tree's own, not the space it was laid out
285
+ * against: a box with `width: 100px` measures 100 wide on any page.
282
286
  */
283
287
  measure(node: NodeInput, options?: MeasureOptions): Promise<MeasuredSize>;
284
288
  /** Registers a font ahead of time, deduped against earlier registrations. */
package/dist/export.mjs CHANGED
@@ -1,5 +1,7 @@
1
+ import { fromHtml } from "@takumi-rs/helpers/html";
1
2
  import { fromJsx } from "@takumi-rs/helpers/jsx";
2
3
  import { FontRegistry } from "@takumi-rs/helpers/renderer";
4
+ import { subsetFonts } from "@takumi-rs/helpers";
3
5
  //#region pkg/takumi_pdf_wasm.js
4
6
  /**
5
7
  * A PDF renderer holding registered fonts and a decoded-resource cache.
@@ -492,28 +494,40 @@ async function __wbg_init(module_or_path) {
492
494
  }
493
495
  //#endregion
494
496
  //#region src/export.ts
497
+ /** Every character a page counter can produce, so subsetting keeps the fonts a band needs. */
498
+ const COUNTER_DIGITS = "0123456789";
495
499
  function isNode(value) {
496
500
  return typeof value === "object" && value !== null && "type" in value && !("$$typeof" in value);
497
501
  }
498
502
  async function resolveNode(input) {
499
- return isNode(input) ? {
503
+ if (isNode(input)) return {
500
504
  node: input,
501
505
  stylesheets: []
502
- } : fromJsx(input);
506
+ };
507
+ if (typeof input === "string") return fromHtml(input);
508
+ return fromJsx(input);
503
509
  }
504
510
  /** A PDF renderer holding registered fonts. Reuse one instance across renders. */
505
511
  var PdfRenderer = class {
506
512
  inner = new PdfRenderer$1();
507
513
  fonts = new FontRegistry((font) => this.inner.registerFont(font));
508
- /** Renders a node tree or JSX to PDF bytes. See {@link RenderOptions}. */
514
+ /** Renders a node tree, JSX, or an HTML string to PDF bytes. See {@link RenderOptions}. */
509
515
  async render(node, options = {}) {
510
516
  const { fonts, images, header, footer, stylesheets, fontFamilies, ...rest } = options;
511
- const [main, headerResult, footerResult, resources] = await Promise.all([
517
+ const [main, headerResult, footerResult] = await Promise.all([
512
518
  resolveNode(node),
513
519
  header === void 0 ? void 0 : resolveNode(header),
514
- footer === void 0 ? void 0 : resolveNode(footer),
515
- this.fonts.resolveResources(fonts, images, fontFamilies)
520
+ footer === void 0 ? void 0 : resolveNode(footer)
516
521
  ]);
522
+ const bands = [headerResult?.node, footerResult?.node].filter((band) => band !== void 0);
523
+ const resources = await this.fonts.resolveResources(fonts && subsetFonts({
524
+ fonts,
525
+ source: bands.length > 0 ? [
526
+ main.node,
527
+ ...bands,
528
+ COUNTER_DIGITS
529
+ ] : main.node
530
+ }), images, fontFamilies);
517
531
  const sheets = [
518
532
  ...stylesheets ?? [],
519
533
  ...main.stylesheets,
@@ -532,14 +546,21 @@ var PdfRenderer = class {
532
546
  /**
533
547
  * Lays out a node tree without rendering and returns its size in CSS px.
534
548
  *
535
- * With page options the tree lays out at the full page width with unbounded
536
- * height, exactly how {@link render} measures a header or footer band
537
- * (`pageNumber` / `totalPages` hooks are filled with three-digit counters),
538
- * so the height tells you how much margin a band needs.
549
+ * With page options the tree lays out against the full page width with
550
+ * unbounded height, exactly how {@link render} measures a header or footer
551
+ * band (`pageNumber` / `totalPages` hooks are filled with three-digit
552
+ * counters), so the height tells you how much margin a band needs.
553
+ *
554
+ * The returned size is the tree's own, not the space it was laid out
555
+ * against: a box with `width: 100px` measures 100 wide on any page.
539
556
  */
540
557
  async measure(node, options = {}) {
541
558
  const { fonts, images, stylesheets, fontFamilies, ...rest } = options;
542
- const [main, resources] = await Promise.all([resolveNode(node), this.fonts.resolveResources(fonts, images, fontFamilies)]);
559
+ const main = await resolveNode(node);
560
+ const resources = await this.fonts.resolveResources(fonts && subsetFonts({
561
+ fonts,
562
+ source: [main.node, COUNTER_DIGITS]
563
+ }), images, fontFamilies);
543
564
  const sheets = [...stylesheets ?? [], ...main.stylesheets];
544
565
  return this.inner.measure(main.node, {
545
566
  ...rest,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "takumi-pdf",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "description": "HTML/JSX to paged, selectable-text PDF, powered by takumi. WebAssembly, no Chromium.",
5
5
  "keywords": [
6
6
  "css",
@@ -83,7 +83,7 @@
83
83
  "publish-lint": "attw --pack . && publint --strict ."
84
84
  },
85
85
  "dependencies": {
86
- "@takumi-rs/helpers": "2.5.11"
86
+ "@takumi-rs/helpers": "2.6.0"
87
87
  },
88
88
  "devDependencies": {
89
89
  "@types/bun": "^1.3.14",
Binary file