UNPKG

devexpress-richedit

Version:

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

204 lines (203 loc) 10.3 kB
import { TableHeightUnitType } from '../../model/tables/secondary-structures/table-units'; import { Errors } from '@devexpress/utils/lib/errors'; import { ListUtils } from '@devexpress/utils/lib/utils/list'; import { NumberMapUtils } from '@devexpress/utils/lib/utils/map/number'; import { TablesManipulator } from '../../model/manipulators/tables/tables-manipulator'; import { TableCellMergingState } from '../../model/tables/secondary-structures/table-base-structures'; import { TableCellUtils } from '../../model/tables/table-utils'; import { Grid } from '../../layout-formatter/table/grid-engine/grid'; import { ParagraphManipulator } from '../../model/manipulators/paragraph-manipulator/paragraph-manipulator'; export class TableRestorer { static paragraphMarkBetween(model) { NumberMapUtils.forEach(model.subDocuments, sd => { for (let tablesOnLevel of sd.tablesByLevels) { let prevTable = tablesOnLevel[0]; if (prevTable) { for (let index = 1, table; table = tablesOnLevel[index];) { if (table.getStartPosition() - prevTable.getEndPosition() == 0) { TableRestorer.joinTables(sd, [prevTable, table]); } else { prevTable = table; index++; } } } } }); } static joinTables(subDocument, tables) { if (tables.length < 2) throw new Error("tables.length should be > 2"); const resultTable = tables[0]; ListUtils.forEach(tables, table => { const isEqualProps = resultTable.properties.equals(table.properties); for (let row of table.rows) { row.parentTable = resultTable; resultTable.rows.push(row); if (!isEqualProps) row.tablePropertiesException = table.properties; } }, 1); ListUtils.forEach(tables, table => TablesManipulator.removeTableCore(table, subDocument.tables, subDocument.tablesByLevels), 1); const colCount = resultTable.getTotalVirtualColumnsCount(); for (let row of resultTable.rows) row.gridAfter += colCount - row.getTotalCellsInRowConsiderGrid(); } static fixAllTables(model) { NumberMapUtils.forEach(model.subDocuments, sd => { TableRestorer.fixTables(sd); }); } static fixTables(subDocument) { subDocument.tables.forEach((table) => { TableRestorer.fixTable(table, subDocument); }); } static fixLastParagraphs(model) { NumberMapUtils.forEach(model.subDocuments, sd => { TableRestorer.fixLastParagraph(sd); }); } static fixLastParagraph(sd) { const endPosition = sd.interval.end; if (ListUtils.elementBy(sd.tables, (table) => table.getEndPosition() == endPosition)) TableRestorer.insertParagraphInSubDocumentEnd(sd); } static insertParagraphInSubDocumentEnd(sd) { ParagraphManipulator.insertParagraphInEnd(sd, sd.getDocumentEndPosition(), sd.getLastRun().getCharPropsBundle(sd.documentModel)); } static fixTable(table, subDocument) { TableRestorer.normalize(table); TableRestorer.normalizeRows(table, subDocument); TableRestorer.normalizeTableGrid(table); TablesManipulator.normalizeCellColumnSpansWithoutHistory(table, true); } static normalizeRows(table, subDocument) { ListUtils.reverseForEach(table.rows, (row, rowIndex) => { if (ListUtils.allOf(row.cells, (cell) => cell.verticalMerging == TableCellMergingState.Continue)) { if (rowIndex > 0) { const prevRowIndex = rowIndex - 1; if (row.height.type != TableHeightUnitType.Auto) table.rows[prevRowIndex].height.value += row.height.value; TablesManipulator.removeRowCore(subDocument, table, rowIndex); const cellInfos = new Grid(table).tableCellInfos; ListUtils.forEach(table.rows[prevRowIndex].cells, (cell, cellIndex) => { const cellInfo = cellInfos[prevRowIndex][cellIndex]; if (cell.verticalMerging == TableCellMergingState.Restart && cellInfo.getStartRowIndex() == cellInfo.getEndRowIndex() - 1) cell.verticalMerging = TableCellMergingState.None; }); } } }); } static normalizeTableGrid(table) { const maxEndColumnIndex = table.getTotalVirtualColumnsCount(); table.rows.forEach((row) => { const gridAfterDelta = maxEndColumnIndex - row.getTotalCellsInRowConsiderGrid(); if (gridAfterDelta) row.gridAfter += gridAfterDelta; }); } static normalize(table) { if (table.rows.length == 1) { TableRestorer.removeInvalidVerticalSpansFromTableWithOneRow(table.rows); return; } table.rows.forEach((row, rowIndex) => { for (var cellIndex = 0; cellIndex < row.cells.length; cellIndex++) { var cell = row.cells[cellIndex]; var mergingState = cell.verticalMerging; if (mergingState == TableCellMergingState.None) continue; if (mergingState == TableCellMergingState.Continue) TableRestorer.actualizeVerticalMergingForContinueCell(cell, rowIndex); else TableRestorer.actualizeVerticalMergingForRestartCell(cell, rowIndex); } }); } static getCellColumnIndexConsiderRowGrid(cell) { return TableCellUtils.getStartColumnIndex(cell); } static getAbsoluteCellIndexInRow(row, columnIndex, layoutIndex) { if (row.cells.length == 0) throw new Error(Errors.InternalException); const cells = row.cells; const cellsCount = cells.length; let cellIndex = 0; columnIndex -= layoutIndex ? row.gridBefore : row.gridBefore; while (columnIndex > 0 && cellIndex < cellsCount) { var currentCell = cells[cellIndex]; columnIndex -= layoutIndex ? currentCell.columnSpan : currentCell.columnSpan; if (columnIndex >= 0) cellIndex++; } return cellIndex; } static getLowerCell(rows, currentRowIndex, columnIndex) { return TableRestorer.getCell(rows, currentRowIndex + 1, columnIndex); } static getUpperCell(rows, currentRowIndex, columnIndex) { return TableRestorer.getCell(rows, currentRowIndex - 1, columnIndex); } static getCell(rows, rowIndex, columnIndex) { const row = rows[rowIndex]; if (row == null) return null; var index = TableRestorer.getAbsoluteCellIndexInRow(row, columnIndex, false); if (row.cells.length <= index) return TableRestorer.getCell(rows, rowIndex - 1, columnIndex); return row.cells[index]; } static actualizeVerticalMergingForContinueCellCore(cell, rowIndex, columnIndex) { if (cell.parentRow.isLastRowInTable) { cell.verticalMerging = TableCellMergingState.None; return; } var lowerCell = TableRestorer.getLowerCell(cell.parentRow.parentTable.rows, rowIndex, columnIndex); if (lowerCell == null || lowerCell.verticalMerging != TableCellMergingState.Continue) cell.verticalMerging = TableCellMergingState.None; else cell.verticalMerging = TableCellMergingState.Restart; } static actualizeVerticalMergingForContinueCell(cell, rowIndex) { const columnIndex = TableRestorer.getCellColumnIndexConsiderRowGrid(cell); if (rowIndex == 0) { TableRestorer.actualizeVerticalMergingForContinueCellCore(cell, rowIndex, columnIndex); return; } const upperCell = TableRestorer.getUpperCell(cell.parentRow.parentTable.rows, rowIndex, columnIndex); if (upperCell == null || upperCell.verticalMerging == TableCellMergingState.None) TableRestorer.actualizeVerticalMergingForContinueCellCore(cell, rowIndex, columnIndex); else if (upperCell != null) { const expectedLeftColumn = TableRestorer.getCellColumnIndexConsiderRowGrid(upperCell); const expectedRightColumn = expectedLeftColumn + upperCell.columnSpan; const actualLeftColumn = TableRestorer.getCellColumnIndexConsiderRowGrid(cell); const actualRightColumn = actualLeftColumn + cell.columnSpan; if (expectedLeftColumn != actualLeftColumn || expectedRightColumn != actualRightColumn) cell.verticalMerging = TableCellMergingState.None; } } static actualizeVerticalMergingForRestartCell(cell, rowIndex) { if (cell.parentRow.isLastRowInTable) { cell.verticalMerging = TableCellMergingState.None; return; } var currentCellColumnIndex = TableRestorer.getCellColumnIndexConsiderRowGrid(cell); var lowerCell = TableRestorer.getLowerCell(cell.parentRow.parentTable.rows, rowIndex, currentCellColumnIndex); if (lowerCell == null || lowerCell.verticalMerging != TableCellMergingState.Continue || cell.columnSpan != lowerCell.columnSpan) { cell.verticalMerging = TableCellMergingState.None; } else { var lowerCellColumnIndex = TableRestorer.getCellColumnIndexConsiderRowGrid(lowerCell); if (lowerCellColumnIndex != currentCellColumnIndex) cell.verticalMerging = TableCellMergingState.None; } } static removeInvalidVerticalSpansFromTableWithOneRow(rows) { rows[0].cells.forEach((cell) => { cell.verticalMerging = TableCellMergingState.None; }); } }