@ai-matrx/print 0.8.0 → 0.8.2

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.
@@ -133,7 +133,7 @@ interface DocumentSettings {
133
133
  fontSize: number;
134
134
  }
135
135
  interface DocumentNotice {
136
- code: "frontmatter-invalid" | "setting-ignored" | "xref-unresolved" | "citation-unresolved" | "html-stripped" | "image-unavailable" | "glyph-substituted" | "content-lost" | "math-error" | "empty-document";
136
+ code: "frontmatter-invalid" | "setting-ignored" | "xref-unresolved" | "citation-unresolved" | "html-stripped" | "image-unavailable" | "glyph-substituted" | "content-lost" | "math-error" | "toc-pages-deferred" | "empty-document";
137
137
  message: string;
138
138
  remedy: string;
139
139
  }
@@ -313,10 +313,24 @@ declare const NOTO_EMOJI_URL = "https://cdn.jsdelivr.net/npm/@fontsource/noto-em
313
313
  declare const NOTO_SYMBOLS_URL = "https://cdn.jsdelivr.net/npm/@fontsource/noto-sans-math@5.3.0/files/noto-sans-math-latin-400-normal.woff";
314
314
  declare const defaultEmojiFontLoader: FontLoader;
315
315
  declare const defaultSymbolFontLoader: FontLoader;
316
+ /** Loads the CJK subset faces covering these code points. */
317
+ type CjkFontLoader = (codepoints: number[]) => Promise<Array<{
318
+ name: string;
319
+ bytes: Uint8Array;
320
+ }>>;
316
321
  interface FallbackFontLoaders {
317
322
  emoji?: FontLoader | undefined;
318
323
  symbols?: FontLoader | undefined;
319
- }
324
+ cjk?: CjkFontLoader | undefined;
325
+ }
326
+ /** Noto Sans JP (SIL OFL) from fontsource: 124 unicode-range subsets; only the needed ones load. */
327
+ declare const NOTO_CJK_BASE = "https://cdn.jsdelivr.net/npm/@fontsource/noto-sans-jp@5.3.0";
328
+ /** Parse a fontsource stylesheet: each face's file stem and its unicode ranges. */
329
+ declare function parseUnicodeRangeCss(css: string): Array<{
330
+ file: string;
331
+ ranges: Array<[number, number]>;
332
+ }>;
333
+ declare const defaultCjkFontLoader: CjkFontLoader;
320
334
  /**
321
335
  * Words pdfmake did not put on a page: a text node with no position was never
322
336
  * placed, and a node measured taller than its page (`__height`) whose lines
@@ -657,4 +671,4 @@ declare function exportDocument(source: string, format: DocumentFormat, options?
657
671
  /** Browser helper: save an export through a download link. */
658
672
  declare function downloadDocumentExport(exp: DocumentExport): void;
659
673
 
