@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.
- package/dist/browser/modules/pdf/render/layout-engine.js +15 -11
- package/dist/browser/modules/pdf/render/page-renderer.js +28 -6
- package/dist/browser/modules/pdf/render/style-converter.js +14 -14
- package/dist/cjs/modules/pdf/render/layout-engine.js +15 -11
- package/dist/cjs/modules/pdf/render/page-renderer.js +28 -6
- package/dist/cjs/modules/pdf/render/style-converter.js +14 -14
- package/dist/esm/modules/pdf/render/layout-engine.js +15 -11
- package/dist/esm/modules/pdf/render/page-renderer.js +28 -6
- package/dist/esm/modules/pdf/render/style-converter.js +14 -14
- package/dist/iife/excelts.iife.js +45 -30
- package/dist/iife/excelts.iife.js.map +1 -1
- package/dist/iife/excelts.iife.min.js +4 -4
- package/package.json +1 -1
|
@@ -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 ??
|
|
592
|
-
italic: run.font.italic ??
|
|
593
|
-
strike: run.font.strike ??
|
|
594
|
-
underline: run.font.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 ??
|
|
611
|
-
italic: run.font.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
|
|
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 ??
|
|
1391
|
-
italic: run.font.italic ??
|
|
1392
|
-
strike: run.font.strike ??
|
|
1393
|
-
underline: run.font.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,
|
|
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,
|
|
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,
|
|
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 =
|
|
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 -
|
|
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
|
|
286
|
+
return 2.0;
|
|
287
287
|
case "double":
|
|
288
|
-
return 0.
|
|
288
|
+
return 0.5;
|
|
289
289
|
case "hair":
|
|
290
|
-
return 0.
|
|
290
|
+
return 0.2;
|
|
291
291
|
case "dotted":
|
|
292
|
-
return 0.
|
|
292
|
+
return 0.5;
|
|
293
293
|
case "dashed":
|
|
294
|
-
return 0.
|
|
294
|
+
return 0.5;
|
|
295
295
|
case "dashDot":
|
|
296
|
-
return 0.
|
|
296
|
+
return 0.5;
|
|
297
297
|
case "dashDotDot":
|
|
298
|
-
return 0.
|
|
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
|
|
304
|
+
return 1.0;
|
|
305
305
|
case "mediumDashDotDot":
|
|
306
|
-
return 0
|
|
306
|
+
return 1.0;
|
|
307
307
|
default:
|
|
308
|
-
return 0.
|
|
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 ??
|
|
599
|
-
italic: run.font.italic ??
|
|
600
|
-
strike: run.font.strike ??
|
|
601
|
-
underline: run.font.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 ??
|
|
618
|
-
italic: run.font.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
|
|
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 ??
|
|
1398
|
-
italic: run.font.italic ??
|
|
1399
|
-
strike: run.font.strike ??
|
|
1400
|
-
underline: run.font.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,
|
|
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,
|
|
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,
|
|
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 =
|
|
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 -
|
|
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
|
|
298
|
+
return 2.0;
|
|
299
299
|
case "double":
|
|
300
|
-
return 0.
|
|
300
|
+
return 0.5;
|
|
301
301
|
case "hair":
|
|
302
|
-
return 0.
|
|
302
|
+
return 0.2;
|
|
303
303
|
case "dotted":
|
|
304
|
-
return 0.
|
|
304
|
+
return 0.5;
|
|
305
305
|
case "dashed":
|
|
306
|
-
return 0.
|
|
306
|
+
return 0.5;
|
|
307
307
|
case "dashDot":
|
|
308
|
-
return 0.
|
|
308
|
+
return 0.5;
|
|
309
309
|
case "dashDotDot":
|
|
310
|
-
return 0.
|
|
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
|
|
316
|
+
return 1.0;
|
|
317
317
|
case "mediumDashDotDot":
|
|
318
|
-
return 0
|
|
318
|
+
return 1.0;
|
|
319
319
|
default:
|
|
320
|
-
return 0.
|
|
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 ??
|
|
592
|
-
italic: run.font.italic ??
|
|
593
|
-
strike: run.font.strike ??
|
|
594
|
-
underline: run.font.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 ??
|
|
611
|
-
italic: run.font.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
|
|
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 ??
|
|
1391
|
-
italic: run.font.italic ??
|
|
1392
|
-
strike: run.font.strike ??
|
|
1393
|
-
underline: run.font.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,
|
|
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,
|
|
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,
|
|
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 =
|
|
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 -
|
|
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
|
|
286
|
+
return 2.0;
|
|
287
287
|
case "double":
|
|
288
|
-
return 0.
|
|
288
|
+
return 0.5;
|
|
289
289
|
case "hair":
|
|
290
|
-
return 0.
|
|
290
|
+
return 0.2;
|
|
291
291
|
case "dotted":
|
|
292
|
-
return 0.
|
|
292
|
+
return 0.5;
|
|
293
293
|
case "dashed":
|
|
294
|
-
return 0.
|
|
294
|
+
return 0.5;
|
|
295
295
|
case "dashDot":
|
|
296
|
-
return 0.
|
|
296
|
+
return 0.5;
|
|
297
297
|
case "dashDotDot":
|
|
298
|
-
return 0.
|
|
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
|
|
304
|
+
return 1.0;
|
|
305
305
|
case "mediumDashDotDot":
|
|
306
|
-
return 0
|
|
306
|
+
return 1.0;
|
|
307
307
|
default:
|
|
308
|
-
return 0.
|
|
308
|
+
return 0.5;
|
|
309
309
|
}
|
|
310
310
|
}
|
|
311
311
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* @cj-tech-master/excelts v9.5.
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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 -
|
|
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 .
|
|
88627
|
-
case "medium": return
|
|
88628
|
-
case "thick": return
|
|
88629
|
-
case "double": return .
|
|
88630
|
-
case "hair": return .
|
|
88631
|
-
case "dotted": return .
|
|
88632
|
-
case "dashed": return .
|
|
88633
|
-
case "dashDot": return .
|
|
88634
|
-
case "dashDotDot": return .
|
|
88635
|
-
case "slantDashDot": return .
|
|
88636
|
-
case "mediumDashed": return
|
|
88637
|
-
case "mediumDashDot": return
|
|
88638
|
-
case "mediumDashDotDot": return
|
|
88639
|
-
default: return .
|
|
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 ??
|
|
89186
|
-
italic: run.font.italic ??
|
|
89187
|
-
strike: run.font.strike ??
|
|
89188
|
-
underline: run.font.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 ??
|
|
89199
|
-
italic: run.font.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 ??
|
|
89767
|
-
italic: run.font.italic ??
|
|
89768
|
-
strike: run.font.strike ??
|
|
89769
|
-
underline: run.font.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);
|