@cj-tech-master/excelts 9.5.1 → 9.5.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.
@@ -588,10 +588,10 @@ function countRichTextWrapLines(text, runs, scaleFactor, effectiveWidth, fontMan
588
588
  ? {
589
589
  name: run.font.name ?? cellFont?.name,
590
590
  size: run.font.size ?? cellFont?.size,
591
- bold: run.font.bold ?? cellFont?.bold,
592
- italic: run.font.italic ?? cellFont?.italic,
593
- strike: run.font.strike ?? cellFont?.strike,
594
- underline: run.font.underline ?? cellFont?.underline,
591
+ bold: run.font.bold ?? false,
592
+ italic: run.font.italic ?? false,
593
+ strike: run.font.strike ?? false,
594
+ underline: run.font.underline ?? undefined,
595
595
  color: run.font.color ?? cellFont?.color
596
596
  }
597
597
  : cellFont;
@@ -607,8 +607,8 @@ function countRichTextWrapLines(text, runs, scaleFactor, effectiveWidth, fontMan
607
607
  ? {
608
608
  name: run.font.name ?? cellFont?.name,
609
609
  size: run.font.size ?? cellFont?.size,
610
- bold: run.font.bold ?? cellFont?.bold,
611
- italic: run.font.italic ?? cellFont?.italic
610
+ bold: run.font.bold ?? false,
611
+ italic: run.font.italic ?? false
612
612
  }
613
613
  : cellFont;
614
614
  const fontProps = extractFontProperties(effectiveRunFont, defaultFamily, defaultSize);
@@ -1382,15 +1382,19 @@ function buildRichTextRuns(cell, options, fontManager, scaleFactor, cellFont) {
1382
1382
  const defaultSize = cellFont?.size ?? options.defaultFontSize;
1383
1383
  return runs.map(run => {
1384
1384
  // When a run has no font at all, use cell font entirely.
1385
- // When a run has a partial font, merge with cell font for missing properties.
1385
+ // When a run has its own <rPr>, properties NOT listed in that <rPr> default
1386
+ // to their base values (false/undefined), NOT to the cell font.
1387
+ // Only `name` and `size` fall back to cell font (they define the typeface/size
1388
+ // context), but stylistic flags (bold, italic, strike, underline) do not
1389
+ // inherit from the cell — an absent <b/> means "not bold".
1386
1390
  const effectiveFont = run.font
1387
1391
  ? {
1388
1392
  name: run.font.name ?? cellFont?.name,
1389
1393
  size: run.font.size ?? cellFont?.size,
1390
- bold: run.font.bold ?? cellFont?.bold,
1391
- italic: run.font.italic ?? cellFont?.italic,
1392
- strike: run.font.strike ?? cellFont?.strike,
1393
- underline: run.font.underline ?? cellFont?.underline,
1394
+ bold: run.font.bold ?? false,
1395
+ italic: run.font.italic ?? false,
1396
+ strike: run.font.strike ?? false,
1397
+ underline: run.font.underline ?? undefined,
1394
1398
  color: run.font.color ?? cellFont?.color
1395
1399
  }
1396
1400
  : cellFont;
@@ -76,9 +76,17 @@ export function renderPage(page, options, fontManager, totalPages) {
76
76
  });
77
77
  }
78
78
  }
79
- // Draw white in unfilled portions of the overflow area
79
+ // Draw white in unfilled portions of the overflow area.
80
+ // Inset vertically by a small amount to avoid covering the horizontal
81
+ // border lines at the top/bottom edges of the cell.
82
+ const borderInset = 0.25;
83
+ const eraseY = cellY + borderInset;
84
+ const eraseH = cellH - borderInset * 2;
85
+ if (eraseH <= 0) {
86
+ continue;
87
+ }
80
88
  if (filledRanges.length === 0) {
81
- stream.fillRect(overflowLeft, cellY, cell.textOverflowWidth, cellH, { r: 1, g: 1, b: 1 });
89
+ stream.fillRect(overflowLeft, eraseY, cell.textOverflowWidth, eraseH, { r: 1, g: 1, b: 1 });
82
90
  }
83
91
  else {
84
92
  // Sort filled ranges and draw white in gaps
@@ -86,12 +94,12 @@ export function renderPage(page, options, fontManager, totalPages) {
86
94
  let cursor = overflowLeft;
87
95
  for (const fr of filledRanges) {
88
96
  if (fr.left > cursor) {
89
- stream.fillRect(cursor, cellY, fr.left - cursor, cellH, { r: 1, g: 1, b: 1 });
97
+ stream.fillRect(cursor, eraseY, fr.left - cursor, eraseH, { r: 1, g: 1, b: 1 });
90
98
  }
91
99
  cursor = Math.max(cursor, fr.right);
92
100
  }
93
101
  if (cursor < overflowRight) {
94
- stream.fillRect(cursor, cellY, overflowRight - cursor, cellH, { r: 1, g: 1, b: 1 });
102
+ stream.fillRect(cursor, eraseY, overflowRight - cursor, eraseH, { r: 1, g: 1, b: 1 });
95
103
  }
96
104
  }
97
105
  }
@@ -362,12 +370,26 @@ function drawRichText(stream, cell, fontManager, indentPts, scaleFactor = 1) {
362
370
  const runResources = runs.map(r => runResource(r));
363
371
  // Word-wrap using actual per-run measurements — returns character ranges
364
372
  const lineRanges = wrapRichTextLines(fullText, runForChar, runs, runResources, fontManager, availWidth);
373
+ // Compute per-line heights based on the maximum font size in each line
374
+ const lineHeights = [];
375
+ for (const range of lineRanges) {
376
+ let lineMaxFont = cell.fontSize;
377
+ for (let ci = range.start; ci < range.end; ci++) {
378
+ const ri = runForChar[ci] ?? 0;
379
+ if (runs[ri].fontSize > lineMaxFont) {
380
+ lineMaxFont = runs[ri].fontSize;
381
+ }
382
+ }
383
+ lineHeights.push(lineMaxFont * LINE_HEIGHT_FACTOR);
384
+ }
365
385
  const primaryResourceName = runResources[0];
366
386
  const ascent = fontManager.getFontAscent(primaryResourceName, primaryFontSize);
367
- const totalTextHeight = lineRanges.length * lineHeight;
387
+ const totalTextHeight = lineHeights.reduce((sum, h) => sum + h, 0);
368
388
  const textStartY = computeTextStartY(verticalAlign, rect, totalTextHeight, ascent, pad.top, pad.bottom);
389
+ let cumulativeY = 0;
369
390
  for (let li = 0; li < lineRanges.length; li++) {
370
- const lineY = textStartY - li * lineHeight;
391
+ const lineY = textStartY - cumulativeY;
392
+ cumulativeY += lineHeights[li];
371
393
  const { start: lineStart, end: lineEnd } = lineRanges[li];
372
394
  // Split the line into segments by run
373
395
  const segments = [];
@@ -279,33 +279,33 @@ export function excelFillToPdfColor(fill) {
279
279
  export function borderStyleToLineWidth(style) {
280
280
  switch (style) {
281
281
  case "thin":
282
- return 0.25;
283
- case "medium":
284
282
  return 0.5;
283
+ case "medium":
284
+ return 1.0;
285
285
  case "thick":
286
- return 1;
286
+ return 2.0;
287
287
  case "double":
288
- return 0.25;
288
+ return 0.5;
289
289
  case "hair":
290
- return 0.1;
290
+ return 0.2;
291
291
  case "dotted":
292
- return 0.25;
292
+ return 0.5;
293
293
  case "dashed":
294
- return 0.25;
294
+ return 0.5;
295
295
  case "dashDot":
296
- return 0.25;
296
+ return 0.5;
297
297
  case "dashDotDot":
298
- return 0.25;
298
+ return 0.5;
299
299
  case "slantDashDot":
300
- return 0.25;
301
- case "mediumDashed":
302
300
  return 0.5;
301
+ case "mediumDashed":
302
+ return 1.0;
303
303
  case "mediumDashDot":
304
- return 0.5;
304
+ return 1.0;
305
305
  case "mediumDashDotDot":
306
- return 0.5;
306
+ return 1.0;
307
307
  default:
308
- return 0.25;
308
+ return 0.5;
309
309
  }
310
310
  }
311
311
  /**
@@ -595,10 +595,10 @@ function countRichTextWrapLines(text, runs, scaleFactor, effectiveWidth, fontMan
595
595
  ? {
596
596
  name: run.font.name ?? cellFont?.name,
597
597
  size: run.font.size ?? cellFont?.size,
598
- bold: run.font.bold ?? cellFont?.bold,
599
- italic: run.font.italic ?? cellFont?.italic,
600
- strike: run.font.strike ?? cellFont?.strike,
601
- underline: run.font.underline ?? cellFont?.underline,
598
+ bold: run.font.bold ?? false,
599
+ italic: run.font.italic ?? false,
600
+ strike: run.font.strike ?? false,
601
+ underline: run.font.underline ?? undefined,
602
602
  color: run.font.color ?? cellFont?.color
603
603
  }
604
604
  : cellFont;
@@ -614,8 +614,8 @@ function countRichTextWrapLines(text, runs, scaleFactor, effectiveWidth, fontMan
614
614
  ? {
615
615
  name: run.font.name ?? cellFont?.name,
616
616
  size: run.font.size ?? cellFont?.size,
617
- bold: run.font.bold ?? cellFont?.bold,
618
- italic: run.font.italic ?? cellFont?.italic
617
+ bold: run.font.bold ?? false,
618
+ italic: run.font.italic ?? false
619
619
  }
620
620
  : cellFont;
621
621
  const fontProps = (0, style_converter_1.extractFontProperties)(effectiveRunFont, defaultFamily, defaultSize);
@@ -1389,15 +1389,19 @@ function buildRichTextRuns(cell, options, fontManager, scaleFactor, cellFont) {
1389
1389
  const defaultSize = cellFont?.size ?? options.defaultFontSize;
1390
1390
  return runs.map(run => {
1391
1391
  // When a run has no font at all, use cell font entirely.
1392
- // When a run has a partial font, merge with cell font for missing properties.
1392
+ // When a run has its own <rPr>, properties NOT listed in that <rPr> default
1393
+ // to their base values (false/undefined), NOT to the cell font.
1394
+ // Only `name` and `size` fall back to cell font (they define the typeface/size
1395
+ // context), but stylistic flags (bold, italic, strike, underline) do not
1396
+ // inherit from the cell — an absent <b/> means "not bold".
1393
1397
  const effectiveFont = run.font
1394
1398
  ? {
1395
1399
  name: run.font.name ?? cellFont?.name,
1396
1400
  size: run.font.size ?? cellFont?.size,
1397
- bold: run.font.bold ?? cellFont?.bold,
1398
- italic: run.font.italic ?? cellFont?.italic,
1399
- strike: run.font.strike ?? cellFont?.strike,
1400
- underline: run.font.underline ?? cellFont?.underline,
1401
+ bold: run.font.bold ?? false,
1402
+ italic: run.font.italic ?? false,
1403
+ strike: run.font.strike ?? false,
1404
+ underline: run.font.underline ?? undefined,
1401
1405
  color: run.font.color ?? cellFont?.color
1402
1406
  }
1403
1407
  : cellFont;
@@ -85,9 +85,17 @@ function renderPage(page, options, fontManager, totalPages) {
85
85
  });
86
86
  }
87
87
  }
88
- // Draw white in unfilled portions of the overflow area
88
+ // Draw white in unfilled portions of the overflow area.
89
+ // Inset vertically by a small amount to avoid covering the horizontal
90
+ // border lines at the top/bottom edges of the cell.
91
+ const borderInset = 0.25;
92
+ const eraseY = cellY + borderInset;
93
+ const eraseH = cellH - borderInset * 2;
94
+ if (eraseH <= 0) {
95
+ continue;
96
+ }
89
97
  if (filledRanges.length === 0) {
90
- stream.fillRect(overflowLeft, cellY, cell.textOverflowWidth, cellH, { r: 1, g: 1, b: 1 });
98
+ stream.fillRect(overflowLeft, eraseY, cell.textOverflowWidth, eraseH, { r: 1, g: 1, b: 1 });
91
99
  }
92
100
  else {
93
101
  // Sort filled ranges and draw white in gaps
@@ -95,12 +103,12 @@ function renderPage(page, options, fontManager, totalPages) {
95
103
  let cursor = overflowLeft;
96
104
  for (const fr of filledRanges) {
97
105
  if (fr.left > cursor) {
98
- stream.fillRect(cursor, cellY, fr.left - cursor, cellH, { r: 1, g: 1, b: 1 });
106
+ stream.fillRect(cursor, eraseY, fr.left - cursor, eraseH, { r: 1, g: 1, b: 1 });
99
107
  }
100
108
  cursor = Math.max(cursor, fr.right);
101
109
  }
102
110
  if (cursor < overflowRight) {
103
- stream.fillRect(cursor, cellY, overflowRight - cursor, cellH, { r: 1, g: 1, b: 1 });
111
+ stream.fillRect(cursor, eraseY, overflowRight - cursor, eraseH, { r: 1, g: 1, b: 1 });
104
112
  }
105
113
  }
106
114
  }
@@ -371,12 +379,26 @@ function drawRichText(stream, cell, fontManager, indentPts, scaleFactor = 1) {
371
379
  const runResources = runs.map(r => runResource(r));
372
380
  // Word-wrap using actual per-run measurements — returns character ranges
373
381
  const lineRanges = wrapRichTextLines(fullText, runForChar, runs, runResources, fontManager, availWidth);
382
+ // Compute per-line heights based on the maximum font size in each line
383
+ const lineHeights = [];
384
+ for (const range of lineRanges) {
385
+ let lineMaxFont = cell.fontSize;
386
+ for (let ci = range.start; ci < range.end; ci++) {
387
+ const ri = runForChar[ci] ?? 0;
388
+ if (runs[ri].fontSize > lineMaxFont) {
389
+ lineMaxFont = runs[ri].fontSize;
390
+ }
391
+ }
392
+ lineHeights.push(lineMaxFont * constants_1.LINE_HEIGHT_FACTOR);
393
+ }
374
394
  const primaryResourceName = runResources[0];
375
395
  const ascent = fontManager.getFontAscent(primaryResourceName, primaryFontSize);
376
- const totalTextHeight = lineRanges.length * lineHeight;
396
+ const totalTextHeight = lineHeights.reduce((sum, h) => sum + h, 0);
377
397
  const textStartY = computeTextStartY(verticalAlign, rect, totalTextHeight, ascent, pad.top, pad.bottom);
398
+ let cumulativeY = 0;
378
399
  for (let li = 0; li < lineRanges.length; li++) {
379
- const lineY = textStartY - li * lineHeight;
400
+ const lineY = textStartY - cumulativeY;
401
+ cumulativeY += lineHeights[li];
380
402
  const { start: lineStart, end: lineEnd } = lineRanges[li];
381
403
  // Split the line into segments by run
382
404
  const segments = [];
@@ -291,33 +291,33 @@ function excelFillToPdfColor(fill) {
291
291
  function borderStyleToLineWidth(style) {
292
292
  switch (style) {
293
293
  case "thin":
294
- return 0.25;
295
- case "medium":
296
294
  return 0.5;
295
+ case "medium":
296
+ return 1.0;
297
297
  case "thick":
298
- return 1;
298
+ return 2.0;
299
299
  case "double":
300
- return 0.25;
300
+ return 0.5;
301
301
  case "hair":
302
- return 0.1;
302
+ return 0.2;
303
303
  case "dotted":
304
- return 0.25;
304
+ return 0.5;
305
305
  case "dashed":
306
- return 0.25;
306
+ return 0.5;
307
307
  case "dashDot":
308
- return 0.25;
308
+ return 0.5;
309
309
  case "dashDotDot":
310
- return 0.25;
310
+ return 0.5;
311
311
  case "slantDashDot":
312
- return 0.25;
313
- case "mediumDashed":
314
312
  return 0.5;
313
+ case "mediumDashed":
314
+ return 1.0;
315
315
  case "mediumDashDot":
316
- return 0.5;
316
+ return 1.0;
317
317
  case "mediumDashDotDot":
318
- return 0.5;
318
+ return 1.0;
319
319
  default:
320
- return 0.25;
320
+ return 0.5;
321
321
  }
322
322
  }
323
323
  /**
@@ -588,10 +588,10 @@ function countRichTextWrapLines(text, runs, scaleFactor, effectiveWidth, fontMan
588
588
  ? {
589
589
  name: run.font.name ?? cellFont?.name,
590
590
  size: run.font.size ?? cellFont?.size,
591
- bold: run.font.bold ?? cellFont?.bold,
592
- italic: run.font.italic ?? cellFont?.italic,
593
- strike: run.font.strike ?? cellFont?.strike,
594
- underline: run.font.underline ?? cellFont?.underline,
591
+ bold: run.font.bold ?? false,
592
+ italic: run.font.italic ?? false,
593
+ strike: run.font.strike ?? false,
594
+ underline: run.font.underline ?? undefined,
595
595
  color: run.font.color ?? cellFont?.color
596
596
  }
597
597
  : cellFont;
@@ -607,8 +607,8 @@ function countRichTextWrapLines(text, runs, scaleFactor, effectiveWidth, fontMan
607
607
  ? {
608
608
  name: run.font.name ?? cellFont?.name,
609
609
  size: run.font.size ?? cellFont?.size,
610
- bold: run.font.bold ?? cellFont?.bold,
611
- italic: run.font.italic ?? cellFont?.italic
610
+ bold: run.font.bold ?? false,
611
+ italic: run.font.italic ?? false
612
612
  }
613
613
  : cellFont;
614
614
  const fontProps = extractFontProperties(effectiveRunFont, defaultFamily, defaultSize);
@@ -1382,15 +1382,19 @@ function buildRichTextRuns(cell, options, fontManager, scaleFactor, cellFont) {
1382
1382
  const defaultSize = cellFont?.size ?? options.defaultFontSize;
1383
1383
  return runs.map(run => {
1384
1384
  // When a run has no font at all, use cell font entirely.
1385
- // When a run has a partial font, merge with cell font for missing properties.
1385
+ // When a run has its own <rPr>, properties NOT listed in that <rPr> default
1386
+ // to their base values (false/undefined), NOT to the cell font.
1387
+ // Only `name` and `size` fall back to cell font (they define the typeface/size
1388
+ // context), but stylistic flags (bold, italic, strike, underline) do not
1389
+ // inherit from the cell — an absent <b/> means "not bold".
1386
1390
  const effectiveFont = run.font
1387
1391
  ? {
1388
1392
  name: run.font.name ?? cellFont?.name,
1389
1393
  size: run.font.size ?? cellFont?.size,
1390
- bold: run.font.bold ?? cellFont?.bold,
1391
- italic: run.font.italic ?? cellFont?.italic,
1392
- strike: run.font.strike ?? cellFont?.strike,
1393
- underline: run.font.underline ?? cellFont?.underline,
1394
+ bold: run.font.bold ?? false,
1395
+ italic: run.font.italic ?? false,
1396
+ strike: run.font.strike ?? false,
1397
+ underline: run.font.underline ?? undefined,
1394
1398
  color: run.font.color ?? cellFont?.color
1395
1399
  }
1396
1400
  : cellFont;
@@ -76,9 +76,17 @@ export function renderPage(page, options, fontManager, totalPages) {
76
76
  });
77
77
  }
78
78
  }
79
- // Draw white in unfilled portions of the overflow area
79
+ // Draw white in unfilled portions of the overflow area.
80
+ // Inset vertically by a small amount to avoid covering the horizontal
81
+ // border lines at the top/bottom edges of the cell.
82
+ const borderInset = 0.25;
83
+ const eraseY = cellY + borderInset;
84
+ const eraseH = cellH - borderInset * 2;
85
+ if (eraseH <= 0) {
86
+ continue;
87
+ }
80
88
  if (filledRanges.length === 0) {
81
- stream.fillRect(overflowLeft, cellY, cell.textOverflowWidth, cellH, { r: 1, g: 1, b: 1 });
89
+ stream.fillRect(overflowLeft, eraseY, cell.textOverflowWidth, eraseH, { r: 1, g: 1, b: 1 });
82
90
  }
83
91
  else {
84
92
  // Sort filled ranges and draw white in gaps
@@ -86,12 +94,12 @@ export function renderPage(page, options, fontManager, totalPages) {
86
94
  let cursor = overflowLeft;
87
95
  for (const fr of filledRanges) {
88
96
  if (fr.left > cursor) {
89
- stream.fillRect(cursor, cellY, fr.left - cursor, cellH, { r: 1, g: 1, b: 1 });
97
+ stream.fillRect(cursor, eraseY, fr.left - cursor, eraseH, { r: 1, g: 1, b: 1 });
90
98
  }
91
99
  cursor = Math.max(cursor, fr.right);
92
100
  }
93
101
  if (cursor < overflowRight) {
94
- stream.fillRect(cursor, cellY, overflowRight - cursor, cellH, { r: 1, g: 1, b: 1 });
102
+ stream.fillRect(cursor, eraseY, overflowRight - cursor, eraseH, { r: 1, g: 1, b: 1 });
95
103
  }
96
104
  }
97
105
  }
@@ -362,12 +370,26 @@ function drawRichText(stream, cell, fontManager, indentPts, scaleFactor = 1) {
362
370
  const runResources = runs.map(r => runResource(r));
363
371
  // Word-wrap using actual per-run measurements — returns character ranges
364
372
  const lineRanges = wrapRichTextLines(fullText, runForChar, runs, runResources, fontManager, availWidth);
373
+ // Compute per-line heights based on the maximum font size in each line
374
+ const lineHeights = [];
375
+ for (const range of lineRanges) {
376
+ let lineMaxFont = cell.fontSize;
377
+ for (let ci = range.start; ci < range.end; ci++) {
378
+ const ri = runForChar[ci] ?? 0;
379
+ if (runs[ri].fontSize > lineMaxFont) {
380
+ lineMaxFont = runs[ri].fontSize;
381
+ }
382
+ }
383
+ lineHeights.push(lineMaxFont * LINE_HEIGHT_FACTOR);
384
+ }
365
385
  const primaryResourceName = runResources[0];
366
386
  const ascent = fontManager.getFontAscent(primaryResourceName, primaryFontSize);
367
- const totalTextHeight = lineRanges.length * lineHeight;
387
+ const totalTextHeight = lineHeights.reduce((sum, h) => sum + h, 0);
368
388
  const textStartY = computeTextStartY(verticalAlign, rect, totalTextHeight, ascent, pad.top, pad.bottom);
389
+ let cumulativeY = 0;
369
390
  for (let li = 0; li < lineRanges.length; li++) {
370
- const lineY = textStartY - li * lineHeight;
391
+ const lineY = textStartY - cumulativeY;
392
+ cumulativeY += lineHeights[li];
371
393
  const { start: lineStart, end: lineEnd } = lineRanges[li];
372
394
  // Split the line into segments by run
373
395
  const segments = [];
@@ -279,33 +279,33 @@ export function excelFillToPdfColor(fill) {
279
279
  export function borderStyleToLineWidth(style) {
280
280
  switch (style) {
281
281
  case "thin":
282
- return 0.25;
283
- case "medium":
284
282
  return 0.5;
283
+ case "medium":
284
+ return 1.0;
285
285
  case "thick":
286
- return 1;
286
+ return 2.0;
287
287
  case "double":
288
- return 0.25;
288
+ return 0.5;
289
289
  case "hair":
290
- return 0.1;
290
+ return 0.2;
291
291
  case "dotted":
292
- return 0.25;
292
+ return 0.5;
293
293
  case "dashed":
294
- return 0.25;
294
+ return 0.5;
295
295
  case "dashDot":
296
- return 0.25;
296
+ return 0.5;
297
297
  case "dashDotDot":
298
- return 0.25;
298
+ return 0.5;
299
299
  case "slantDashDot":
300
- return 0.25;
301
- case "mediumDashed":
302
300
  return 0.5;
301
+ case "mediumDashed":
302
+ return 1.0;
303
303
  case "mediumDashDot":
304
- return 0.5;
304
+ return 1.0;
305
305
  case "mediumDashDotDot":
306
- return 0.5;
306
+ return 1.0;
307
307
  default:
308
- return 0.25;
308
+ return 0.5;
309
309
  }
310
310
  }
311
311
  /**
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @cj-tech-master/excelts v9.5.1
2
+ * @cj-tech-master/excelts v9.5.2
3
3
  * Zero-dependency TypeScript toolkit — Excel (XLSX), PDF, CSV, Markdown, XML, ZIP/TAR, and streaming.
4
4
  * (c) 2026 cjnoname
5
5
  * Released under the MIT License
@@ -87277,7 +87277,11 @@ self.onmessage = async function(event) {
87277
87277
  right: Math.min(oRight, overflowRight)
87278
87278
  });
87279
87279
  }
87280
- if (filledRanges.length === 0) stream.fillRect(overflowLeft, cellY, cell.textOverflowWidth, cellH, {
87280
+ const borderInset = .25;
87281
+ const eraseY = cellY + borderInset;
87282
+ const eraseH = cellH - borderInset * 2;
87283
+ if (eraseH <= 0) continue;
87284
+ if (filledRanges.length === 0) stream.fillRect(overflowLeft, eraseY, cell.textOverflowWidth, eraseH, {
87281
87285
  r: 1,
87282
87286
  g: 1,
87283
87287
  b: 1
@@ -87286,14 +87290,14 @@ self.onmessage = async function(event) {
87286
87290
  filledRanges.sort((a, b) => a.left - b.left);
87287
87291
  let cursor = overflowLeft;
87288
87292
  for (const fr of filledRanges) {
87289
- if (fr.left > cursor) stream.fillRect(cursor, cellY, fr.left - cursor, cellH, {
87293
+ if (fr.left > cursor) stream.fillRect(cursor, eraseY, fr.left - cursor, eraseH, {
87290
87294
  r: 1,
87291
87295
  g: 1,
87292
87296
  b: 1
87293
87297
  });
87294
87298
  cursor = Math.max(cursor, fr.right);
87295
87299
  }
87296
- if (cursor < overflowRight) stream.fillRect(cursor, cellY, overflowRight - cursor, cellH, {
87300
+ if (cursor < overflowRight) stream.fillRect(cursor, eraseY, overflowRight - cursor, eraseH, {
87297
87301
  r: 1,
87298
87302
  g: 1,
87299
87303
  b: 1
@@ -87469,11 +87473,22 @@ self.onmessage = async function(event) {
87469
87473
  for (let ri = 0; ri < runs.length; ri++) for (let ci = 0; ci < runs[ri].text.length; ci++) runForChar.push(ri);
87470
87474
  const runResources = runs.map((r) => runResource(r));
87471
87475
  const lineRanges = wrapRichTextLines(fullText, runForChar, runs, runResources, fontManager, availWidth);
87476
+ const lineHeights = [];
87477
+ for (const range of lineRanges) {
87478
+ let lineMaxFont = cell.fontSize;
87479
+ for (let ci = range.start; ci < range.end; ci++) {
87480
+ const ri = runForChar[ci] ?? 0;
87481
+ if (runs[ri].fontSize > lineMaxFont) lineMaxFont = runs[ri].fontSize;
87482
+ }
87483
+ lineHeights.push(lineMaxFont * LINE_HEIGHT_FACTOR);
87484
+ }
87472
87485
  const primaryResourceName = runResources[0];
87473
87486
  const ascent = fontManager.getFontAscent(primaryResourceName, primaryFontSize);
87474
- const textStartY = computeTextStartY(verticalAlign, rect, lineRanges.length * lineHeight, ascent, pad.top, pad.bottom);
87487
+ const textStartY = computeTextStartY(verticalAlign, rect, lineHeights.reduce((sum, h) => sum + h, 0), ascent, pad.top, pad.bottom);
87488
+ let cumulativeY = 0;
87475
87489
  for (let li = 0; li < lineRanges.length; li++) {
87476
- const lineY = textStartY - li * lineHeight;
87490
+ const lineY = textStartY - cumulativeY;
87491
+ cumulativeY += lineHeights[li];
87477
87492
  const { start: lineStart, end: lineEnd } = lineRanges[li];
87478
87493
  const segments = [];
87479
87494
  for (let ci = lineStart; ci < lineEnd; ci++) {
@@ -88623,20 +88638,20 @@ self.onmessage = async function(event) {
88623
88638
  */
88624
88639
  function borderStyleToLineWidth(style) {
88625
88640
  switch (style) {
88626
- case "thin": return .25;
88627
- case "medium": return .5;
88628
- case "thick": return 1;
88629
- case "double": return .25;
88630
- case "hair": return .1;
88631
- case "dotted": return .25;
88632
- case "dashed": return .25;
88633
- case "dashDot": return .25;
88634
- case "dashDotDot": return .25;
88635
- case "slantDashDot": return .25;
88636
- case "mediumDashed": return .5;
88637
- case "mediumDashDot": return .5;
88638
- case "mediumDashDotDot": return .5;
88639
- default: return .25;
88641
+ case "thin": return .5;
88642
+ case "medium": return 1;
88643
+ case "thick": return 2;
88644
+ case "double": return .5;
88645
+ case "hair": return .2;
88646
+ case "dotted": return .5;
88647
+ case "dashed": return .5;
88648
+ case "dashDot": return .5;
88649
+ case "dashDotDot": return .5;
88650
+ case "slantDashDot": return .5;
88651
+ case "mediumDashed": return 1;
88652
+ case "mediumDashDot": return 1;
88653
+ case "mediumDashDotDot": return 1;
88654
+ default: return .5;
88640
88655
  }
88641
88656
  }
88642
88657
  /**
@@ -89182,10 +89197,10 @@ self.onmessage = async function(event) {
89182
89197
  const fontProps = extractFontProperties(run.font ? {
89183
89198
  name: run.font.name ?? cellFont?.name,
89184
89199
  size: run.font.size ?? cellFont?.size,
89185
- bold: run.font.bold ?? cellFont?.bold,
89186
- italic: run.font.italic ?? cellFont?.italic,
89187
- strike: run.font.strike ?? cellFont?.strike,
89188
- underline: run.font.underline ?? cellFont?.underline,
89200
+ bold: run.font.bold ?? false,
89201
+ italic: run.font.italic ?? false,
89202
+ strike: run.font.strike ?? false,
89203
+ underline: run.font.underline ?? void 0,
89189
89204
  color: run.font.color ?? cellFont?.color
89190
89205
  } : cellFont, defaultFamily, defaultSize);
89191
89206
  const pdfFontName = resolvePdfFontName(fontProps.fontFamily, fontProps.bold, fontProps.italic);
@@ -89195,8 +89210,8 @@ self.onmessage = async function(event) {
89195
89210
  return extractFontProperties(run.font ? {
89196
89211
  name: run.font.name ?? cellFont?.name,
89197
89212
  size: run.font.size ?? cellFont?.size,
89198
- bold: run.font.bold ?? cellFont?.bold,
89199
- italic: run.font.italic ?? cellFont?.italic
89213
+ bold: run.font.bold ?? false,
89214
+ italic: run.font.italic ?? false
89200
89215
  } : cellFont, defaultFamily, defaultSize).fontSize * scaleFactor;
89201
89216
  });
89202
89217
  const measureRange = (start, end) => {
@@ -89763,10 +89778,10 @@ self.onmessage = async function(event) {
89763
89778
  const fontProps = extractFontProperties(run.font ? {
89764
89779
  name: run.font.name ?? cellFont?.name,
89765
89780
  size: run.font.size ?? cellFont?.size,
89766
- bold: run.font.bold ?? cellFont?.bold,
89767
- italic: run.font.italic ?? cellFont?.italic,
89768
- strike: run.font.strike ?? cellFont?.strike,
89769
- underline: run.font.underline ?? cellFont?.underline,
89781
+ bold: run.font.bold ?? false,
89782
+ italic: run.font.italic ?? false,
89783
+ strike: run.font.strike ?? false,
89784
+ underline: run.font.underline ?? void 0,
89770
89785
  color: run.font.color ?? cellFont?.color
89771
89786
  } : cellFont, defaultFamily, defaultSize);
89772
89787
  if (fontManager.hasEmbeddedFont()) fontManager.trackText(run.text);