660
- export { BUNDLED_STYLE_IDS, type BibliographyEntry, type Block, CSL_STYLE_ALIASES, type Caption, CitationEngine, type CitationStyle, type CslFetcher, type CslItem, type CslName, DEFAULT_FOOTER, DOCUMENT_FORMATS, type DocSection, type DocTree, type DocumentExport, type DocumentFormat, type DocumentNotice, type DocumentSettings, type ExportDocumentOptions, type FallbackFontLoaders, type FontLoader, type ImageLoader, type ImageMap, type Inline, type ListItem, type Margins, type MathSpan, NOTO_EMOJI_URL, NOTO_SYMBOLS_URL, type Orientation, PAGE_SIZES, type PageSizeName, type ParseDocumentOptions, type PrepareDocumentOptions, type ResolvedImage, type RunningTemplate, type TocEntry, type TypesetMath, buildPdfDefinition, countUnplacedWords, cslStyleId, defaultCslFetcher, defaultEmojiFontLoader, defaultImageLoader, defaultSymbolFontLoader, documentCss, documentToDocx, documentToEpub, documentToHtml, documentToPdf, downloadDocumentExport, exportDocument, extractMath, fillTemplate, loadCslLocale, loadCslStyle, mathKey, normalizeReference, parseCitationGroup, parseDocument, parseLength, parseLocator, parseMathML, plainText, prepareDocument, renderBlocksHtml, renderPdfDefinition, resolveDocumentImages, resolveSettings, scanCitationKeys, splitFrontmatter, typesetDocumentMath };
674
+ export { BUNDLED_STYLE_IDS, type BibliographyEntry, type Block, CSL_STYLE_ALIASES, type Caption, CitationEngine, type CitationStyle, type CjkFontLoader, type CslFetcher, type CslItem, type CslName, DEFAULT_FOOTER, DOCUMENT_FORMATS, type DocSection, type DocTree, type DocumentExport, type DocumentFormat, type DocumentNotice, type DocumentSettings, type ExportDocumentOptions, type FallbackFontLoaders, type FontLoader, type ImageLoader, type ImageMap, type Inline, type ListItem, type Margins, type MathSpan, NOTO_CJK_BASE, NOTO_EMOJI_URL, NOTO_SYMBOLS_URL, type Orientation, PAGE_SIZES, type PageSizeName, type ParseDocumentOptions, type PrepareDocumentOptions, type ResolvedImage, type RunningTemplate, type TocEntry, type TypesetMath, buildPdfDefinition, countUnplacedWords, cslStyleId, defaultCjkFontLoader, defaultCslFetcher, defaultEmojiFontLoader, defaultImageLoader, defaultSymbolFontLoader, documentCss, documentToDocx, documentToEpub, documentToHtml, documentToPdf, downloadDocumentExport, exportDocument, extractMath, fillTemplate, loadCslLocale, loadCslStyle, mathKey, normalizeReference, parseCitationGroup, parseDocument, parseLength, parseLocator, parseMathML, parseUnicodeRangeCss, plainText, prepareDocument, renderBlocksHtml, renderPdfDefinition, resolveDocumentImages, resolveSettings, scanCitationKeys, splitFrontmatter, typesetDocumentMath };
@@ -133,7 +133,7 @@ interface DocumentSettings {
133
133
  fontSize: number;
134
134
  }
135
135
  interface DocumentNotice {
136
- code: "frontmatter-invalid" | "setting-ignored" | "xref-unresolved" | "citation-unresolved" | "html-stripped" | "image-unavailable" | "glyph-substituted" | "content-lost" | "math-error" | "empty-document";
136
+ code: "frontmatter-invalid" | "setting-ignored" | "xref-unresolved" | "citation-unresolved" | "html-stripped" | "image-unavailable" | "glyph-substituted" | "content-lost" | "math-error" | "toc-pages-deferred" | "empty-document";
137
137
  message: string;
138
138
  remedy: string;
139
139
  }
@@ -313,10 +313,24 @@ declare const NOTO_EMOJI_URL = "https://cdn.jsdelivr.net/npm/@fontsource/noto-em
313
313
  declare const NOTO_SYMBOLS_URL = "https://cdn.jsdelivr.net/npm/@fontsource/noto-sans-math@5.3.0/files/noto-sans-math-latin-400-normal.woff";
314
314
  declare const defaultEmojiFontLoader: FontLoader;
315
315
  declare const defaultSymbolFontLoader: FontLoader;
