documentation-hub 5.7.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/.eslintrc.json +43 -0
- package/.github/workflows/build.yml +64 -0
- package/.github/workflows/ci.yml +39 -0
- package/.vscode/extensions.json +3 -0
- package/Current.md +97 -0
- package/DocHub_Image.png +0 -0
- package/README.md +666 -0
- package/USER_GUIDE.md +1173 -0
- package/Updater.md +311 -0
- package/build/256x256.png +0 -0
- package/build/512x512.png +0 -0
- package/build/app-update.yml +4 -0
- package/build/create-icon.js +208 -0
- package/build/icon.ico +0 -0
- package/build/icon.png +0 -0
- package/build/icon_1024x1024.png +0 -0
- package/dist/assets/Analytics-BpsG9895.js +1 -0
- package/dist/assets/Card-IAZin8kp.js +1 -0
- package/dist/assets/CurrentSession-B-rFkHvf.js +12 -0
- package/dist/assets/Dashboard-C_5gMb0q.js +1 -0
- package/dist/assets/Documents-CqZ25axS.js +1 -0
- package/dist/assets/Input-l89xwXBi.js +1 -0
- package/dist/assets/Reporting-DqdHJY_a.js +1 -0
- package/dist/assets/Search-XNbu5z_3.js +1 -0
- package/dist/assets/SessionManager-lH9hZfzH.js +1 -0
- package/dist/assets/Sessions-ClZOPYNc.js +1 -0
- package/dist/assets/Settings-DUEHGURa.js +11 -0
- package/dist/assets/index-8xUe8ptc.js +24 -0
- package/dist/assets/index-RYyJqF7O.css +1 -0
- package/dist/assets/path-BkOl0AGO.js +1 -0
- package/dist/assets/promises-ID_B9S-h.js +1 -0
- package/dist/assets/urlHelpers-TvgahX0r.js +1 -0
- package/dist/assets/useToast-yRSO1dkm.js +1 -0
- package/dist/assets/vendor-charts-RkGK5ROP.js +36 -0
- package/dist/assets/vendor-db-l0sNRNKZ.js +1 -0
- package/dist/assets/vendor-react-BVZ_anCF.js +4 -0
- package/dist/assets/vendor-search-Dw8P0qyA.js +1 -0
- package/dist/assets/vendor-ui-BU7NfluV.js +53 -0
- package/dist/electron/PowerAutomateApiService-LfW09ZGr.js +147 -0
- package/dist/electron/main-CXkNtyv-.js +19789 -0
- package/dist/electron/main.js +5 -0
- package/dist/electron/preload.js +1 -0
- package/dist/icon.png +0 -0
- package/dist/index.html +27 -0
- package/docs/CODEBASE_ANALYSIS_REPORT.md +309 -0
- package/docs/DEBUG_LOGGING_GUIDE.md +244 -0
- package/docs/README.md +115 -0
- package/docs/TOC_WIRING_GUIDE.md +344 -0
- package/docs/analysis/Bullet_Symbol_Bug_Analysis.md +136 -0
- package/docs/analysis/DOCXMLATER_ANALYSIS_SUMMARY.txt +169 -0
- package/docs/analysis/Document_Processing_Issues_Analysis.md +704 -0
- package/docs/analysis/FIELD_PRESERVATION_ANALYSIS.md +1200 -0
- package/docs/analysis/INDENTATION_PRESERVE_ANALYSIS.md +181 -0
- package/docs/analysis/INDENTATION_PRESERVE_IMPLEMENTATION.md +207 -0
- package/docs/analysis/List_Implementation.md +206 -0
- package/docs/analysis/List_Implementation_Accuracy_Report.md +366 -0
- package/docs/analysis/PROCESSING_OPTIONS_UI_UPDATES.md +220 -0
- package/docs/analysis/RefactorStyles.md +852 -0
- package/docs/analysis/STYLE_PARAMETER_ENHANCEMENT.md +143 -0
- package/docs/analysis/docxmlater-comparison-todo-2025-11-13.md +636 -0
- package/docs/analysis/docxmlater-implementation-analysis-2025-11-13.md +340 -0
- package/docs/analysis/docxmlater-template_ui-integration-analysis.md +263 -0
- package/docs/analysis/github-issues-to-create.md +237 -0
- package/docs/api/API_README.md +538 -0
- package/docs/api/API_REFERENCE.md +751 -0
- package/docs/api/TYPE_DEFINITIONS.md +869 -0
- package/docs/architecture/FONT_EMBEDDING_GUIDE.md +318 -0
- package/docs/architecture/docxmlater-functions-and-structure.md +726 -0
- package/docs/docxmlater-readme.md +1341 -0
- package/docs/fixes/EXECUTION_LOG_TEST_BASE.md +573 -0
- package/docs/fixes/HYPERLINK_TEXT_SANITIZATION.md +253 -0
- package/docs/fixes/README.md +37 -0
- package/docs/github-issues/issue-1-body.md +125 -0
- package/docs/github-issues/issue-10-body.md +850 -0
- package/docs/github-issues/issue-2-body.md +200 -0
- package/docs/github-issues/issue-3-body.md +270 -0
- package/docs/github-issues/issue-4-body.md +169 -0
- package/docs/github-issues/issue-5-body.md +173 -0
- package/docs/github-issues/issue-6-body.md +158 -0
- package/docs/github-issues/issue-7-body.md +171 -0
- package/docs/github-issues/issue-8-body.md +407 -0
- package/docs/github-issues/issue-9-body.md +515 -0
- package/docs/github-issues/issue-tracker.md +274 -0
- package/docs/github-issues/predictive-analysis-2025-10-18.md +2131 -0
- package/docs/implementation/List_Framework_Refactor_Plan.md +336 -0
- package/docs/implementation/PRIMARY_TEXT_COLOR_FEATURE.md +217 -0
- package/docs/implementation/RELEASE_PLAN_v2.1.0.md +362 -0
- package/docs/implementation/RefactorStyles.md +588 -0
- package/docs/implementation/implement-plan.md +489 -0
- package/docs/implementation/missing-helpers-implementation.md +391 -0
- package/docs/implementation/refactor-plan.md +520 -0
- package/docs/implementation/session-implementation-complete.md +233 -0
- package/docs/implementation/session-management-plan.md +250 -0
- package/docs/setup-checklist.md +77 -0
- package/docs/versions/changelog.md +345 -0
- package/electron/customUpdater.ts +656 -0
- package/electron/main.ts +2441 -0
- package/electron/memoryConfig.ts +187 -0
- package/electron/preload.ts +394 -0
- package/electron/proxyConfig.ts +340 -0
- package/electron/services/BackupService.ts +452 -0
- package/electron/services/DictionaryService.ts +402 -0
- package/electron/services/LocalDictionaryLookupService.ts +147 -0
- package/electron/services/PowerAutomateApiService.ts +231 -0
- package/electron/services/SharePointSyncService.ts +474 -0
- package/electron/windowsCertStore.ts +427 -0
- package/electron/zscalerConfig.ts +381 -0
- package/eslint.config.js +92 -0
- package/jest.config.js +52 -0
- package/package.json +214 -0
- package/postcss.config.mjs +6 -0
- package/public/icon.png +0 -0
- package/publish-release.ps1 +5 -0
- package/renovate.json +30 -0
- package/src/App.tsx +216 -0
- package/src/__mocks__/p-limit.js +12 -0
- package/src/__mocks__/styleMock.js +1 -0
- package/src/components/common/BugReportButton.tsx +44 -0
- package/src/components/common/BugReportDialog.tsx +193 -0
- package/src/components/common/Button.tsx +153 -0
- package/src/components/common/Card.tsx +86 -0
- package/src/components/common/ColorPickerDialog.tsx +177 -0
- package/src/components/common/ConfirmDialog.tsx +96 -0
- package/src/components/common/DebugConsole.tsx +275 -0
- package/src/components/common/EmptyState.tsx +183 -0
- package/src/components/common/ErrorBoundary.tsx +98 -0
- package/src/components/common/ErrorDetailsDialog.tsx +153 -0
- package/src/components/common/ErrorFallback.tsx +218 -0
- package/src/components/common/Input.tsx +109 -0
- package/src/components/common/Skeleton.tsx +184 -0
- package/src/components/common/SplashScreen.tsx +81 -0
- package/src/components/common/Toast.tsx +155 -0
- package/src/components/common/Tooltip.tsx +79 -0
- package/src/components/common/UpdateNotification.tsx +320 -0
- package/src/components/comparison/ComparisonWindow.tsx +374 -0
- package/src/components/comparison/SideBySideDiff.tsx +486 -0
- package/src/components/comparison/index.ts +8 -0
- package/src/components/document/DocumentUploader.tsx +288 -0
- package/src/components/document/HyperlinkPreview.tsx +430 -0
- package/src/components/document/HyperlinkService.md +1484 -0
- package/src/components/document/Hyperlink_Technical_Documentation.md +496 -0
- package/src/components/document/InlineChangesView.tsx +707 -0
- package/src/components/document/ProcessingProgress.tsx +303 -0
- package/src/components/document/ProcessingResults.tsx +256 -0
- package/src/components/document/TrackedChangesDetail.tsx +530 -0
- package/src/components/document/TrackedChangesPanel.tsx +546 -0
- package/src/components/document/VirtualDocumentList.tsx +240 -0
- package/src/components/editor/DocumentEditor.tsx +723 -0
- package/src/components/editor/DocumentEditorModal.tsx +640 -0
- package/src/components/editor/EditorQuickActions.tsx +502 -0
- package/src/components/editor/EditorToolbar.tsx +312 -0
- package/src/components/editor/TableEditor.tsx +926 -0
- package/src/components/editor/index.ts +18 -0
- package/src/components/layout/Header.tsx +190 -0
- package/src/components/layout/Sidebar.tsx +313 -0
- package/src/components/layout/TitleBar.tsx +190 -0
- package/src/components/navigation/CommandPalette.tsx +233 -0
- package/src/components/navigation/KeyboardShortcutsModal.tsx +173 -0
- package/src/components/sessions/ChangeItem.tsx +408 -0
- package/src/components/sessions/ChangeViewer.tsx +1155 -0
- package/src/components/sessions/DocumentComparisonModal.tsx +314 -0
- package/src/components/sessions/ProcessingOptions.tsx +297 -0
- package/src/components/sessions/ReplacementsTab.tsx +438 -0
- package/src/components/sessions/RevisionHandlingOptions.tsx +87 -0
- package/src/components/sessions/SessionManager.tsx +188 -0
- package/src/components/sessions/StylesEditor.tsx +1335 -0
- package/src/components/sessions/TabContainer.tsx +151 -0
- package/src/components/sessions/VirtualSessionList.tsx +157 -0
- package/src/components/sessions/sessionToProcessorManager.tsx +420 -0
- package/src/components/settings/CertificateManager.tsx +410 -0
- package/src/components/settings/SegmentedControl.tsx +88 -0
- package/src/components/settings/SettingRow.tsx +52 -0
- package/src/contexts/GlobalStatsContext.tsx +396 -0
- package/src/contexts/SessionContext.tsx +2129 -0
- package/src/contexts/ThemeContext.tsx +428 -0
- package/src/contexts/UserSettingsContext.tsx +290 -0
- package/src/contexts/__tests__/GlobalStatsContext.test.tsx +390 -0
- package/src/global.d.ts +273 -0
- package/src/hooks/useDocumentQueue.tsx +210 -0
- package/src/hooks/useToast.tsx +55 -0
- package/src/main.tsx +10 -0
- package/src/pages/Analytics.tsx +386 -0
- package/src/pages/CurrentSession.tsx +1174 -0
- package/src/pages/Dashboard.tsx +319 -0
- package/src/pages/Documents.tsx +317 -0
- package/src/pages/Projects.tsx +250 -0
- package/src/pages/Reporting.tsx +386 -0
- package/src/pages/Search.tsx +349 -0
- package/src/pages/Sessions.tsx +285 -0
- package/src/pages/Settings.tsx +2662 -0
- package/src/services/HyperlinkService.ts +1085 -0
- package/src/services/document/DocXMLaterProcessor.ts +617 -0
- package/src/services/document/DocumentProcessingComparison.ts +856 -0
- package/src/services/document/DocumentSnapshotService.ts +575 -0
- package/src/services/document/WordDocumentProcessor.ts +10509 -0
- package/src/services/document/__tests__/DocXMLaterProcessor.hyperlinks.test.md +311 -0
- package/src/services/document/__tests__/WordDocumentProcessor.integration.test.ts +515 -0
- package/src/services/document/__tests__/WordDocumentProcessor.test.ts +812 -0
- package/src/services/document/blanklines/BlankLineManager.ts +658 -0
- package/src/services/document/blanklines/__tests__/paragraphChecks.test.ts +281 -0
- package/src/services/document/blanklines/helpers/blankLineInsertion.ts +87 -0
- package/src/services/document/blanklines/helpers/blankLineSnapshot.ts +251 -0
- package/src/services/document/blanklines/helpers/clearCustom.ts +121 -0
- package/src/services/document/blanklines/helpers/contextChecks.ts +117 -0
- package/src/services/document/blanklines/helpers/imageChecks.ts +51 -0
- package/src/services/document/blanklines/helpers/paragraphChecks.ts +236 -0
- package/src/services/document/blanklines/helpers/removeBlanksBetweenListItems.ts +91 -0
- package/src/services/document/blanklines/helpers/removeTrailingBlanks.ts +35 -0
- package/src/services/document/blanklines/helpers/tableGuards.ts +21 -0
- package/src/services/document/blanklines/index.ts +67 -0
- package/src/services/document/blanklines/rules/additionRules.ts +337 -0
- package/src/services/document/blanklines/rules/indentationRules.ts +317 -0
- package/src/services/document/blanklines/rules/removalRules.ts +362 -0
- package/src/services/document/blanklines/rules/ruleTypes.ts +92 -0
- package/src/services/document/blanklines/types.ts +29 -0
- package/src/services/document/helpers/ImageBorderCropper.ts +377 -0
- package/src/services/document/helpers/__tests__/whitespace.test.ts +272 -0
- package/src/services/document/helpers/whitespace.ts +117 -0
- package/src/services/document/list/ListNormalizer.ts +947 -0
- package/src/services/document/list/index.ts +45 -0
- package/src/services/document/list/list-detection.ts +275 -0
- package/src/services/document/list/list-types.ts +162 -0
- package/src/services/document/processors/HyperlinkProcessor.ts +370 -0
- package/src/services/document/processors/ListProcessor.ts +257 -0
- package/src/services/document/processors/StructureProcessor.ts +176 -0
- package/src/services/document/processors/StyleProcessor.ts +389 -0
- package/src/services/document/processors/TableProcessor.ts +2238 -0
- package/src/services/document/processors/__tests__/HyperlinkProcessor.test.ts +314 -0
- package/src/services/document/processors/__tests__/ListProcessor.test.ts +291 -0
- package/src/services/document/processors/__tests__/StructureProcessor.test.ts +257 -0
- package/src/services/document/processors/__tests__/TableProcessor.hlp-tips-bullets.test.ts +459 -0
- package/src/services/document/processors/__tests__/TableProcessor.test.ts +1604 -0
- package/src/services/document/processors/index.ts +28 -0
- package/src/services/document/types/docx-processing.ts +310 -0
- package/src/services/editor/EditorActionHandlers.ts +901 -0
- package/src/services/editor/index.ts +13 -0
- package/src/setupTests.ts +47 -0
- package/src/styles/global.css +782 -0
- package/src/types/backup.ts +132 -0
- package/src/types/dictionary.ts +125 -0
- package/src/types/document-processing.ts +331 -0
- package/src/types/docxmlater-augments.d.ts +142 -0
- package/src/types/editor.ts +280 -0
- package/src/types/electron.ts +340 -0
- package/src/types/globalStats.ts +155 -0
- package/src/types/hyperlink.ts +471 -0
- package/src/types/operations.ts +354 -0
- package/src/types/session.ts +427 -0
- package/src/types/settings.ts +112 -0
- package/src/utils/MemoryMonitor.ts +248 -0
- package/src/utils/cn.ts +6 -0
- package/src/utils/colorConvert.ts +306 -0
- package/src/utils/diffUtils.ts +347 -0
- package/src/utils/documentUtils.ts +202 -0
- package/src/utils/electronGuard.ts +62 -0
- package/src/utils/indexedDB.ts +915 -0
- package/src/utils/logger.ts +717 -0
- package/src/utils/pathSecurity.ts +232 -0
- package/src/utils/pathValidator.ts +236 -0
- package/src/utils/processingTimeEstimator.ts +153 -0
- package/src/utils/safeJsonParse.ts +62 -0
- package/src/utils/textSanitizer.ts +162 -0
- package/src/utils/urlHelpers.ts +304 -0
- package/src/utils/urlPatterns.ts +198 -0
- package/src/utils/urlSanitizer.ts +152 -0
- package/src/vite-env.d.ts +11 -0
- package/tsconfig.electron.json +19 -0
- package/tsconfig.json +36 -0
- package/tsconfig.node.json +12 -0
- package/typedoc.json +45 -0
- package/vite.config.ts +152 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clears Structured Document Tags (SDTs) from a document, unwrapping their content.
|
|
3
|
+
* Ported from Document.ts clearCustom(), clearCustomInTable(),
|
|
4
|
+
* unwrapNestedStructuredDocumentTags(), and sanitizeTableRowExceptions().
|
|
5
|
+
*
|
|
6
|
+
* SDTs are wrappers added by tools like Google Docs that can interfere with
|
|
7
|
+
* document processing. This function removes them while preserving content.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
Document,
|
|
12
|
+
Paragraph,
|
|
13
|
+
Table,
|
|
14
|
+
TableCell,
|
|
15
|
+
StructuredDocumentTag,
|
|
16
|
+
} from "docxmlater";
|
|
17
|
+
|
|
18
|
+
// Use any[] for unwrapped body since getBodyElements() may return
|
|
19
|
+
// types beyond Paragraph | Table | StructuredDocumentTag (e.g., TableOfContentsElement)
|
|
20
|
+
type BodyElement = any;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Removes all SDT wrappers from the document, unwrapping their content
|
|
24
|
+
* into the body elements array. Also sanitizes table row exceptions
|
|
25
|
+
* that would otherwise cause formatting leakage outside the SDT context.
|
|
26
|
+
*
|
|
27
|
+
* Ported from Document.ts clearCustom() (lines 15119-15154)
|
|
28
|
+
*/
|
|
29
|
+
export function clearCustom(doc: Document): void {
|
|
30
|
+
const bodyElements = doc.getBodyElements();
|
|
31
|
+
const unwrappedBody: BodyElement[] = [];
|
|
32
|
+
|
|
33
|
+
for (const element of bodyElements) {
|
|
34
|
+
if (element instanceof StructuredDocumentTag) {
|
|
35
|
+
// Unwrap SDT: add its content directly to the body
|
|
36
|
+
const sdtContent = element.getContent();
|
|
37
|
+
for (const item of sdtContent) {
|
|
38
|
+
if (item instanceof Table) {
|
|
39
|
+
// Sanitize tblPrEx from table rows when coming out of SDT
|
|
40
|
+
sanitizeTableRowExceptions(item);
|
|
41
|
+
unwrappedBody.push(item);
|
|
42
|
+
} else if (item instanceof Paragraph) {
|
|
43
|
+
unwrappedBody.push(item);
|
|
44
|
+
} else if (item instanceof StructuredDocumentTag) {
|
|
45
|
+
// Recursively handle nested SDTs
|
|
46
|
+
unwrapNestedSDTs(item, unwrappedBody);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
} else if (element instanceof Table) {
|
|
50
|
+
// Process table: unwrap SDTs inside cells
|
|
51
|
+
clearCustomInTable(element);
|
|
52
|
+
unwrappedBody.push(element);
|
|
53
|
+
} else {
|
|
54
|
+
unwrappedBody.push(element);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Replace body elements with unwrapped content
|
|
59
|
+
doc.setBodyElements(unwrappedBody);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Sanitizes table property exceptions from all rows in a table.
|
|
64
|
+
* Clears tblPrEx (row-level table property overrides) to prevent formatting
|
|
65
|
+
* from leaking when tables are relocated outside SDT context.
|
|
66
|
+
*
|
|
67
|
+
* Ported from Document.ts sanitizeTableRowExceptions() (lines 15169-15182)
|
|
68
|
+
*/
|
|
69
|
+
function sanitizeTableRowExceptions(table: Table): void {
|
|
70
|
+
const rows = table.getRows();
|
|
71
|
+
|
|
72
|
+
for (const row of rows) {
|
|
73
|
+
const exceptions = row.getTablePropertyExceptions();
|
|
74
|
+
|
|
75
|
+
if (exceptions && Object.keys(exceptions).length > 0) {
|
|
76
|
+
row.setTablePropertyExceptions(undefined as any);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Recursively unwraps nested SDTs, adding their content to the target array.
|
|
83
|
+
*
|
|
84
|
+
* Ported from Document.ts unwrapNestedStructuredDocumentTags() (lines 15188-15202)
|
|
85
|
+
*/
|
|
86
|
+
function unwrapNestedSDTs(
|
|
87
|
+
sdt: StructuredDocumentTag,
|
|
88
|
+
targetArray: BodyElement[]
|
|
89
|
+
): void {
|
|
90
|
+
const content = sdt.getContent();
|
|
91
|
+
|
|
92
|
+
for (const item of content) {
|
|
93
|
+
if (item instanceof Paragraph || item instanceof Table) {
|
|
94
|
+
targetArray.push(item);
|
|
95
|
+
} else if (item instanceof StructuredDocumentTag) {
|
|
96
|
+
unwrapNestedSDTs(item, targetArray);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Processes a table's cells to clear SDTs inside them.
|
|
103
|
+
*
|
|
104
|
+
* Ported from Document.ts clearCustomInTable() (lines 15209-15233)
|
|
105
|
+
* Note: The original implementation's inner loop body was empty.
|
|
106
|
+
*/
|
|
107
|
+
function clearCustomInTable(table: Table): void {
|
|
108
|
+
const rows = table.getRows();
|
|
109
|
+
|
|
110
|
+
for (const row of rows) {
|
|
111
|
+
const cells = row.getCells();
|
|
112
|
+
|
|
113
|
+
for (const cell of cells) {
|
|
114
|
+
if (!(cell instanceof TableCell)) {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
// Original implementation iterates cell paragraphs but doesn't modify them.
|
|
118
|
+
// Nested tables in cells are handled separately via getAllTables().
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Functions for checking paragraph context (list context, etc.).
|
|
3
|
+
* These need access to body elements via the Document public API.
|
|
4
|
+
* Ported from docxmlater Document.ts private methods.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Document, Paragraph, Table, TableCell } from "docxmlater";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Checks if a non-list paragraph is "within" a list context.
|
|
11
|
+
* A paragraph is within a list context if it has no numbering,
|
|
12
|
+
* and the previous and next list items share the same numId.
|
|
13
|
+
* Ported from Document.ts:8302-8348
|
|
14
|
+
*/
|
|
15
|
+
export function isWithinListContext(doc: Document, index: number): boolean {
|
|
16
|
+
const current = doc.getBodyElementAt(index);
|
|
17
|
+
if (!(current instanceof Paragraph)) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// If current is a list item, it's not "within" - it IS the list
|
|
22
|
+
const currentNum = current.getNumbering();
|
|
23
|
+
if (currentNum) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Find previous list item (scanning backwards)
|
|
28
|
+
let prevNumId: number | undefined;
|
|
29
|
+
for (let i = index - 1; i >= 0; i--) {
|
|
30
|
+
const el = doc.getBodyElementAt(i);
|
|
31
|
+
if (el instanceof Paragraph) {
|
|
32
|
+
const num = el.getNumbering();
|
|
33
|
+
if (num) {
|
|
34
|
+
prevNumId = num.numId;
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
} else if (el instanceof Table) {
|
|
38
|
+
// Stop at table boundaries
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Find next list item (scanning forwards)
|
|
44
|
+
let nextNumId: number | undefined;
|
|
45
|
+
const count = doc.getBodyElementCount();
|
|
46
|
+
for (let i = index + 1; i < count; i++) {
|
|
47
|
+
const el = doc.getBodyElementAt(i);
|
|
48
|
+
if (el instanceof Paragraph) {
|
|
49
|
+
const num = el.getNumbering();
|
|
50
|
+
if (num) {
|
|
51
|
+
nextNumId = num.numId;
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
} else if (el instanceof Table) {
|
|
55
|
+
// Stop at table boundaries
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Within list context only if both prev and next are same list
|
|
61
|
+
return (
|
|
62
|
+
prevNumId !== undefined &&
|
|
63
|
+
nextNumId !== undefined &&
|
|
64
|
+
prevNumId === nextNumId
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Checks if a paragraph is within a list context inside a table cell.
|
|
70
|
+
* Ported from Document.ts:8359-8389
|
|
71
|
+
*/
|
|
72
|
+
export function isWithinListContextInCell(
|
|
73
|
+
cell: TableCell,
|
|
74
|
+
paraIndex: number
|
|
75
|
+
): boolean {
|
|
76
|
+
const cellParas = cell.getParagraphs();
|
|
77
|
+
const current = cellParas[paraIndex];
|
|
78
|
+
if (!current) return false;
|
|
79
|
+
|
|
80
|
+
// If current is a list item, it's not "within" - it IS the list
|
|
81
|
+
const currentNum = current.getNumbering();
|
|
82
|
+
if (currentNum) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Find previous list item in cell
|
|
87
|
+
let prevNumId: number | undefined;
|
|
88
|
+
for (let i = paraIndex - 1; i >= 0; i--) {
|
|
89
|
+
const para = cellParas[i];
|
|
90
|
+
if (para) {
|
|
91
|
+
const num = para.getNumbering();
|
|
92
|
+
if (num) {
|
|
93
|
+
prevNumId = num.numId;
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Find next list item in cell
|
|
100
|
+
let nextNumId: number | undefined;
|
|
101
|
+
for (let i = paraIndex + 1; i < cellParas.length; i++) {
|
|
102
|
+
const para = cellParas[i];
|
|
103
|
+
if (para) {
|
|
104
|
+
const num = para.getNumbering();
|
|
105
|
+
if (num) {
|
|
106
|
+
nextNumId = num.numId;
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
prevNumId !== undefined &&
|
|
114
|
+
nextNumId !== undefined &&
|
|
115
|
+
prevNumId === nextNumId
|
|
116
|
+
);
|
|
117
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure functions for checking image-related paragraph characteristics.
|
|
3
|
+
* Ported from docxmlater Document.ts public/private methods.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Paragraph, ImageRun, Image, Revision } from "docxmlater";
|
|
7
|
+
|
|
8
|
+
const EMU_PER_PIXEL = 9525; // At 96 DPI
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Checks if an image is "small" (both dimensions < 100 pixels).
|
|
12
|
+
* Ported from Document.ts:8423-8428
|
|
13
|
+
*/
|
|
14
|
+
export function isImageSmall(image: Image): boolean {
|
|
15
|
+
const widthPx = image.getWidth() / EMU_PER_PIXEL;
|
|
16
|
+
const heightPx = image.getHeight() / EMU_PER_PIXEL;
|
|
17
|
+
return widthPx < 100 && heightPx < 100;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Checks if a paragraph contains a small image (< 100x100 pixels).
|
|
22
|
+
* Ported from Document.ts:8448-8453
|
|
23
|
+
*/
|
|
24
|
+
export function isSmallImageParagraph(para: Paragraph): boolean {
|
|
25
|
+
const imageRun = getImageRunFromParagraph(para);
|
|
26
|
+
if (!imageRun) return false;
|
|
27
|
+
const image = imageRun.getImageElement();
|
|
28
|
+
return isImageSmall(image);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Extracts the first ImageRun from a paragraph, including from Revision elements.
|
|
33
|
+
* Ported from Document.ts:8474-8491
|
|
34
|
+
*/
|
|
35
|
+
export function getImageRunFromParagraph(para: Paragraph): ImageRun | null {
|
|
36
|
+
const content = para.getContent();
|
|
37
|
+
for (const item of content) {
|
|
38
|
+
if (item instanceof ImageRun) {
|
|
39
|
+
return item;
|
|
40
|
+
}
|
|
41
|
+
// Also check inside Revision objects (tracked changes)
|
|
42
|
+
if (item instanceof Revision) {
|
|
43
|
+
for (const revContent of item.getContent()) {
|
|
44
|
+
if (revContent instanceof ImageRun) {
|
|
45
|
+
return revContent;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure functions for checking paragraph content characteristics.
|
|
3
|
+
* Ported from docxmlater Document.ts private methods.
|
|
4
|
+
* Most operate only on Paragraph/Run/etc. instances with no Document dependency.
|
|
5
|
+
* Exception: getEffectiveLeftIndent() requires Document to resolve style-inherited indentation.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
Document,
|
|
10
|
+
Paragraph,
|
|
11
|
+
Run,
|
|
12
|
+
Hyperlink,
|
|
13
|
+
ImageRun,
|
|
14
|
+
Shape,
|
|
15
|
+
TextBox,
|
|
16
|
+
Field,
|
|
17
|
+
Revision,
|
|
18
|
+
} from "docxmlater";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Checks if a paragraph is blank (no meaningful content).
|
|
22
|
+
* Ported from Document.ts:8146-8215
|
|
23
|
+
*/
|
|
24
|
+
export function isParagraphBlank(para: Paragraph): boolean {
|
|
25
|
+
const content = para.getContent();
|
|
26
|
+
|
|
27
|
+
// No content at all
|
|
28
|
+
if (!content || content.length === 0) {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Check all content items
|
|
33
|
+
for (const item of content) {
|
|
34
|
+
// Hyperlinks count as content
|
|
35
|
+
if (item instanceof Hyperlink) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// ImageRun (images embedded in runs) count as content
|
|
40
|
+
// IMPORTANT: Check ImageRun BEFORE Run since ImageRun extends Run
|
|
41
|
+
if (item instanceof ImageRun) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Shapes count as content
|
|
46
|
+
if (item instanceof Shape) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// TextBox count as content
|
|
51
|
+
if (item instanceof TextBox) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Fields count as content
|
|
56
|
+
if (item instanceof Field) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Revisions (track changes) - check nested content for text and hyperlinks
|
|
61
|
+
if (item instanceof Revision) {
|
|
62
|
+
const revisionText = item.getText().trim();
|
|
63
|
+
if (revisionText !== "") {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
// Also check if revision contains hyperlinks (may have empty display text)
|
|
67
|
+
for (const revContent of item.getContent()) {
|
|
68
|
+
if (revContent instanceof Hyperlink) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
continue; // Already checked, move to next item
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Check runs for non-whitespace text
|
|
76
|
+
if ((item as any).getText) {
|
|
77
|
+
const text = (item as any).getText().trim();
|
|
78
|
+
if (text !== "") {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Check for bookmarks
|
|
85
|
+
if (
|
|
86
|
+
para.getBookmarksStart().length > 0 ||
|
|
87
|
+
para.getBookmarksEnd().length > 0
|
|
88
|
+
) {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Checks if a paragraph starts with bold text and has a colon within the first 55 characters.
|
|
97
|
+
* Examples: "Note:", "Warning:", "Note: This can include the following:"
|
|
98
|
+
* Ported from Document.ts:8267-8287
|
|
99
|
+
*/
|
|
100
|
+
export function startsWithBoldColon(para: Paragraph): boolean {
|
|
101
|
+
const content = para.getContent();
|
|
102
|
+
if (!content || content.length === 0) return false;
|
|
103
|
+
|
|
104
|
+
// Get first content item that is a Run (skip any non-Run items at start)
|
|
105
|
+
const firstRun = content.find((item) => item instanceof Run) as
|
|
106
|
+
| Run
|
|
107
|
+
| undefined;
|
|
108
|
+
if (!firstRun) return false;
|
|
109
|
+
|
|
110
|
+
// Check if first run is bold
|
|
111
|
+
const formatting = firstRun.getFormatting();
|
|
112
|
+
if (!formatting.bold) return false;
|
|
113
|
+
|
|
114
|
+
// Check if colon exists within first 55 characters of paragraph text
|
|
115
|
+
const fullText = para.getText();
|
|
116
|
+
if (!fullText) return false;
|
|
117
|
+
|
|
118
|
+
const first55 = fullText.substring(0, 55);
|
|
119
|
+
return first55.includes(":");
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Checks if a paragraph is centered bold text (all text runs are bold and centered).
|
|
124
|
+
* Ported from Document.ts:8499-8523
|
|
125
|
+
*/
|
|
126
|
+
export function isCenteredBoldText(para: Paragraph): boolean {
|
|
127
|
+
if (para.getAlignment() !== "center") return false;
|
|
128
|
+
|
|
129
|
+
const content = para.getContent();
|
|
130
|
+
if (!content || content.length === 0) return false;
|
|
131
|
+
|
|
132
|
+
let hasTextRun = false;
|
|
133
|
+
|
|
134
|
+
for (const item of content) {
|
|
135
|
+
if (item instanceof Run) {
|
|
136
|
+
const text = item.getText();
|
|
137
|
+
if (text && text.trim() !== "") {
|
|
138
|
+
hasTextRun = true;
|
|
139
|
+
const formatting = item.getFormatting();
|
|
140
|
+
if (!formatting.bold) return false;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return hasTextRun;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Checks if a paragraph contains text but no media elements (images, shapes, textboxes).
|
|
150
|
+
* Ported from Document.ts:8531-8548
|
|
151
|
+
*/
|
|
152
|
+
export function isTextOnlyParagraph(para: Paragraph): boolean {
|
|
153
|
+
if (isParagraphBlank(para)) return false;
|
|
154
|
+
|
|
155
|
+
const content = para.getContent();
|
|
156
|
+
for (const item of content) {
|
|
157
|
+
if (item instanceof ImageRun) return false;
|
|
158
|
+
if (item instanceof Shape) return false;
|
|
159
|
+
if (item instanceof TextBox) return false;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const text = para.getText();
|
|
163
|
+
return !!text && text.trim() !== "";
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Detects Table of Contents paragraphs by style.
|
|
168
|
+
* Ported from Document.ts:8397-8401
|
|
169
|
+
*/
|
|
170
|
+
export function isTocParagraph(para: Paragraph): boolean {
|
|
171
|
+
const styleId = para.getStyle();
|
|
172
|
+
if (!styleId) return false;
|
|
173
|
+
const lower = styleId.toLowerCase();
|
|
174
|
+
return /^toc\s?\d$/i.test(lower) || lower.startsWith("toc");
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Checks if a paragraph contains a navigation hyperlink
|
|
179
|
+
* (display text starts with "Top of" or "Return to", case insensitive).
|
|
180
|
+
*/
|
|
181
|
+
export function hasNavigationHyperlink(para: Paragraph): boolean {
|
|
182
|
+
const content = para.getContent();
|
|
183
|
+
if (!content) return false;
|
|
184
|
+
|
|
185
|
+
for (const item of content) {
|
|
186
|
+
if (item instanceof Hyperlink) {
|
|
187
|
+
const text = item.getText().toLowerCase().trim();
|
|
188
|
+
if (text.startsWith("top of") || text.startsWith("return to")) {
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Get the effective left indentation of a paragraph, resolving style-inherited
|
|
198
|
+
* indentation when the paragraph has no direct indentation set.
|
|
199
|
+
*
|
|
200
|
+
* In OOXML, a paragraph's visual indentation can come from:
|
|
201
|
+
* 1. Direct paragraph properties (<w:pPr><w:ind w:left="..."/>)
|
|
202
|
+
* 2. The paragraph's style definition (e.g., ListParagraph defines w:left="720")
|
|
203
|
+
*
|
|
204
|
+
* Paragraph.getFormatting()?.indentation?.left only returns (1).
|
|
205
|
+
* This helper also checks (2) via the Document's style definitions.
|
|
206
|
+
*
|
|
207
|
+
* NOTE: docxmlater currently parses style-level indentation as an empty object
|
|
208
|
+
* (keys exist but values are undefined), so we also check for known indented
|
|
209
|
+
* styles like ListParagraph which defines w:ind w:left="720" in its XML.
|
|
210
|
+
*/
|
|
211
|
+
export function getEffectiveLeftIndent(para: Paragraph, doc: Document): number {
|
|
212
|
+
// 1. Check direct paragraph indentation
|
|
213
|
+
const directIndent = para.getFormatting()?.indentation?.left;
|
|
214
|
+
if (directIndent && directIndent > 0) return directIndent;
|
|
215
|
+
|
|
216
|
+
// 2. Check style-inherited indentation
|
|
217
|
+
const styleId = para.getStyle();
|
|
218
|
+
if (styleId) {
|
|
219
|
+
const style = doc.getStyle(styleId);
|
|
220
|
+
if (style) {
|
|
221
|
+
const stylePf = style.getParagraphFormatting();
|
|
222
|
+
const styleIndent = stylePf?.indentation?.left;
|
|
223
|
+
if (styleIndent && styleIndent > 0) return styleIndent;
|
|
224
|
+
|
|
225
|
+
// Fallback: docxmlater may create an indentation object with undefined values
|
|
226
|
+
// for styles that DO have w:ind in their XML (e.g., ListParagraph w:left="720").
|
|
227
|
+
// If the style has an indentation object at all, it likely defines indentation.
|
|
228
|
+
// ListParagraph is the most common case: Word's built-in style with w:left="720".
|
|
229
|
+
if (stylePf?.indentation && styleId === "ListParagraph") {
|
|
230
|
+
return 720; // Standard ListParagraph indentation
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return 0;
|
|
236
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Removes blank paragraphs between consecutive list items.
|
|
3
|
+
* Ported from Document.ts removeBlanksBetweenListItems() (lines 7159-7216)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Document, Paragraph, Table } from "docxmlater";
|
|
7
|
+
import { isParagraphBlank } from "./paragraphChecks";
|
|
8
|
+
import { tableHasNestedContent } from "./tableGuards";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Removes blank paragraphs between consecutive list items in both
|
|
12
|
+
* body elements and table cells.
|
|
13
|
+
*
|
|
14
|
+
* This should be called AFTER list normalization (e.g., after converting
|
|
15
|
+
* typed list prefixes like "1.", "a." to proper Word numbering) to clean up
|
|
16
|
+
* blanks that were missed because items didn't have Word numbering yet.
|
|
17
|
+
*
|
|
18
|
+
* @param doc - The document to process
|
|
19
|
+
* @returns Number of blank paragraphs removed
|
|
20
|
+
*/
|
|
21
|
+
export function removeBlanksBetweenListItems(doc: Document): number {
|
|
22
|
+
let removed = 0;
|
|
23
|
+
|
|
24
|
+
// Process table cells
|
|
25
|
+
// Skip tables with nested content to avoid violating ECMA-376 cell structure
|
|
26
|
+
for (const table of doc.getAllTables()) {
|
|
27
|
+
if (tableHasNestedContent(table)) continue;
|
|
28
|
+
for (const row of table.getRows()) {
|
|
29
|
+
for (const cell of row.getCells()) {
|
|
30
|
+
let cellParas = cell.getParagraphs();
|
|
31
|
+
for (let ci = 1; ci < cellParas.length - 1; ci++) {
|
|
32
|
+
const prev = cellParas[ci - 1];
|
|
33
|
+
const current = cellParas[ci];
|
|
34
|
+
const next = cellParas[ci + 1];
|
|
35
|
+
|
|
36
|
+
if (!current || !isParagraphBlank(current)) continue;
|
|
37
|
+
|
|
38
|
+
const prevNumbering = prev?.getNumbering();
|
|
39
|
+
const nextNumbering = next?.getNumbering();
|
|
40
|
+
|
|
41
|
+
if (prevNumbering && nextNumbering) {
|
|
42
|
+
cell.removeParagraph(ci);
|
|
43
|
+
ci--;
|
|
44
|
+
removed++;
|
|
45
|
+
cellParas = cell.getParagraphs();
|
|
46
|
+
} else if (prevNumbering && !nextNumbering) {
|
|
47
|
+
// Also remove blank between list item and indented continuation
|
|
48
|
+
const nextIndent = next?.getFormatting()?.indentation?.left || 0;
|
|
49
|
+
if (nextIndent > 0) {
|
|
50
|
+
cell.removeParagraph(ci);
|
|
51
|
+
ci--;
|
|
52
|
+
removed++;
|
|
53
|
+
cellParas = cell.getParagraphs();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Process body-level list items
|
|
62
|
+
for (let bi = 1; bi < doc.getBodyElementCount() - 1; bi++) {
|
|
63
|
+
const prev = doc.getBodyElementAt(bi - 1);
|
|
64
|
+
const current = doc.getBodyElementAt(bi);
|
|
65
|
+
const next = doc.getBodyElementAt(bi + 1);
|
|
66
|
+
|
|
67
|
+
if (!(current instanceof Paragraph) || !isParagraphBlank(current)) continue;
|
|
68
|
+
if (prev instanceof Table || next instanceof Table) continue;
|
|
69
|
+
|
|
70
|
+
if (prev instanceof Paragraph && next instanceof Paragraph) {
|
|
71
|
+
const prevNumbering = prev.getNumbering();
|
|
72
|
+
const nextNumbering = next.getNumbering();
|
|
73
|
+
|
|
74
|
+
if (prevNumbering && nextNumbering) {
|
|
75
|
+
doc.removeBodyElementAt(bi);
|
|
76
|
+
bi--;
|
|
77
|
+
removed++;
|
|
78
|
+
} else if (prevNumbering && !nextNumbering) {
|
|
79
|
+
// Also remove blank between list item and indented continuation
|
|
80
|
+
const nextIndent = next.getFormatting()?.indentation?.left || 0;
|
|
81
|
+
if (nextIndent > 0) {
|
|
82
|
+
doc.removeBodyElementAt(bi);
|
|
83
|
+
bi--;
|
|
84
|
+
removed++;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return removed;
|
|
91
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Removes trailing blank paragraphs from all table cells in the document.
|
|
3
|
+
* Ported from Document.ts removeTrailingBlanksInTableCells() (lines 5485-5502)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Document } from "docxmlater";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Removes trailing blank paragraphs from all table cells.
|
|
10
|
+
* A trailing blank is a blank paragraph at the END of a cell, after all content.
|
|
11
|
+
* Respects the ECMA-376 requirement that each cell must have at least one paragraph.
|
|
12
|
+
*
|
|
13
|
+
* @param doc - The document to process
|
|
14
|
+
* @param options.ignorePreserveFlag - If true, removes trailing blanks even if marked preserved (default: true)
|
|
15
|
+
* @returns Total number of paragraphs removed across all cells
|
|
16
|
+
*/
|
|
17
|
+
export function removeTrailingBlanksInTableCells(
|
|
18
|
+
doc: Document,
|
|
19
|
+
options?: { ignorePreserveFlag?: boolean }
|
|
20
|
+
): number {
|
|
21
|
+
let totalRemoved = 0;
|
|
22
|
+
const ignorePreserve = options?.ignorePreserveFlag ?? true;
|
|
23
|
+
|
|
24
|
+
for (const table of doc.getAllTables()) {
|
|
25
|
+
for (const row of table.getRows()) {
|
|
26
|
+
for (const cell of row.getCells()) {
|
|
27
|
+
totalRemoved += cell.removeTrailingBlankParagraphs({
|
|
28
|
+
ignorePreserveFlag: ignorePreserve,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return totalRemoved;
|
|
35
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Guard functions for skipping tables with nested content during blank line processing.
|
|
3
|
+
* Cells containing nested tables have rawNestedContent that must be preserved —
|
|
4
|
+
* removing paragraphs from these cells can violate ECMA-376 (every w:tc must end with w:p).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Table } from "docxmlater";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Returns true if the table contains any cells with nested tables.
|
|
11
|
+
* Tables with nested content should be skipped during blank line
|
|
12
|
+
* removal to avoid corrupting the cell structure.
|
|
13
|
+
*/
|
|
14
|
+
export function tableHasNestedContent(table: Table): boolean {
|
|
15
|
+
for (const row of table.getRows()) {
|
|
16
|
+
for (const cell of row.getCells()) {
|
|
17
|
+
if (cell.hasNestedTables()) return true;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return false;
|
|
21
|
+
}
|