@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.js CHANGED
@@ -16,7 +16,7 @@ function dominantTextColor(lines) {
16
16
  }
17
17
  return [...counts].sort((left, right) => right[1] - left[1])[0]?.[0] ?? "#000000";
18
18
  }
19
- function semanticTextHtml(text, lines, defaultColor) {
19
+ function semanticTextHtml(text, lines, defaultColor, preserveWeight = true) {
20
20
  const ranges = [];
21
21
  let cursor = 0;
22
22
  for (const span of lines.flatMap((line) => line.spans)) {
@@ -25,14 +25,25 @@ function semanticTextHtml(text, lines, defaultColor) {
25
25
  if (start < 0) continue;
26
26
  cursor = start + span.text.length;
27
27
  const color = normalizedColor(span.color);
28
- if (color && color !== defaultColor) ranges.push({ start, end: cursor, color });
28
+ const bold = preserveWeight && /(?:bold|semibold|demi|medium|medi)/i.test(span.fontFamily ?? "");
29
+ const italic = /(?:italic|oblique|slanted|slant|ital)/i.test(span.fontFamily ?? "");
30
+ const nondefaultColor = color && color !== defaultColor ? color : void 0;
31
+ if (nondefaultColor || bold || italic) {
32
+ ranges.push({
33
+ start,
34
+ end: cursor,
35
+ ...nondefaultColor ? { color: nondefaultColor } : {},
36
+ bold,
37
+ italic
38
+ });
39
+ }
29
40
  }
30
41
  const merged = mergeRanges(ranges, text);
31
42
  let html = "";
32
43
  let offset = 0;
33
44
  for (const range of merged) {
34
45
  html += escapeHtml(text.slice(offset, range.start));
35
- html += `<span style="color:${range.color}">${escapeHtml(text.slice(range.start, range.end))}</span>`;
46
+ html += styledHtml(text.slice(range.start, range.end), range);
36
47
  offset = range.end;
37
48
  }
38
49
  return html + escapeHtml(text.slice(offset));
@@ -41,7 +52,7 @@ function mergeRanges(ranges, text) {
41
52
  const merged = [];
42
53
  for (const range of ranges) {
43
54
  const previous = merged.at(-1);
44
- if (previous && previous.color === range.color && /^\s*$/.test(text.slice(previous.end, range.start))) {
55
+ if (previous && previous.color === range.color && previous.bold === range.bold && previous.italic === range.italic && /^\s*$/.test(text.slice(previous.end, range.start))) {
45
56
  previous.end = range.end;
46
57
  } else {
47
58
  merged.push({ ...range });
@@ -49,6 +60,13 @@ function mergeRanges(ranges, text) {
49
60
  }
50
61
  return merged;
51
62
  }
63
+ function styledHtml(value, range) {
64
+ let html = escapeHtml(value);
65
+ if (range.color) html = `<span style="color:${range.color}">${html}</span>`;
66
+ if (range.italic) html = `<em>${html}</em>`;
67
+ if (range.bold) html = `<strong>${html}</strong>`;
68
+ return html;
69
+ }
52
70
  function normalizedColor(value) {
53
71
  if (!value || !/^#[\da-f]{6}$/i.test(value)) return void 0;
54
72
  const color = value.toLowerCase();
@@ -342,7 +360,9 @@ async function writeSemanticDocument(pages, write, lookaheadPages) {
342
360
  await flushPendingParagraph();
343
361
  if (employmentOpen && block.type !== "list") await closeEmployment();
344
362
  if (!contentStarted && !headerOpen && block.type === "heading" && block.level === 1) {
345
- await write(`<header><h1>${semanticTextHtml(block.text, block.lines, defaultColor)}</h1>`);
363
+ await write(
364
+ `<header><h1>${semanticTextHtml(block.text, block.lines, defaultColor, false)}</h1>`
365
+ );
346
366
  headerOpen = true;
347
367
  continue;
348
368
  }
@@ -357,7 +377,7 @@ async function writeSemanticDocument(pages, write, lookaheadPages) {
357
377
  }
358
378
  if (block.type === "heading" && !headerHasParagraph && (block.level === 1 || block.text.trimStart().startsWith("#") || block.level === 4 && nextBlock?.type === "paragraph" && isContactBlock(nextBlock))) {
359
379
  await write(
360
- `<h${block.level}>${semanticTextHtml(block.text, block.lines, defaultColor)}</h${block.level}>`
380
+ `<h${block.level}>${semanticTextHtml(block.text, block.lines, defaultColor, false)}</h${block.level}>`
361
381
  );
362
382
  continue;
363
383
  }
@@ -395,7 +415,7 @@ async function writeSemanticDocument(pages, write, lookaheadPages) {
395
415
  const level = contentStarted && block.level === 1 ? 2 : block.level;
396
416
  await closeSections(level);
397
417
  await write(
398
- `<section data-level="${level}"><h${level}>${semanticTextHtml(block.text, block.lines, defaultColor)}</h${level}>`
418
+ `<section data-level="${level}"><h${level}>${semanticTextHtml(block.text, block.lines, defaultColor, false)}</h${level}>`
399
419
  );
400
420
  sectionLevels.push(level);
401
421
  continue;
@@ -564,7 +584,7 @@ function financialSummaryRow(entry, columns) {
564
584
  }
565
585
  function semanticBlockHtml(block, defaultColor = "#000000") {
566
586
  if (block.type === "heading")
567
- return `<h${block.level}>${semanticTextHtml(block.text, block.lines, defaultColor)}</h${block.level}>`;
587
+ return `<h${block.level}>${semanticTextHtml(block.text, block.lines, defaultColor, false)}</h${block.level}>`;
568
588
  if (block.type === "paragraph")
569
589
  return `<p>${semanticTextHtml(block.text, block.lines, defaultColor)}</p>`;
570
590
  if (block.type === "preformatted") return `<pre>${escapeHtml3(block.text)}</pre>`;
@@ -589,7 +609,7 @@ function semanticBlockHtml(block, defaultColor = "#000000") {
589
609
  return `<section><h3>${escapeHtml3(block.role)}</h3><p>${escapeHtml3(block.organization)}</p><p>${escapeHtml3(block.date)}</p></section>`;
590
610
  }
591
611
  const tag = block.ordered ? "ol" : "ul";
592
- return `<${tag}>${block.items.map((item) => `<li>${escapeHtml3(item.text)}</li>`).join("")}</${tag}>`;
612
+ return `<${tag}>${block.items.map((item) => `<li>${semanticTextHtml(item.text, item.lines, defaultColor)}</li>`).join("")}</${tag}>`;
593
613
  }
594
614
  function semanticBlockY(block) {
595
615
  const lines = block.type === "list" ? block.items.flatMap((item) => item.lines) : block.lines;
@@ -862,7 +882,7 @@ async function writeFlowPage(page, write) {
862
882
  if (block.type === "table") await write(tableToHtml(block.table));
863
883
  else if (block.type === "heading") {
864
884
  await write(
865
- `<h${block.level}>${semanticTextHtml(block.text, block.lines, defaultColor)}</h${block.level}>`
885
+ `<h${block.level}>${semanticTextHtml(block.text, block.lines, defaultColor, false)}</h${block.level}>`
866
886
  );
867
887
  } else if (block.type === "paragraph") {
868
888
  await write(
@@ -901,7 +921,9 @@ async function writeFlowPage(page, write) {
901
921
  } else {
902
922
  const tag = block.ordered ? "ol" : "ul";
903
923
  await write(`<${tag}>`);
904
- for (const item of block.items) await write(`<li>${escapeHtml4(item.text)}</li>`);
924
+ for (const item of block.items) {
925
+ await write(`<li>${semanticTextHtml(item.text, item.lines, defaultColor)}</li>`);
926
+ }
905
927
  await write(`</${tag}>`);
906
928
  }
907
929
  }