316
+ /** Loads the CJK subset faces covering these code points. */
317
+ type CjkFontLoader = (codepoints: number[]) => Promise<Array<{
318
+ name: string;
319
+ bytes: Uint8Array;
320
+ }>>;
316
321
  interface FallbackFontLoaders {
317
322
  emoji?: FontLoader | undefined;
318
323
  symbols?: FontLoader | undefined;
319
- }
324
+ cjk?: CjkFontLoader | undefined;
325
+ }
326
+ /** Noto Sans JP (SIL OFL) from fontsource: 124 unicode-range subsets; only the needed ones load. */
327
+ declare const NOTO_CJK_BASE = "https://cdn.jsdelivr.net/npm/@fontsource/noto-sans-jp@5.3.0";
328
+ /** Parse a fontsource stylesheet: each face's file stem and its unicode ranges. */
329
+ declare function parseUnicodeRangeCss(css: string): Array<{
330
+ file: string;
331
+ ranges: Array<[number, number]>;
332
+ }>;
333
+ declare const defaultCjkFontLoader: CjkFontLoader;
320
334
  /**
321
335
  * Words pdfmake did not put on a page: a text node with no position was never
322
336
  * placed, and a node measured taller than its page (`__height`) whose lines
@@ -657,4 +671,4 @@ declare function exportDocument(source: string, format: DocumentFormat, options?
657
671
  /** Browser helper: save an export through a download link. */
658
672
  declare function downloadDocumentExport(exp: DocumentExport): void;
659
673
 
