UNPKG

devexpress-richedit

Version:

DevExpress Rich Text Editor is an advanced word-processing tool designed for working with rich text documents.

251 lines (250 loc) 11.7 kB
import { SubDocumentInterval } from '../../../../model/sub-document'; import { TablePropertiesMask } from '../../../../model/tables/properties/table-properties'; import { TableCellMergingState, TableLayoutType } from '../../../../model/tables/secondary-structures/table-base-structures'; import { TableBorderCalculator } from '../../../../model/tables/secondary-structures/table-border-calculator'; import { TableWidthUnitType } from '../../../../model/tables/secondary-structures/table-units'; import { ListUtils } from '@devexpress/utils/lib/utils/list'; import { NumberMapUtils } from '@devexpress/utils/lib/utils/map/number'; import { RtfTableColumnsCalculator } from './rtf-table-columns-calculator'; import { FixedInterval } from '@devexpress/utils/lib/intervals/fixed'; export class RtfTableConverter { constructor(tableReader) { this.rtfCellMap = {}; this.tableReader = tableReader; this.rtfCellMap = {}; } get subDocument() { return this.tableReader.data.subDocument; } convertTables(rtfTables, _isCopySingleCellAsText) { NumberMapUtils.clear(this.rtfCellMap); this.tablesQueue = ListUtils.shallowCopy(rtfTables); let tblIndex = 0; while (this.tablesQueue.length > 0) { const table = this.tablesQueue[0]; this.tablesQueue.shift(); this.convertTable(table, tblIndex); tblIndex++; } } convertTable(rtfTable, tblIndex) { if (!this.rtfTableIsValid(rtfTable)) return; let parentCell = null; if (rtfTable.parentCell != null) { parentCell = this.rtfCellMap[rtfTable.parentCell.idForParentCellMap]; } this.prepareRtfTable(rtfTable); const table = rtfTable.createTable(tblIndex, parentCell, this.tableReader.data.documentModel); this.convertTableCore(table, rtfTable); this.subDocument.tables.push(table); } rtfTableIsValid(rtfTable) { if (rtfTable.rows.length == 0) return false; const lastRow = ListUtils.last(rtfTable.rows); if (lastRow.cells.length > 0) return true; rtfTable.rows.splice(rtfTable.rows.length - 1, 1); return this.rtfTableIsValid(rtfTable); } prepareRtfTable(table) { if (this.shouldUseFloatingPosition(table)) { } const tablePreferredWidth = table.properties.preferredWidth; if (tablePreferredWidth.type == TableWidthUnitType.Nil) { tablePreferredWidth.type = TableWidthUnitType.Auto; tablePreferredWidth.value = 0; } if (!table.properties.coreProperties.getUseValue(TablePropertiesMask.UseRightMargin) && !table.properties.coreProperties.getUseValue(TablePropertiesMask.UseLeftMargin) && table.properties.useHalfSpace) { const margin = table.properties.halfSpace; table.properties.coreProperties.cellMargins.left.type = TableWidthUnitType.ModelUnits; table.properties.coreProperties.cellMargins.left.value = margin; table.properties.coreProperties.cellMargins.right.type = TableWidthUnitType.ModelUnits; table.properties.coreProperties.cellMargins.right.value = margin; } table.indent = this.calculateTableLeftOffset(table); const indent = table.properties.coreProperties.indent; if (indent.type != TableWidthUnitType.ModelUnits) { indent.value = this.calculateTableIndent(table); indent.type = TableWidthUnitType.ModelUnits; } } shouldUseFloatingPosition(_table) { return false; } calculateTableLeftOffset(table) { const rows = table.rows; let result = rows[0].properties.left; const rowsCount = rows.length; for (let i = 1; i < rowsCount; i++) result = Math.min(result, rows[i].properties.left); return result; } calculateTableIndent(table) { const cellProperties = table.rows[0].cells[0].properties; const leftBorder = cellProperties.coreProperties.borders.left; const leftMargin = this.getCellLeftMargin(table); const borderWidth = TableBorderCalculator.getActualWidth(leftBorder); return Math.max(borderWidth / 2, this.getActualWidth(leftMargin)) + table.indent; } getCellLeftMargin(table) { const cellProperties = table.rows[0].cells[0].properties; if (cellProperties.coreProperties.cellMargins.left.type == TableWidthUnitType.ModelUnits) return cellProperties.coreProperties.cellMargins.left; return table.properties.coreProperties.cellMargins.left; } getActualWidth(unitInfo) { if (unitInfo.type == TableWidthUnitType.ModelUnits) return unitInfo.value; return 0; } convertTableCore(table, rtfTable) { const tableGrid = this.calculateTableGrid(rtfTable); const rows = table.rows; const tableLayoutType = table.properties.layoutType; rtfTable.rows.forEach((rtfRow) => { this.prepareRtfRow(rtfRow, tableGrid, tableLayoutType); const row = rtfRow.createTableRow(table); rows.push(row); this.convertRow(row, rtfRow); }); } calculateTableGrid(table) { const calculator = new RtfTableColumnsCalculator(); return calculator.calculate(table, table.indent); } prepareRtfRow(row, grid, tableLayoutType) { const gridBefore = grid.binarySearchLeft(row.properties.left); row.properties.gridBefore = gridBefore; if (row.properties.widthBefore.value == 0 && gridBefore > 0) { row.properties.widthBefore.value = row.offset; row.properties.widthBefore.type = TableWidthUnitType.ModelUnits; } this.prepareRtfRowCells(row, grid, gridBefore, tableLayoutType); const lastColumnIndex = grid.collection.length - 1; const gridAfter = lastColumnIndex - this.calculateRowColumnSpan(row); row.properties.gridAfter = gridAfter; if (row.properties.widthAfter.value == 0 && gridAfter > 0) { let widthAfter = 1; if (row.properties.widthAfter.type == TableWidthUnitType.ModelUnits) { widthAfter = this.calculateWidthAfter(row); } else widthAfter = grid.collection[lastColumnIndex] - grid.collection[lastColumnIndex - gridAfter]; row.properties.widthAfter.value = widthAfter; row.properties.widthAfter.type = TableWidthUnitType.ModelUnits; } } calculateWidthAfter(row) { const rowWidth = this.calculateTotalRowWidth(row); if (rowWidth < 0) return 1; const rows = row.table.rows; let maxWidth = 0; for (let i = 0; i < rows.length; i++) { const currentRow = rows[i]; if (currentRow === row) continue; const currentRowWidth = this.calculateTotalRowWidth(currentRow); if (currentRowWidth > 0) maxWidth = Math.max(maxWidth, currentRowWidth); } return Math.max(1, rowWidth - maxWidth); } calculateTotalRowWidth(row) { let totalWidth = 0; const cells = row.cells; const count = cells.length; for (let i = 0; i < count; i++) { const width = cells[i].properties.preferredWidth; if (width.type == TableWidthUnitType.ModelUnits) totalWidth += width.value; else return -1; } return totalWidth; } prepareRtfRowCells(row, grid, gridBefore, tableLayoutType) { let prevBorderIndex = gridBefore; let left = grid.collection[gridBefore]; for (let i = 0; i < row.cells.length; i++) { const cell = row.cells[i]; const right = cell.properties.right > left ? cell.properties.right : left; const borderIndex = grid.binarySearchRight(right) - this.calculateEquidistantCellOrder(row.cells, i, right); const columnSpan = borderIndex - prevBorderIndex; cell.columnSpan = Math.max(1, columnSpan); const preferredWidth = cell.properties.preferredWidth; if (preferredWidth.type == TableWidthUnitType.Nil || (preferredWidth.type == TableWidthUnitType.Auto && tableLayoutType != TableLayoutType.Autofit)) { preferredWidth.value = grid.collection[borderIndex] - grid.collection[prevBorderIndex]; preferredWidth.type = TableWidthUnitType.ModelUnits; } prevBorderIndex = Math.max(borderIndex, prevBorderIndex); left = right; } } calculateEquidistantCellOrder(cells, index, left) { const count = cells.length; let equidistantCellsCount = 0; for (let i = index + 1; i < count; i++) { if (cells[i].properties.right > left) break; equidistantCellsCount++; } return equidistantCellsCount; } calculateRowColumnSpan(row) { let result = row.properties.gridBefore; const cellsCount = row.cells.length; for (let i = 0; i < cellsCount; i++) result += row.cells[i].columnSpan; return result; } convertRow(row, rtfRow) { const rtfCells = rtfRow.cells; for (let i = 0; i < rtfCells.length; i++) this.convertCell(row, rtfCells[i]); } convertCell(row, rtfCell) { if (rtfCell.properties.horizontalMerging == TableCellMergingState.Restart) this.mergeCells(rtfCell); const cell = rtfCell.createCell(row, this.subDocument); row.cells.push(cell); this.rtfCellMap[rtfCell.idForParentCellMap] = cell; } mergeCells(firstCell) { const nextIndex = firstCell.index + 1; const cells = firstCell.row.cells; while (nextIndex < cells.length && cells[nextIndex].properties.horizontalMerging == TableCellMergingState.Continue) { const nextRtfCell = cells[nextIndex]; const parentCellMap = this.tableReader.parentCellMap; const tables = parentCellMap[nextRtfCell.idForParentCellMap]; if (tables !== undefined) { for (let table of tables) ListUtils.remove(this.tablesQueue, table); delete parentCellMap[nextRtfCell.idForParentCellMap]; } firstCell.columnSpan += nextRtfCell.columnSpan; this.removeCell(cells, nextRtfCell); } } removeCell(cells, cell) { ListUtils.remove(cells, cell); const count = cells.length; for (let i = cell.index; i < count; i++) cells[i].index--; const cellInterval = FixedInterval.fromPositions(cell.startPos, cell.endPos); this.tableReader.data.intervalsToDelete.push(new SubDocumentInterval(this.subDocument, cellInterval)); } recalcParagraphIndexes(_removedCell) { } recalcParagraphIndexesInRow(_row, _cellIndex, _delta) { } recalcParagraphIndexesInTable(_table, _rowIndex, _delta, _paragraphIndex) { } recalcParagraphIndexesInTables(delta, paragraphIndex) { const count = this.tablesQueue.length; for (let i = 0; i < count; i++) this.recalcParagraphIndexesInTable(this.tablesQueue[i], 0, delta, paragraphIndex); } }