@boxpdf/html-writer 0.1.16 → 0.1.17

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/index.cjs CHANGED
@@ -39,7 +39,7 @@ function dominantTextColor(lines) {
39
39
  }
40
40
  return [...counts].sort((left, right) => right[1] - left[1])[0]?.[0] ?? "#000000";
41
41
  }
42
- function semanticTextHtml(text, lines, defaultColor) {
42
+ function semanticTextHtml(text, lines, defaultColor, preserveWeight = true) {
43
43
  const ranges = [];
44
44
  let cursor = 0;
45
45
  for (const span of lines.flatMap((line) => line.spans)) {
@@ -48,14 +48,25 @@ function semanticTextHtml(text, lines, defaultColor) {
48
48
  if (start < 0) continue;
49
49
  cursor = start + span.text.length;
50
50
  const color = normalizedColor(span.color);
51
- if (color && color !== defaultColor) ranges.push({ start, end: cursor, color });
51
+ const bold = preserveWeight && /(?:bold|semibold|demi|medium|medi)/i.test(span.fontFamily ?? "");
52
+ const italic = /(?:italic|oblique|slanted|slant|ital)/i.test(span.fontFamily ?? "");
53
+ const nondefaultColor = color && color !== defaultColor ? color : void 0;
54
+ if (nondefaultColor || bold || italic) {
55
+ ranges.push({
56
+ start,
57
+ end: cursor,
58
+ ...nondefaultColor ? { color: nondefaultColor } : {},
59
+ bold,
60
+ italic
61
+ });
62
+ }
52
63
  }
53
64
  const merged = mergeRanges(ranges, text);
54
65
  let html = "";
55
66
  let offset = 0;
56
67
  for (const range of merged) {
57
68
  html += escapeHtml(text.slice(offset, range.start));
58
- html += `<span style="color:${range.color}">${escapeHtml(text.slice(range.start, range.end))}</span>`;
69
+ html += styledHtml(text.slice(range.start, range.end), range);
59
70
  offset = range.end;
60
71
  }
61
72
  return html + escapeHtml(text.slice(offset));
@@ -64,7 +75,7 @@ function mergeRanges(ranges, text) {
64
75
  const merged = [];
65
76
  for (const range of ranges) {
66
77
  const previous = merged.at(-1);
67
- if (previous && previous.color === range.color && /^\s*$/.test(text.slice(previous.end, range.start))) {
78
+ if (previous && previous.color === range.color && previous.bold === range.bold && previous.italic === range.italic && /^\s*$/.test(text.slice(previous.end, range.start))) {
68
79
  previous.end = range.end;
69
80
  } else {
70
81
  merged.push({ ...range });
@@ -72,6 +83,13 @@ function mergeRanges(ranges, text) {
72
83
  }
73
84
  return merged;
74
85
  }
86
+ function styledHtml(value, range) {
87
+ let html = escapeHtml(value);
88
+ if (range.color) html = `<span style="color:${range.color}">${html}</span>`;
89
+ if (range.italic) html = `<em>${html}</em>`;
90
+ if (range.bold) html = `<strong>${html}</strong>`;
91
+ return html;
92
+ }
75
93
  function normalizedColor(value) {
76
94
  if (!value || !/^#[\da-f]{6}$/i.test(value)) return void 0;
77
95
  const color = value.toLowerCase();
@@ -365,7 +383,9 @@ async function writeSemanticDocument(pages, write, lookaheadPages) {
365
383
  await flushPendingParagraph();
366
384
  if (employmentOpen && block.type !== "list") await closeEmployment();
367
385
  if (!contentStarted && !headerOpen && block.type === "heading" && block.level === 1) {
368
- await write(`<header><h1>${semanticTextHtml(block.text, block.lines, defaultColor)}</h1>`);
386
+ await write(
387
+ `<header><h1>${semanticTextHtml(block.text, block.lines, defaultColor, false)}</h1>`
388
+ );
369
389
  headerOpen = true;
370
390
  continue;
371
391
  }
@@ -380,7 +400,7 @@ async function writeSemanticDocument(pages, write, lookaheadPages) {
380
400
  }
381
401
  if (block.type === "heading" && !headerHasParagraph && (block.level === 1 || block.text.trimStart().startsWith("#") || block.level === 4 && nextBlock?.type === "paragraph" && isContactBlock(nextBlock))) {
382
402
  await write(
383
- `<h${block.level}>${semanticTextHtml(block.text, block.lines, defaultColor)}</h${block.level}>`
403
+ `<h${block.level}>${semanticTextHtml(block.text, block.lines, defaultColor, false)}</h${block.level}>`
384
404
  );
385
405
  continue;
386
406
  }
@@ -418,7 +438,7 @@ async function writeSemanticDocument(pages, write, lookaheadPages) {
418
438
  const level = contentStarted && block.level === 1 ? 2 : block.level;
419
439
  await closeSections(level);
420
440
  await write(
421
- `<section data-level="${level}"><h${level}>${semanticTextHtml(block.text, block.lines, defaultColor)}</h${level}>`
441
+ `<section data-level="${level}"><h${level}>${semanticTextHtml(block.text, block.lines, defaultColor, false)}</h${level}>`
422
442
  );
423
443
  sectionLevels.push(level);
424
444
  continue;
@@ -587,7 +607,7 @@ function financialSummaryRow(entry, columns) {
587
607
  }
588
608
  function semanticBlockHtml(block, defaultColor = "#000000") {
589
609
  if (block.type === "heading")
590
- return `<h${block.level}>${semanticTextHtml(block.text, block.lines, defaultColor)}</h${block.level}>`;
610
+ return `<h${block.level}>${semanticTextHtml(block.text, block.lines, defaultColor, false)}</h${block.level}>`;
591
611
  if (block.type === "paragraph")
592
612
  return `<p>${semanticTextHtml(block.text, block.lines, defaultColor)}</p>`;
593
613
  if (block.type === "preformatted") return `<pre>${escapeHtml3(block.text)}</pre>`;
@@ -612,7 +632,7 @@ function semanticBlockHtml(block, defaultColor = "#000000") {
612
632
  return `<section><h3>${escapeHtml3(block.role)}</h3><p>${escapeHtml3(block.organization)}</p><p>${escapeHtml3(block.date)}</p></section>`;
613
633
  }
614
634
  const tag = block.ordered ? "ol" : "ul";
615
- return `<${tag}>${block.items.map((item) => `<li>${escapeHtml3(item.text)}</li>`).join("")}</${tag}>`;
635
+ return `<${tag}>${block.items.map((item) => `<li>${semanticTextHtml(item.text, item.lines, defaultColor)}</li>`).join("")}</${tag}>`;
616
636
  }
617
637
  function semanticBlockY(block) {
618
638
  const lines = block.type === "list" ? block.items.flatMap((item) => item.lines) : block.lines;
@@ -885,7 +905,7 @@ async function writeFlowPage(page, write) {
885
905
  if (block.type === "table") await write((0, import_structure2.tableToHtml)(block.table));
886
906
  else if (block.type === "heading") {
887
907
  await write(
888
- `<h${block.level}>${semanticTextHtml(block.text, block.lines, defaultColor)}</h${block.level}>`
908
+ `<h${block.level}>${semanticTextHtml(block.text, block.lines, defaultColor, false)}</h${block.level}>`
889
909
  );
890
910
  } else if (block.type === "paragraph") {
891
911
  await write(
@@ -924,7 +944,9 @@ async function writeFlowPage(page, write) {
924
944
  } else {
925
945
  const tag = block.ordered ? "ol" : "ul";
926
946
  await write(`<${tag}>`);
927
- for (const item of block.items) await write(`<li>${escapeHtml4(item.text)}</li>`);
947
+ for (const item of block.items) {
948
+ await write(`<li>${semanticTextHtml(item.text, item.lines, defaultColor)}</li>`);
949
+ }
928
950
  await write(`</${tag}>`);
929
951
  }
930
952
  }