660
- export { BUNDLED_STYLE_IDS, type BibliographyEntry, type Block, CSL_STYLE_ALIASES, type Caption, CitationEngine, type CitationStyle, type CslFetcher, type CslItem, type CslName, DEFAULT_FOOTER, DOCUMENT_FORMATS, type DocSection, type DocTree, type DocumentExport, type DocumentFormat, type DocumentNotice, type DocumentSettings, type ExportDocumentOptions, type FallbackFontLoaders, type FontLoader, type ImageLoader, type ImageMap, type Inline, type ListItem, type Margins, type MathSpan, NOTO_EMOJI_URL, NOTO_SYMBOLS_URL, type Orientation, PAGE_SIZES, type PageSizeName, type ParseDocumentOptions, type PrepareDocumentOptions, type ResolvedImage, type RunningTemplate, type TocEntry, type TypesetMath, buildPdfDefinition, countUnplacedWords, cslStyleId, defaultCslFetcher, defaultEmojiFontLoader, defaultImageLoader, defaultSymbolFontLoader, documentCss, documentToDocx, documentToEpub, documentToHtml, documentToPdf, downloadDocumentExport, exportDocument, extractMath, fillTemplate, loadCslLocale, loadCslStyle, mathKey, normalizeReference, parseCitationGroup, parseDocument, parseLength, parseLocator, parseMathML, plainText, prepareDocument, renderBlocksHtml, renderPdfDefinition, resolveDocumentImages, resolveSettings, scanCitationKeys, splitFrontmatter, typesetDocumentMath };
674
+ export { BUNDLED_STYLE_IDS, type BibliographyEntry, type Block, CSL_STYLE_ALIASES, type Caption, CitationEngine, type CitationStyle, type CjkFontLoader, type CslFetcher, type CslItem, type CslName, DEFAULT_FOOTER, DOCUMENT_FORMATS, type DocSection, type DocTree, type DocumentExport, type DocumentFormat, type DocumentNotice, type DocumentSettings, type ExportDocumentOptions, type FallbackFontLoaders, type FontLoader, type ImageLoader, type ImageMap, type Inline, type ListItem, type Margins, type MathSpan, NOTO_CJK_BASE, NOTO_EMOJI_URL, NOTO_SYMBOLS_URL, type Orientation, PAGE_SIZES, type PageSizeName, type ParseDocumentOptions, type PrepareDocumentOptions, type ResolvedImage, type RunningTemplate, type TocEntry, type TypesetMath, buildPdfDefinition, countUnplacedWords, cslStyleId, defaultCjkFontLoader, defaultCslFetcher, defaultEmojiFontLoader, defaultImageLoader, defaultSymbolFontLoader, documentCss, documentToDocx, documentToEpub, documentToHtml, documentToPdf, downloadDocumentExport, exportDocument, extractMath, fillTemplate, loadCslLocale, loadCslStyle, mathKey, normalizeReference, parseCitationGroup, parseDocument, parseLength, parseLocator, parseMathML, parseUnicodeRangeCss, plainText, prepareDocument, renderBlocksHtml, renderPdfDefinition, resolveDocumentImages, resolveSettings, scanCitationKeys, splitFrontmatter, typesetDocumentMath };
package/dist/document.js CHANGED
@@ -4456,12 +4456,13 @@ async function loadTypography(tree, loaders = {}) {
4456
4456
  const base = await loadFaces(vfs, s.fontSize);
4457
4457
  let needEmoji = false;
4458
4458
  let needSymbols = false;
4459
+ const cjk = /* @__PURE__ */ new Set();
4459
4460
  for (const ch of documentText(tree)) {
4460
4461
  const cp = ch.codePointAt(0);
4461
4462
  if (cp < 32 || cp === 8205 || cp === 65039 || base.body(cp)) continue;
4462
4463
  if (EMOJI_CP.test(ch)) needEmoji = true;
4464
+ else if (isCjk(cp)) cjk.add(cp);
4463
4465
  else needSymbols = true;
4464
- if (needEmoji && needSymbols) break;
4465
4466
  }
4466
4467
  const fallbacks = [];
4467
4468
  if (needEmoji) {
@@ -4472,6 +4473,26 @@ async function loadTypography(tree, loaders = {}) {
4472
4473
  const b = await registerFace("NotoSymbols", "NotoSansMath.woff", loaders.symbols ?? defaultSymbolFontLoader, tree, "symbols");
4473
4474
  if (b) fallbacks.push({ font: "NotoSymbols", bytes: b });
4474
4475
  }
4476
+ if (cjk.size) {
4477
+ let subsets = [];
4478
+ try {
4479
+ subsets = await (loaders.cjk ?? defaultCjkFontLoader)([...cjk]);
4480
+ } catch {
4481
+ subsets = [];
4482
+ }
4483
+ for (const sub of subsets) {
4484
+ const font = `NotoCJK_${sub.name.replace(/[^\w]/g, "_")}`;
4485
+ const b = await registerFace(font, `${sub.name}.woff`, async () => sub.bytes, tree, "Japanese / Chinese");
4486
+ if (b) fallbacks.push({ font, bytes: b });
4487
+ }
4488
+ if (!subsets.length) {
4489
+ tree.notices.push({
4490
+ code: "glyph-substituted",
4491
+ message: "The Japanese / Chinese font could not be loaded, so those characters were left out of the PDF (Word, EPUB and HTML keep them).",
4492
+ remedy: "Check the internet connection (the font comes from cdn.jsdelivr.net) and export again."
4493
+ });
4494
+ }
4495
+ }
4475
4496
  const faces = fallbacks.length ? await loadFaces(vfs, s.fontSize, fallbacks) : base;
4476
4497
  if (!s.hyphenate) return { faces };
4477
4498
  const split = await loadHyphenator(s.lang);
@@ -4494,6 +4515,37 @@ var urlLoader = (url) => async () => {
4494
4515
  };
4495
4516
  var defaultEmojiFontLoader = urlLoader(NOTO_EMOJI_URL);
4496
4517
  var defaultSymbolFontLoader = urlLoader(NOTO_SYMBOLS_URL);
4518
+ var NOTO_CJK_BASE = "https://cdn.jsdelivr.net/npm/@fontsource/noto-sans-jp@5.3.0";
4519
+ function parseUnicodeRangeCss(css) {
4520
+ const out = [];
4521
+ for (const block3 of css.split("@font-face").slice(1)) {
4522
+ const file = /url\(\.\/files\/([\w-]+)\.woff\)/.exec(block3)?.[1];
4523
+ const range = /unicode-range:\s*([^;]+);/.exec(block3)?.[1];
4524
+ if (!file || !range) continue;
4525
+ const ranges = range.split(",").map((r) => {
4526
+ const [a, b] = r.trim().replace(/^U\+/i, "").split("-");
4527
+ const lo = parseInt(a, 16);
4528
+ return [lo, b ? parseInt(b, 16) : lo];
4529
+ });
4530
+ out.push({ file, ranges });
4531
+ }
4532
+ return out;
4533
+ }
4534
+ var defaultCjkFontLoader = async (cps) => {
4535
+ if (typeof fetch !== "function" || cps.length === 0) return [];
4536
+ const res = await fetch(`${NOTO_CJK_BASE}/400.css`);
4537
+ if (!res.ok) return [];
4538
+ const faces = parseUnicodeRangeCss(await res.text()).filter((f) => cps.some((cp) => f.ranges.some(([a, b]) => cp >= a && cp <= b)));
4539
+ const out = [];
4540
+ await Promise.all(
4541
+ faces.map(async (f) => {
4542
+ const r = await fetch(`${NOTO_CJK_BASE}/files/${f.file}.woff`);
4543
+ if (r.ok) out.push({ name: f.file, bytes: new Uint8Array(await r.arrayBuffer()) });
4544
+ })
4545
+ );
4546
+ return out;
4547
+ };
4548
+ var isCjk = (cp) => cp >= 11904 && cp <= 40959 || cp >= 44032 && cp <= 55215 || cp >= 63744 && cp <= 64255 || cp >= 65072 && cp <= 65103 || cp >= 65280 && cp <= 65519 || cp >= 131072 && cp <= 201551;
4497
4549
  var EMOJI_CP = /\p{Extended_Pictographic}|\p{Regional_Indicator}|\u20E3/u;
4498
4550
  function documentText(tree) {
4499
4551
  const s = tree.settings;
@@ -5471,7 +5523,21 @@ async function exportDocument(source, format, options = {}) {
5471
5523
  bytes = r.bytes;
5472
5524
  pageCount = r.pageCount;
5473
5525
  } else if (format === "docx") {
5474
- const pages = tree.toc.length ? await headingPages(tree, images, options.fonts) : void 0;
5526
+ const hasToc = tree.sections.some((sec) => sec.blocks.some((blk) => blk.type === "toc"));
5527
+ let pages;
5528
+ if (hasToc) {
5529
+ try {
5530
+ pages = await headingPages(tree, images, options.fonts);
5531
+ } catch (err) {
5532
+ const n = {
5533
+ code: "toc-pages-deferred",
5534
+ message: `The Word file's table of contents lists every heading; its page numbers fill in when the file is opened (they could not be measured here: ${err.message}).`,
5535
+ remedy: "Open the file in Word (or update fields) to show the page numbers."
5536
+ };
5537
+ tree.notices.push(n);
5538
+ options.onNotice?.(n);
5539
+ }
5540
+ }
5475
5541
  bytes = await documentToDocx(tree, { images, headingPages: pages });
5476
5542
  } else bytes = await documentToEpub(tree, { images });
5477
5543
  }
@@ -5495,12 +5561,14 @@ export {
5495
5561
  CitationEngine,
5496
5562
  DEFAULT_FOOTER,
5497
5563
  DOCUMENT_FORMATS,
5564
+ NOTO_CJK_BASE,
5498
5565
  NOTO_EMOJI_URL,
5499
5566
  NOTO_SYMBOLS_URL,
5500
5567
  PAGE_SIZES,
5501
5568
  buildPdfDefinition,
5502
5569
  countUnplacedWords,
5503
5570
  cslStyleId,
5571
+ defaultCjkFontLoader,
5504
5572
  defaultCslFetcher,
5505
5573
  defaultEmojiFontLoader,
5506
5574
  defaultImageLoader,
@@ -5523,6 +5591,7 @@ export {
5523
5591
  parseLength,
5524
5592
  parseLocator,
5525
5593
  parseMathML,
5594
+ parseUnicodeRangeCss,
5526
5595
  plainText,
5527
5596
  prepareDocument,
5528
5597
  renderBlocksHtml,