officeparser 7.1.0 → 7.2.0
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/README.md +93 -18
- package/dist/OfficeGenerator.d.ts +1 -1
- package/dist/OfficeGenerator.js +16 -7
- package/dist/cli.d.ts +4 -0
- package/dist/cli.js +12 -3
- package/dist/defaults.js +11 -0
- package/dist/generators/BaseGenerator.d.ts +3 -3
- package/dist/generators/ChunkingGenerator.js +8 -1
- package/dist/generators/CsvGenerator.d.ts +1 -1
- package/dist/generators/HtmlGenerator.d.ts +2 -1
- package/dist/generators/HtmlGenerator.js +462 -40
- package/dist/generators/MarkdownGenerator.d.ts +1 -1
- package/dist/generators/MarkdownGenerator.js +3 -1
- package/dist/generators/PdfGenerator.d.ts +1 -1
- package/dist/generators/PdfGenerator.js +0 -6
- package/dist/generators/RtfGenerator.d.ts +2 -1
- package/dist/generators/RtfGenerator.js +43 -6
- package/dist/generators/TextGenerator.d.ts +1 -1
- package/dist/officeparser.browser.d.ts +259 -52
- package/dist/officeparser.browser.iife.js +380 -93
- package/dist/officeparser.browser.mjs +380 -93
- package/dist/parsers/CsvParser.js +1 -1
- package/dist/parsers/ExcelParser.js +63 -19
- package/dist/parsers/HtmlParser.js +10 -1
- package/dist/parsers/MarkdownParser.js +13 -10
- package/dist/parsers/OpenOfficeParser.js +57 -34
- package/dist/parsers/PdfParser.js +23 -1
- package/dist/parsers/PowerPointParser.js +164 -40
- package/dist/parsers/RtfParser.js +28 -24
- package/dist/parsers/WordParser.js +154 -11
- package/dist/sbom.cdx.json +100 -100
- package/dist/types.d.ts +265 -52
- package/dist/types.js +2 -0
- package/dist/utils/astUtils.d.ts +2 -2
- package/dist/utils/astUtils.js +2 -1
- package/dist/utils/configUtils.d.ts +5 -0
- package/dist/utils/configUtils.js +55 -1
- package/dist/utils/errorUtils.js +2 -1
- package/dist/utils/xmlUtils.d.ts +9 -0
- package/dist/utils/xmlUtils.js +53 -1
- package/package.json +1 -1
|
@@ -50,6 +50,120 @@ class HtmlGenerator extends BaseGenerator_js_1.BaseGenerator {
|
|
|
50
50
|
spreadsheetTabs = `<div class="spreadsheet-tabs">${tabs}</div>`;
|
|
51
51
|
spreadsheetScript = `
|
|
52
52
|
<script>
|
|
53
|
+
function initSpreadsheetResizing() {
|
|
54
|
+
document.querySelectorAll('.excel-grid').forEach(table => {
|
|
55
|
+
if (table.dataset.resizingInitialized) return;
|
|
56
|
+
if (table.offsetWidth === 0) return; // skip hidden tables
|
|
57
|
+
table.dataset.resizingInitialized = 'true';
|
|
58
|
+
|
|
59
|
+
const sheetId = table.parentElement.id || 'sheet';
|
|
60
|
+
const docId = window.location.pathname;
|
|
61
|
+
|
|
62
|
+
// Freeze initial auto-layout widths of columns and set table layout to fixed
|
|
63
|
+
const colHeaders = table.querySelectorAll('.excel-col-header');
|
|
64
|
+
colHeaders.forEach((header, index) => {
|
|
65
|
+
let currentWidth = header.offsetWidth;
|
|
66
|
+
const savedWidth = localStorage.getItem(docId + '_' + sheetId + '_col_' + index);
|
|
67
|
+
if (savedWidth) {
|
|
68
|
+
currentWidth = parseInt(savedWidth, 10);
|
|
69
|
+
}
|
|
70
|
+
header.style.width = currentWidth + 'px';
|
|
71
|
+
header.style.minWidth = currentWidth + 'px';
|
|
72
|
+
});
|
|
73
|
+
table.style.width = table.offsetWidth + 'px';
|
|
74
|
+
table.style.tableLayout = 'fixed';
|
|
75
|
+
|
|
76
|
+
// Freeze initial auto-layout heights of rows
|
|
77
|
+
table.querySelectorAll('tr').forEach((row, index) => {
|
|
78
|
+
let currentHeight = row.offsetHeight;
|
|
79
|
+
const savedHeight = localStorage.getItem(docId + '_' + sheetId + '_row_' + index);
|
|
80
|
+
if (savedHeight) {
|
|
81
|
+
currentHeight = parseInt(savedHeight, 10);
|
|
82
|
+
}
|
|
83
|
+
row.style.height = currentHeight + 'px';
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
colHeaders.forEach((header, index) => {
|
|
87
|
+
if (header.querySelector('.col-resizer')) return;
|
|
88
|
+
const resizer = document.createElement('div');
|
|
89
|
+
resizer.className = 'col-resizer';
|
|
90
|
+
header.appendChild(resizer);
|
|
91
|
+
|
|
92
|
+
let startX = 0;
|
|
93
|
+
let startWidth = 0;
|
|
94
|
+
let startTableWidth = 0;
|
|
95
|
+
|
|
96
|
+
const onMouseMove = (e) => {
|
|
97
|
+
const width = startWidth + (e.clientX - startX);
|
|
98
|
+
if (width > 40) {
|
|
99
|
+
header.style.width = width + 'px';
|
|
100
|
+
header.style.minWidth = width + 'px';
|
|
101
|
+
table.style.width = (startTableWidth + (width - startWidth)) + 'px';
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const onMouseUp = (e) => {
|
|
106
|
+
resizer.classList.remove('resizing');
|
|
107
|
+
document.removeEventListener('mousemove', onMouseMove);
|
|
108
|
+
document.removeEventListener('mouseup', onMouseUp);
|
|
109
|
+
const finalWidth = startWidth + (e.clientX - startX);
|
|
110
|
+
if (finalWidth > 40) {
|
|
111
|
+
localStorage.setItem(docId + '_' + sheetId + '_col_' + index, finalWidth);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
resizer.addEventListener('mousedown', (e) => {
|
|
116
|
+
e.preventDefault();
|
|
117
|
+
e.stopPropagation();
|
|
118
|
+
startX = e.clientX;
|
|
119
|
+
startWidth = header.offsetWidth;
|
|
120
|
+
startTableWidth = table.offsetWidth;
|
|
121
|
+
resizer.classList.add('resizing');
|
|
122
|
+
document.addEventListener('mousemove', onMouseMove);
|
|
123
|
+
document.addEventListener('mouseup', onMouseUp);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
table.querySelectorAll('.excel-row-num').forEach((rowHeader, index) => {
|
|
128
|
+
if (rowHeader.querySelector('.row-resizer')) return;
|
|
129
|
+
const resizer = document.createElement('div');
|
|
130
|
+
resizer.className = 'row-resizer';
|
|
131
|
+
rowHeader.appendChild(resizer);
|
|
132
|
+
|
|
133
|
+
const row = rowHeader.parentElement;
|
|
134
|
+
let startY = 0;
|
|
135
|
+
let startHeight = 0;
|
|
136
|
+
|
|
137
|
+
const onMouseMove = (e) => {
|
|
138
|
+
const height = startHeight + (e.clientY - startY);
|
|
139
|
+
if (height > 20) {
|
|
140
|
+
row.style.height = height + 'px';
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const onMouseUp = (e) => {
|
|
145
|
+
resizer.classList.remove('resizing');
|
|
146
|
+
document.removeEventListener('mousemove', onMouseMove);
|
|
147
|
+
document.removeEventListener('mouseup', onMouseUp);
|
|
148
|
+
const finalHeight = startHeight + (e.clientY - startY);
|
|
149
|
+
if (finalHeight > 20) {
|
|
150
|
+
localStorage.setItem(docId + '_' + sheetId + '_row_' + index, finalHeight);
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
resizer.addEventListener('mousedown', (e) => {
|
|
155
|
+
e.preventDefault();
|
|
156
|
+
e.stopPropagation();
|
|
157
|
+
startY = e.clientY;
|
|
158
|
+
startHeight = row.offsetHeight;
|
|
159
|
+
resizer.classList.add('resizing');
|
|
160
|
+
document.addEventListener('mousemove', onMouseMove);
|
|
161
|
+
document.addEventListener('mouseup', onMouseUp);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
53
167
|
function switchSheet() {
|
|
54
168
|
try {
|
|
55
169
|
let hash = window.location.hash;
|
|
@@ -79,6 +193,8 @@ class HtmlGenerator extends BaseGenerator_js_1.BaseGenerator {
|
|
|
79
193
|
}
|
|
80
194
|
});
|
|
81
195
|
}
|
|
196
|
+
|
|
197
|
+
initSpreadsheetResizing();
|
|
82
198
|
} catch (e) {
|
|
83
199
|
console.error('Sheet switch failed:', e);
|
|
84
200
|
const firstSheet = document.querySelector('.spreadsheet-sheet');
|
|
@@ -96,9 +212,11 @@ class HtmlGenerator extends BaseGenerator_js_1.BaseGenerator {
|
|
|
96
212
|
metaTags = this.renderMetaTags();
|
|
97
213
|
}
|
|
98
214
|
const styles = this.config.htmlConfig.standalone ? '' : `<style>${this.getPremiumStyles(this.isSpreadsheetMode, isPresentation, isPdf)}</style>`;
|
|
215
|
+
const inj = this.config.htmlConfig.injections;
|
|
99
216
|
const value = this.config.htmlConfig.standalone ? `<!DOCTYPE html>
|
|
100
217
|
<html lang="en">
|
|
101
218
|
<head>
|
|
219
|
+
${inj.headStart}
|
|
102
220
|
<meta charset="UTF-8">
|
|
103
221
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
104
222
|
<title>${this.escape(title)}</title>
|
|
@@ -107,8 +225,10 @@ class HtmlGenerator extends BaseGenerator_js_1.BaseGenerator {
|
|
|
107
225
|
<style>
|
|
108
226
|
${this.getPremiumStyles(this.isSpreadsheetMode, isPresentation, isPdf)}
|
|
109
227
|
</style>
|
|
228
|
+
${inj.headEnd}
|
|
110
229
|
</head>
|
|
111
230
|
<body>
|
|
231
|
+
${inj.bodyStart}
|
|
112
232
|
<div class="${containerClass}">
|
|
113
233
|
<article>
|
|
114
234
|
${metadataBlock}
|
|
@@ -117,6 +237,7 @@ class HtmlGenerator extends BaseGenerator_js_1.BaseGenerator {
|
|
|
117
237
|
${spreadsheetTabs}
|
|
118
238
|
</div>
|
|
119
239
|
${spreadsheetScript}
|
|
240
|
+
${inj.bodyEnd}
|
|
120
241
|
</body>
|
|
121
242
|
</html>` : `${styles}<div class="${containerClass}">${metadataBlock}${bodyContent}${spreadsheetTabs}</div>${spreadsheetScript}`;
|
|
122
243
|
return {
|
|
@@ -490,24 +611,128 @@ class HtmlGenerator extends BaseGenerator_js_1.BaseGenerator {
|
|
|
490
611
|
return `<td${rowSpan}${colSpan}${idAttr}${className}${mappedAttrs}${styleAttr}>${childrenOutput}</td>`;
|
|
491
612
|
}
|
|
492
613
|
case 'sheet': {
|
|
493
|
-
// For sheet-based files, wrap rows in a table
|
|
494
|
-
let finalChildren = childrenOutput;
|
|
495
614
|
const rows = node.children || [];
|
|
496
|
-
//
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
615
|
+
// Find grid bounds
|
|
616
|
+
let maxRow = -1;
|
|
617
|
+
let maxCol = -1;
|
|
618
|
+
for (const child of rows) {
|
|
619
|
+
if (child.type === 'row') {
|
|
620
|
+
for (const cell of child.children || []) {
|
|
621
|
+
if (cell.type === 'cell') {
|
|
622
|
+
const meta = cell.metadata;
|
|
623
|
+
if (meta) {
|
|
624
|
+
const r = meta.row;
|
|
625
|
+
const c = meta.col;
|
|
626
|
+
const rSpan = meta.rowSpan || 1;
|
|
627
|
+
const cSpan = meta.colSpan || 1;
|
|
628
|
+
if (r + rSpan - 1 > maxRow)
|
|
629
|
+
maxRow = r + rSpan - 1;
|
|
630
|
+
if (c + cSpan - 1 > maxCol)
|
|
631
|
+
maxCol = c + cSpan - 1;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
}
|
|
509
635
|
}
|
|
510
636
|
}
|
|
637
|
+
let tableHtml = '';
|
|
638
|
+
if (maxRow >= 0 && maxCol >= 0) {
|
|
639
|
+
// Populate cell grid and track merged cells
|
|
640
|
+
const grid = Array.from({ length: maxRow + 1 }, () => Array(maxCol + 1).fill(null));
|
|
641
|
+
const mergedCovered = Array.from({ length: maxRow + 1 }, () => Array(maxCol + 1).fill(false));
|
|
642
|
+
const rowNodeMap = new Map();
|
|
643
|
+
for (const child of rows) {
|
|
644
|
+
if (child.type === 'row') {
|
|
645
|
+
const cellsInRow = child.children?.filter(c => c.type === 'cell') || [];
|
|
646
|
+
if (cellsInRow.length > 0) {
|
|
647
|
+
const r = cellsInRow[0].metadata.row;
|
|
648
|
+
rowNodeMap.set(r, child);
|
|
649
|
+
for (const cell of cellsInRow) {
|
|
650
|
+
const meta = cell.metadata;
|
|
651
|
+
if (meta) {
|
|
652
|
+
const c = meta.col;
|
|
653
|
+
if (r >= 0 && r <= maxRow && c >= 0 && c <= maxCol) {
|
|
654
|
+
grid[r][c] = cell;
|
|
655
|
+
const rSpan = meta.rowSpan || 1;
|
|
656
|
+
const cSpan = meta.colSpan || 1;
|
|
657
|
+
if (rSpan > 1 || cSpan > 1) {
|
|
658
|
+
for (let rOffset = 0; rOffset < rSpan; rOffset++) {
|
|
659
|
+
for (let cOffset = 0; cOffset < cSpan; cOffset++) {
|
|
660
|
+
if (rOffset === 0 && cOffset === 0)
|
|
661
|
+
continue;
|
|
662
|
+
const targetR = r + rOffset;
|
|
663
|
+
const targetC = c + cOffset;
|
|
664
|
+
if (targetR <= maxRow && targetC <= maxCol) {
|
|
665
|
+
mergedCovered[targetR][targetC] = true;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
// Build column headers (A, B, C...)
|
|
677
|
+
let ths = '<th class="excel-row-num-header"></th>';
|
|
678
|
+
for (let c = 0; c <= maxCol; c++) {
|
|
679
|
+
ths += `<th class="excel-col-header">${this.getColumnLetter(c)}</th>`;
|
|
680
|
+
}
|
|
681
|
+
const thead = `<thead><tr>${ths}</tr></thead>`;
|
|
682
|
+
// Build rows
|
|
683
|
+
let tbodyRows = '';
|
|
684
|
+
for (let r = 0; r <= maxRow; r++) {
|
|
685
|
+
const rowNode = rowNodeMap.get(r);
|
|
686
|
+
let trAttrs = '';
|
|
687
|
+
if (rowNode) {
|
|
688
|
+
const mapping = this.getSemanticMapping(rowNode);
|
|
689
|
+
const rClasses = ['excel-row'];
|
|
690
|
+
if (mapping?.classes)
|
|
691
|
+
rClasses.push(...mapping.classes);
|
|
692
|
+
trAttrs += ` class="${rClasses.join(' ')}"`;
|
|
693
|
+
if (mapping?.attributes) {
|
|
694
|
+
for (const [key, val] of Object.entries(mapping.attributes)) {
|
|
695
|
+
trAttrs += ` ${key}="${this.escape(val)}"`;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
const rAnchorIds = this.config.ignoreInternalLinks ? [] : [...(rowNode.metadata?.anchorIds || [])];
|
|
699
|
+
if (rAnchorIds.length > 0) {
|
|
700
|
+
trAttrs += ` id="${rAnchorIds[0]}"`;
|
|
701
|
+
}
|
|
702
|
+
if (this.config.includeFormatting) {
|
|
703
|
+
const styles = this.getInlineStyles(rowNode);
|
|
704
|
+
if (styles)
|
|
705
|
+
trAttrs += ` style="${styles}"`;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
else {
|
|
709
|
+
trAttrs = ' class="excel-row"';
|
|
710
|
+
}
|
|
711
|
+
let rowCellsHtml = `<td class="excel-row-num">${r + 1}</td>`;
|
|
712
|
+
for (let c = 0; c <= maxCol; c++) {
|
|
713
|
+
if (mergedCovered[r][c]) {
|
|
714
|
+
continue;
|
|
715
|
+
}
|
|
716
|
+
const cell = grid[r][c];
|
|
717
|
+
if (cell) {
|
|
718
|
+
const cellHtml = await this.processNodeRecursive(cell, this.nodeProcessor.bind(this));
|
|
719
|
+
rowCellsHtml += cellHtml;
|
|
720
|
+
}
|
|
721
|
+
else {
|
|
722
|
+
rowCellsHtml += '<td class="excel-cell-empty"></td>';
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
tbodyRows += `<tr${trAttrs}>${rowCellsHtml}</tr>\n`;
|
|
726
|
+
}
|
|
727
|
+
const tbody = `<tbody>${tbodyRows}</tbody>`;
|
|
728
|
+
tableHtml = `<table class="spreadsheet-table excel-grid">${thead}${tbody}</table>`;
|
|
729
|
+
}
|
|
730
|
+
// Process non-row elements (images, charts, etc.)
|
|
731
|
+
const nonRowNodes = rows.filter(c => c.type !== 'row');
|
|
732
|
+
let nonRowHtml = '';
|
|
733
|
+
if (nonRowNodes.length > 0) {
|
|
734
|
+
nonRowHtml = await this.processNodeArray(nonRowNodes);
|
|
735
|
+
}
|
|
511
736
|
const isFirstSheet = this.ast.content.filter(n => n.type === 'sheet')[0] === node;
|
|
512
737
|
const isActive = isFirstSheet;
|
|
513
738
|
const sheetIndex = this.ast.content.filter(n => n.type === 'sheet').indexOf(node);
|
|
@@ -521,11 +746,18 @@ class HtmlGenerator extends BaseGenerator_js_1.BaseGenerator {
|
|
|
521
746
|
const classAttr = ` class="${mergedClasses.join(' ')}"`;
|
|
522
747
|
// Ensure we don't have duplicate IDs
|
|
523
748
|
const finalIdAttr = ` id="${sheetId}"`;
|
|
524
|
-
return `${extraAnchors}<div${finalIdAttr}${classAttr}${mappedAttrs}${styleAttr}
|
|
749
|
+
return `${extraAnchors}<div${finalIdAttr}${classAttr}${mappedAttrs}${styleAttr}>${tableHtml}${nonRowHtml}</div>`;
|
|
525
750
|
}
|
|
526
751
|
case 'paragraph':
|
|
527
752
|
case 'heading': {
|
|
528
753
|
const tag = node.type === 'heading' ? `h${node.metadata?.level || 1}` : 'p';
|
|
754
|
+
// Normalize empty paragraphs so DOCX and PPTX empty cells render with consistent height
|
|
755
|
+
// Strip tags to check if it's purely empty or just contains non-breaking spaces (like PPTX)
|
|
756
|
+
const textOnly = childrenOutput.replace(/<[^>]+>/g, '').trim();
|
|
757
|
+
if (!textOnly && !node.children?.some(c => c.type === 'image' || c.type === 'chart')) {
|
|
758
|
+
const extraClass = className ? ` class="${className.replace('class="', '').replace('"', '')} empty-paragraph"` : ' class="empty-paragraph"';
|
|
759
|
+
return `${extraAnchors}<${tag}${idAttr}${extraClass}${mappedAttrs}${styleAttr}><br></${tag}>`;
|
|
760
|
+
}
|
|
529
761
|
return `${extraAnchors}<${tag}${idAttr}${className}${mappedAttrs}${styleAttr}>${childrenOutput}</${tag}>`;
|
|
530
762
|
}
|
|
531
763
|
case 'slide': {
|
|
@@ -622,6 +854,21 @@ class HtmlGenerator extends BaseGenerator_js_1.BaseGenerator {
|
|
|
622
854
|
return styles.join('; ');
|
|
623
855
|
}
|
|
624
856
|
getPremiumStyles(isSpreadsheet = false, isPresentation = false, isPdf = false) {
|
|
857
|
+
let resolvedWidth = this.config.htmlConfig.containerWidth;
|
|
858
|
+
if (!resolvedWidth || resolvedWidth === 'auto') {
|
|
859
|
+
if (isSpreadsheet) {
|
|
860
|
+
resolvedWidth = '100%';
|
|
861
|
+
}
|
|
862
|
+
else if (isPresentation) {
|
|
863
|
+
resolvedWidth = '297mm';
|
|
864
|
+
}
|
|
865
|
+
else {
|
|
866
|
+
resolvedWidth = '900px';
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
else if (typeof resolvedWidth === 'number') {
|
|
870
|
+
resolvedWidth = `${resolvedWidth}px`;
|
|
871
|
+
}
|
|
625
872
|
return `
|
|
626
873
|
:root {
|
|
627
874
|
--primary-color: #2c3e50;
|
|
@@ -631,6 +878,7 @@ class HtmlGenerator extends BaseGenerator_js_1.BaseGenerator {
|
|
|
631
878
|
--border-color: #e9ecef;
|
|
632
879
|
--accent-color: #3498db;
|
|
633
880
|
--shadow: 0 10px 25px rgba(0,0,0,0.05);
|
|
881
|
+
--container-width: ${resolvedWidth};
|
|
634
882
|
}
|
|
635
883
|
* {
|
|
636
884
|
box-sizing: border-box;
|
|
@@ -644,7 +892,7 @@ class HtmlGenerator extends BaseGenerator_js_1.BaseGenerator {
|
|
|
644
892
|
padding: ${isSpreadsheet ? '0' : '50px 20px'};
|
|
645
893
|
}
|
|
646
894
|
.container {
|
|
647
|
-
max-width:
|
|
895
|
+
max-width: var(--container-width);
|
|
648
896
|
margin: 0 auto;
|
|
649
897
|
background: var(--container-bg);
|
|
650
898
|
padding: 60px 80px;
|
|
@@ -652,7 +900,7 @@ class HtmlGenerator extends BaseGenerator_js_1.BaseGenerator {
|
|
|
652
900
|
box-shadow: var(--shadow);
|
|
653
901
|
}
|
|
654
902
|
.presentation-container, .pdf-container {
|
|
655
|
-
max-width:
|
|
903
|
+
max-width: var(--container-width);
|
|
656
904
|
margin: 0 auto;
|
|
657
905
|
}
|
|
658
906
|
.presentation-container .metadata-summary, .pdf-container .metadata-summary {
|
|
@@ -700,34 +948,51 @@ class HtmlGenerator extends BaseGenerator_js_1.BaseGenerator {
|
|
|
700
948
|
h3 { font-size: 1.5em; }
|
|
701
949
|
|
|
702
950
|
p { margin-bottom: 1.3em; }
|
|
951
|
+
p.empty-paragraph { margin: 0; min-height: 1em; }
|
|
703
952
|
|
|
704
|
-
ul, ol { margin:
|
|
705
|
-
li { margin-bottom: 0.
|
|
953
|
+
ul, ol { margin: 1em 0; padding-left: 2em; }
|
|
954
|
+
li { margin-bottom: 0.25em; }
|
|
955
|
+
li > p { margin-bottom: 0.25em; }
|
|
706
956
|
|
|
707
957
|
.table-container {
|
|
708
|
-
width:
|
|
958
|
+
width: fit-content;
|
|
959
|
+
max-width: 100%;
|
|
709
960
|
overflow-x: auto;
|
|
710
961
|
-webkit-overflow-scrolling: touch;
|
|
711
|
-
margin: 25px
|
|
962
|
+
margin: 25px auto;
|
|
963
|
+
border: 1px solid var(--border-color);
|
|
964
|
+
border-radius: 8px;
|
|
965
|
+
background: white;
|
|
712
966
|
}
|
|
713
967
|
table {
|
|
714
968
|
width: auto;
|
|
715
969
|
max-width: 100%;
|
|
716
970
|
min-width: ${isSpreadsheet ? '100%' : '300px'};
|
|
717
|
-
border-collapse:
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
border
|
|
721
|
-
overflow: hidden;
|
|
971
|
+
border-collapse: separate;
|
|
972
|
+
border-spacing: 0;
|
|
973
|
+
margin: 0;
|
|
974
|
+
border: none;
|
|
722
975
|
}
|
|
723
976
|
th, td {
|
|
724
|
-
padding: 12px
|
|
977
|
+
padding: 8px 12px;
|
|
725
978
|
border-bottom: 1px solid var(--border-color);
|
|
726
979
|
border-right: 1px solid var(--border-color);
|
|
727
980
|
text-align: left;
|
|
728
981
|
vertical-align: top;
|
|
729
982
|
overflow-wrap: break-word;
|
|
730
983
|
}
|
|
984
|
+
th:last-child, td:last-child {
|
|
985
|
+
border-right: none;
|
|
986
|
+
}
|
|
987
|
+
tr:last-child th, tr:last-child td {
|
|
988
|
+
border-bottom: none;
|
|
989
|
+
}
|
|
990
|
+
th, td > p {
|
|
991
|
+
margin: 0;
|
|
992
|
+
}
|
|
993
|
+
td > *:last-child, th > *:last-child {
|
|
994
|
+
margin-bottom: 0;
|
|
995
|
+
}
|
|
731
996
|
th {
|
|
732
997
|
background-color: #f8f9fa;
|
|
733
998
|
color: var(--primary-color);
|
|
@@ -739,7 +1004,6 @@ class HtmlGenerator extends BaseGenerator_js_1.BaseGenerator {
|
|
|
739
1004
|
top: 0;
|
|
740
1005
|
z-index: 10;
|
|
741
1006
|
}
|
|
742
|
-
tr:last-child td { border-bottom: none; }
|
|
743
1007
|
tr:nth-child(even) { background-color: #fdfdfd; }
|
|
744
1008
|
tr:hover { background-color: #f1f4f9; }
|
|
745
1009
|
|
|
@@ -757,8 +1021,99 @@ class HtmlGenerator extends BaseGenerator_js_1.BaseGenerator {
|
|
|
757
1021
|
padding: 8px 12px;
|
|
758
1022
|
font-size: 13px;
|
|
759
1023
|
white-space: nowrap;
|
|
1024
|
+
}
|
|
1025
|
+
/* Spreadsheet Grid styling */
|
|
1026
|
+
.excel-grid {
|
|
1027
|
+
border-collapse: collapse;
|
|
1028
|
+
border-spacing: 0;
|
|
1029
|
+
background: var(--container-bg);
|
|
1030
|
+
border: 1px solid var(--border-color);
|
|
1031
|
+
width: max-content;
|
|
1032
|
+
max-width: none;
|
|
1033
|
+
min-width: 0;
|
|
1034
|
+
}
|
|
1035
|
+
.excel-grid th, .excel-grid td {
|
|
1036
|
+
border: 1px solid var(--border-color);
|
|
1037
|
+
padding: 4px 8px;
|
|
1038
|
+
font-size: 13px;
|
|
1039
|
+
line-height: 1.2;
|
|
1040
|
+
overflow: hidden;
|
|
1041
|
+
text-overflow: ellipsis;
|
|
1042
|
+
white-space: nowrap;
|
|
1043
|
+
}
|
|
1044
|
+
.excel-col-header {
|
|
1045
|
+
background: #f8f9fa;
|
|
1046
|
+
color: #5f6368;
|
|
1047
|
+
font-weight: 500;
|
|
1048
|
+
text-align: center;
|
|
1049
|
+
user-select: none;
|
|
1050
|
+
border-bottom: 2px solid var(--border-color);
|
|
1051
|
+
position: sticky;
|
|
1052
|
+
top: 0;
|
|
1053
|
+
z-index: 10;
|
|
760
1054
|
min-width: 100px;
|
|
761
1055
|
}
|
|
1056
|
+
.col-resizer {
|
|
1057
|
+
position: absolute;
|
|
1058
|
+
top: 0;
|
|
1059
|
+
right: 0;
|
|
1060
|
+
width: 4px;
|
|
1061
|
+
height: 100%;
|
|
1062
|
+
cursor: col-resize;
|
|
1063
|
+
user-select: none;
|
|
1064
|
+
z-index: 20;
|
|
1065
|
+
}
|
|
1066
|
+
.col-resizer:hover, .col-resizer.resizing {
|
|
1067
|
+
background: var(--accent-color, #3498db);
|
|
1068
|
+
}
|
|
1069
|
+
.excel-row-num {
|
|
1070
|
+
background: #f8f9fa;
|
|
1071
|
+
color: #5f6368;
|
|
1072
|
+
text-align: center;
|
|
1073
|
+
font-weight: 500;
|
|
1074
|
+
width: 45px;
|
|
1075
|
+
min-width: 45px !important;
|
|
1076
|
+
max-width: 45px;
|
|
1077
|
+
user-select: none;
|
|
1078
|
+
border-right: 2px solid var(--border-color);
|
|
1079
|
+
position: sticky;
|
|
1080
|
+
left: 0;
|
|
1081
|
+
z-index: 5;
|
|
1082
|
+
}
|
|
1083
|
+
.row-resizer {
|
|
1084
|
+
position: absolute;
|
|
1085
|
+
bottom: 0;
|
|
1086
|
+
left: 0;
|
|
1087
|
+
width: 100%;
|
|
1088
|
+
height: 4px;
|
|
1089
|
+
cursor: row-resize;
|
|
1090
|
+
user-select: none;
|
|
1091
|
+
z-index: 20;
|
|
1092
|
+
}
|
|
1093
|
+
.row-resizer:hover, .row-resizer.resizing {
|
|
1094
|
+
background: var(--accent-color, #3498db);
|
|
1095
|
+
}
|
|
1096
|
+
.excel-row-num-header {
|
|
1097
|
+
background: #f1f3f4;
|
|
1098
|
+
width: 45px;
|
|
1099
|
+
min-width: 45px !important;
|
|
1100
|
+
max-width: 45px;
|
|
1101
|
+
position: sticky;
|
|
1102
|
+
top: 0;
|
|
1103
|
+
left: 0;
|
|
1104
|
+
z-index: 15;
|
|
1105
|
+
border-right: 2px solid var(--border-color);
|
|
1106
|
+
border-bottom: 2px solid var(--border-color);
|
|
1107
|
+
}
|
|
1108
|
+
.excel-cell-empty {
|
|
1109
|
+
background: var(--container-bg);
|
|
1110
|
+
}
|
|
1111
|
+
.excel-grid tr:hover td {
|
|
1112
|
+
background-color: #f1f4f9;
|
|
1113
|
+
}
|
|
1114
|
+
.excel-grid tr:hover td.excel-row-num {
|
|
1115
|
+
background-color: #f8f9fa; /* Keep header color */
|
|
1116
|
+
}
|
|
762
1117
|
|
|
763
1118
|
/* Tab Bar */
|
|
764
1119
|
.spreadsheet-tabs {
|
|
@@ -792,9 +1147,9 @@ class HtmlGenerator extends BaseGenerator_js_1.BaseGenerator {
|
|
|
792
1147
|
/* Slide & Page Separation */
|
|
793
1148
|
.slide {
|
|
794
1149
|
background: white;
|
|
795
|
-
|
|
1150
|
+
aspect-ratio: 297 / 210;
|
|
796
1151
|
margin: 0 auto 40px auto;
|
|
797
|
-
padding:
|
|
1152
|
+
padding: 4% 6%;
|
|
798
1153
|
border-radius: 12px;
|
|
799
1154
|
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
|
|
800
1155
|
position: relative;
|
|
@@ -804,21 +1159,74 @@ class HtmlGenerator extends BaseGenerator_js_1.BaseGenerator {
|
|
|
804
1159
|
border: 1px solid var(--border-color);
|
|
805
1160
|
box-sizing: border-box;
|
|
806
1161
|
width: 100%;
|
|
1162
|
+
overflow: hidden;
|
|
1163
|
+
overflow-y: auto;
|
|
807
1164
|
overflow-wrap: break-word;
|
|
808
1165
|
page-break-after: always;
|
|
809
1166
|
}
|
|
1167
|
+
.slide:has(+ .slide-note) {
|
|
1168
|
+
margin-bottom: 0;
|
|
1169
|
+
border-bottom-left-radius: 0;
|
|
1170
|
+
border-bottom-right-radius: 0;
|
|
1171
|
+
border-bottom: none;
|
|
1172
|
+
}
|
|
1173
|
+
.slide table {
|
|
1174
|
+
width: auto;
|
|
1175
|
+
max-width: 100%;
|
|
1176
|
+
font-size: 0.85em;
|
|
1177
|
+
margin: 10px 0;
|
|
1178
|
+
}
|
|
1179
|
+
.slide th, .slide td {
|
|
1180
|
+
padding: 6px 10px;
|
|
1181
|
+
}
|
|
1182
|
+
.slide > * {
|
|
1183
|
+
flex-shrink: 0;
|
|
1184
|
+
}
|
|
1185
|
+
.slide > .table-container {
|
|
1186
|
+
flex-shrink: 1;
|
|
1187
|
+
min-height: 0;
|
|
1188
|
+
}
|
|
1189
|
+
.slide .table-container {
|
|
1190
|
+
margin: 10px auto;
|
|
1191
|
+
overflow-y: auto;
|
|
1192
|
+
}
|
|
1193
|
+
.slide .chart-container {
|
|
1194
|
+
margin: 15px auto;
|
|
1195
|
+
max-width: 100%;
|
|
1196
|
+
padding: 10px;
|
|
1197
|
+
height: 240px;
|
|
1198
|
+
box-shadow: none;
|
|
1199
|
+
border-radius: 8px;
|
|
1200
|
+
}
|
|
1201
|
+
.slide img {
|
|
1202
|
+
max-height: 200px;
|
|
1203
|
+
width: auto;
|
|
1204
|
+
margin: 10px auto;
|
|
1205
|
+
box-shadow: none;
|
|
1206
|
+
}
|
|
1207
|
+
.slide .image-container {
|
|
1208
|
+
margin: 15px 0;
|
|
1209
|
+
}
|
|
1210
|
+
.slide h1 { font-size: 1.8em; margin-top: 0.4em; margin-bottom: 0.3em; padding-bottom: 5px; }
|
|
1211
|
+
.slide h2 { font-size: 1.4em; margin-top: 0.4em; }
|
|
1212
|
+
.slide h3 { font-size: 1.25em; }
|
|
1213
|
+
.slide p { margin-bottom: 0.6em; font-size: 0.95em; }
|
|
1214
|
+
.slide li > p { margin-bottom: 0.25em; }
|
|
1215
|
+
.slide ul, .slide ol { margin: 0.6em 0; }
|
|
1216
|
+
.slide li { margin-bottom: 0.25em; }
|
|
810
1217
|
.slide-note {
|
|
811
1218
|
background: #fdfdfd;
|
|
812
1219
|
border: 1px solid var(--border-color);
|
|
813
|
-
border-
|
|
814
|
-
border-radius: 12px;
|
|
1220
|
+
border-top: 1px dashed var(--border-color);
|
|
1221
|
+
border-radius: 0 0 12px 12px;
|
|
815
1222
|
padding: 30px 50px;
|
|
816
|
-
margin:
|
|
817
|
-
|
|
1223
|
+
margin: 0 auto 40px auto;
|
|
1224
|
+
width: 100%;
|
|
1225
|
+
box-sizing: border-box;
|
|
818
1226
|
font-size: 0.95em;
|
|
819
1227
|
color: var(--text-color);
|
|
820
1228
|
position: relative;
|
|
821
|
-
box-shadow: 0
|
|
1229
|
+
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
|
|
822
1230
|
}
|
|
823
1231
|
.note-footnote, .note-endnote {
|
|
824
1232
|
margin: 30px auto;
|
|
@@ -848,9 +1256,9 @@ class HtmlGenerator extends BaseGenerator_js_1.BaseGenerator {
|
|
|
848
1256
|
}
|
|
849
1257
|
.page {
|
|
850
1258
|
background: white;
|
|
851
|
-
|
|
1259
|
+
aspect-ratio: 1 / 1.4142;
|
|
852
1260
|
margin: 0 auto 40px auto;
|
|
853
|
-
padding:
|
|
1261
|
+
padding: 8% 10%;
|
|
854
1262
|
box-shadow: 0 5px 20px rgba(0,0,0,0.08);
|
|
855
1263
|
position: relative;
|
|
856
1264
|
border: 1px solid var(--border-color);
|
|
@@ -870,8 +1278,8 @@ class HtmlGenerator extends BaseGenerator_js_1.BaseGenerator {
|
|
|
870
1278
|
|
|
871
1279
|
/* High Fidelity Presentation Mode */
|
|
872
1280
|
@media screen and (max-width: 800px) {
|
|
873
|
-
.slide { min-height: auto; padding: 40px 30px; }
|
|
874
|
-
.page { padding: 40px; }
|
|
1281
|
+
.slide { min-height: auto; aspect-ratio: auto; padding: 40px 30px; }
|
|
1282
|
+
.page { aspect-ratio: auto; padding: 40px; }
|
|
875
1283
|
}
|
|
876
1284
|
|
|
877
1285
|
/* Nested Table Styles */
|
|
@@ -880,6 +1288,8 @@ class HtmlGenerator extends BaseGenerator_js_1.BaseGenerator {
|
|
|
880
1288
|
border-radius: 6px;
|
|
881
1289
|
box-shadow: 0 2px 8px rgba(0,0,0,0.03);
|
|
882
1290
|
background-color: #ffffff;
|
|
1291
|
+
border: 1px solid var(--border-color);
|
|
1292
|
+
overflow: hidden;
|
|
883
1293
|
}
|
|
884
1294
|
td td {
|
|
885
1295
|
padding: 10px 14px;
|
|
@@ -999,11 +1409,23 @@ class HtmlGenerator extends BaseGenerator_js_1.BaseGenerator {
|
|
|
999
1409
|
height: auto !important;
|
|
1000
1410
|
}
|
|
1001
1411
|
}
|
|
1412
|
+
|
|
1413
|
+
/* --- Custom User CSS --- */
|
|
1414
|
+
${this.config.htmlConfig.customCss}
|
|
1002
1415
|
`;
|
|
1003
1416
|
}
|
|
1004
1417
|
slugify(text) {
|
|
1005
1418
|
return text.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '');
|
|
1006
1419
|
}
|
|
1420
|
+
getColumnLetter(colIndex) {
|
|
1421
|
+
let temp = colIndex;
|
|
1422
|
+
let letter = '';
|
|
1423
|
+
while (temp >= 0) {
|
|
1424
|
+
letter = String.fromCharCode((temp % 26) + 65) + letter;
|
|
1425
|
+
temp = Math.floor(temp / 26) - 1;
|
|
1426
|
+
}
|
|
1427
|
+
return letter;
|
|
1428
|
+
}
|
|
1007
1429
|
escape(text) {
|
|
1008
1430
|
if (typeof text !== 'string')
|
|
1009
1431
|
return text;
|