UNPKG

devexpress-richedit

Version:

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

270 lines (269 loc) 20.4 kB
import { UnitConverter } from '@devexpress/utils/lib/class/unit-converter'; import { ListUtils } from '@devexpress/utils/lib/utils/list'; import { BorderInfo } from '../../../model/borders/border-info'; import { TableCellPropertiesMergerBorderBottom, TableCellPropertiesMergerBorderLeft, TableCellPropertiesMergerBorderRight, TableCellPropertiesMergerBorderTop } from '../../../model/tables/properties-mergers/table-cell-properties-merger'; import { TableRowPropertiesMergerCellSpacing } from '../../../model/tables/properties-mergers/table-row-properties-merger'; import { TableBorderCalculator } from '../../../model/tables/secondary-structures/table-border-calculator'; import { HorizontalLineBordersInfo } from './horizontal-line-borders-info'; import { LayoutCursorVerticalTableBorder, LayoutTableBorder } from './layout-table-border'; import { LayoutTableHorizontalBorder } from './layout-table-horizontal-border'; export class TableBorderInfoProvider { constructor(model, table, converter) { this.cellSpacings = ListUtils.map(table.rows, (row) => new TableRowPropertiesMergerCellSpacing(model, table, row.tablePropertiesException) .getProperty(row.properties, table.style, row.conditionalFormatting, model.defaultTableRowProperties) .asNumberNoPercentType(converter)); const defaultProperties = model.defaultTableProperties; this.leftBorder = table.getActualLeftBorder(defaultProperties); this.rightBorder = table.getActualRightBorder(defaultProperties); this.bottomBorder = table.getActualBottomBorder(defaultProperties); this.topBorder = table.getActualTopBorder(defaultProperties); this.horizontalBorder = table.getActualHorizontalBorder(defaultProperties); this.verticalBorder = table.getActualVerticalBorder(defaultProperties); this.leftBorder = TableBorderInfoProvider.borderConvertToPixels(this.leftBorder, converter); this.rightBorder = TableBorderInfoProvider.borderConvertToPixels(this.rightBorder, converter); this.topBorder = TableBorderInfoProvider.borderConvertToPixels(this.topBorder, converter); this.bottomBorder = TableBorderInfoProvider.borderConvertToPixels(this.bottomBorder, converter); this.horizontalBorder = TableBorderInfoProvider.borderConvertToPixels(this.horizontalBorder, converter); this.verticalBorder = TableBorderInfoProvider.borderConvertToPixels(this.verticalBorder, converter); } static borderConvertToPixels(brdInfo, converter) { if (!brdInfo) return null; const newBrd = brdInfo.clone(); newBrd.width = converter(newBrd.width); return newBrd; } } export class BorderHelper { rowCellSpacing(rowIndex) { return this.tblbrdProvider.cellSpacings[rowIndex]; } get borderHorizontal() { return this.tblbrdProvider.horizontalBorder; } get grid() { return this.tableInfo.grid; } get tblStyle() { return this.tableInfo.table.style; } constructor(tableInfo, model) { this.colorProvider = model.colorProvider; this.tableInfo = tableInfo; this.tblbrdProvider = new TableBorderInfoProvider(model, tableInfo.table, UnitConverter.twipsToPixels); } getVerticalBorders() { const verticalBorders = []; const rows = this.grid.table.rows; const tblInfos = this.grid.tableCellInfos; for (var rowIndex = 0, row; row = rows[rowIndex]; rowIndex++) { var cellSpacing = this.rowCellSpacing(rowIndex); const rowBorders = []; const lastRowCellIndex = row.cells.length - 1; var cells = row.cells; verticalBorders.push(rowBorders); for (var cellIndex = 0, cell; cell = cells[cellIndex]; cellIndex++) { const cellBorders = []; rowBorders.push(cellBorders); var cellInfo = tblInfos[rowIndex][cellIndex]; const cellForBorders = cellInfo.getStartRowIndex() != rowIndex ? rows[cellInfo.getStartRowIndex()].cells[cellInfo.getCellIndex(0)] : cell; if (cellSpacing > 0) { if (cellIndex == 0) { cellBorders.push(this.mergeVerticalBorders(null, null, null, null, true, this.tblbrdProvider.leftBorder, (borderWidth) => Math.floor(this.grid.columns.positions[cellInfo.getGridCellIndex()] - borderWidth / 2))); cellBorders.push(this.mergeVerticalBorders(cellForBorders, TableCellPropertiesMergerBorderLeft, null, null, false, this.tblbrdProvider.leftBorder, (_borderWidth) => Math.floor(this.grid.columns.positions[cellInfo.getGridCellIndex()] + cellSpacing * 2))); } else cellBorders.push(this.mergeVerticalBorders(cellForBorders, TableCellPropertiesMergerBorderLeft, null, null, false, this.tblbrdProvider.verticalBorder, (_borderWidth) => Math.floor(this.grid.columns.positions[cellInfo.getGridCellIndex()] + cellSpacing))); cellBorders.push(this.mergeVerticalBorders(cellForBorders, TableCellPropertiesMergerBorderRight, null, null, false, this.tblbrdProvider.verticalBorder, (borderWidth) => Math.floor(this.grid.columns.positions[cellInfo.getGridCellIndex() + cell.columnSpan] - cellSpacing * (cellIndex == cells.length - 1 ? 2 : 1) - borderWidth))); if (cellIndex == lastRowCellIndex) cellBorders.push(this.mergeVerticalBorders(null, null, null, null, true, this.tblbrdProvider.rightBorder, (borderWidth) => Math.floor(this.getCellEndGridPosition(rowIndex, cellIndex) - borderWidth / 2))); } else { if (cellIndex == 0) cellBorders.push(this.mergeVerticalBorders(cellForBorders, TableCellPropertiesMergerBorderLeft, null, null, true, this.tblbrdProvider.leftBorder, (borderWidth) => Math.floor(this.grid.columns.positions[cellInfo.getGridCellIndex()] - borderWidth / 2))); else { const leftCell = cells[cellIndex - 1]; cellBorders.push(this.mergeVerticalBorders(cellForBorders, TableCellPropertiesMergerBorderLeft, leftCell, TableCellPropertiesMergerBorderRight, false, this.tblbrdProvider.verticalBorder, (_borderWidth) => Math.floor(this.grid.columns.positions[cellInfo.getGridCellIndex()]))); } if (cellIndex == lastRowCellIndex) cellBorders.push(this.mergeVerticalBorders(cellForBorders, TableCellPropertiesMergerBorderRight, null, null, true, this.tblbrdProvider.rightBorder, (borderWidth) => Math.floor(this.getCellEndGridPosition(rowIndex, cellIndex) - borderWidth / 2))); } } } return verticalBorders; } getVerticalCursorBorders() { let verticalBorders = []; let rows = this.grid.table.rows; let tblInfos = this.grid.tableCellInfos; for (var rowIndex = 0, row; row = rows[rowIndex]; rowIndex++) { let rowBorders = []; let lastRowCellIndex = row.cells.length - 1; verticalBorders.push(rowBorders); for (var cellIndex = 0; cellIndex < row.cells.length; cellIndex++) { let cellBorders = []; rowBorders.push(cellBorders); if (this.rowCellSpacing(rowIndex) > 0) this.populateVerticalCursorBordersWithSpacing(cellIndex, rowIndex, lastRowCellIndex, this.rowCellSpacing(rowIndex), cellBorders, tblInfos[rowIndex][cellIndex]); else this.populateVerticalCursorBorders(cellIndex, rowIndex, lastRowCellIndex, cellBorders, tblInfos[rowIndex][cellIndex]); } } return verticalBorders; } populateVerticalCursorBordersWithSpacing(cellIndex, rowIndex, lastRowCellIndex, cellSpacing, cellBorders, cellInfo) { if (cellIndex == 0) cellBorders.push(this.createVerticalCursorBorder(this.grid.columns.positions[cellInfo.getGridCellIndex()])); else cellBorders.push(this.createVerticalCursorBorder(this.grid.columns.positions[cellInfo.getGridCellIndex()], cellSpacing)); if (cellIndex == lastRowCellIndex) cellBorders.push(this.createVerticalCursorBorder(this.grid.columns.positions[this.getCellGridColumnIndex(rowIndex, cellIndex)])); } populateVerticalCursorBorders(cellIndex, rowIndex, lastRowCellIndex, cellBorders, cellInfo) { if (cellIndex == 0) cellBorders.push(this.createVerticalCursorBorder(this.grid.columns.positions[cellInfo.getGridCellIndex()])); else cellBorders.push(this.createVerticalCursorBorder(this.grid.columns.positions[cellInfo.getGridCellIndex()])); if (cellIndex == lastRowCellIndex) cellBorders.push(this.createVerticalCursorBorder(this.grid.columns.positions[this.getCellGridColumnIndex(rowIndex, cellIndex)])); } getCellGridColumnIndex(rowIndex, cellIndex) { const cellGridStartPos = this.grid.tableCellInfos[rowIndex][cellIndex].getGridCellIndex(); const cellGridColumnEndIndex = cellGridStartPos + this.grid.table.rows[rowIndex].cells[cellIndex].columnSpan; return cellGridColumnEndIndex; } createVerticalCursorBorder(modelXPos, cellSpacing = -1) { const DEFAULT_CURSOR_BORDER_WIDTH = 4; let size = cellSpacing != -1 ? cellSpacing : DEFAULT_CURSOR_BORDER_WIDTH; return new LayoutCursorVerticalTableBorder(Math.floor(modelXPos - size / 2), 0, 0, new BorderInfo().getLayoutBorder(this.colorProvider)); } mergeVerticalBorders(cellA, mergerCellA, cellB, mergerCellB, isOutsideCellBorders, tableBorderInfo, getX) { const brd = BorderHelper.mergeThreeBorders(this.colorProvider, cellA, mergerCellA, cellB, mergerCellB, isOutsideCellBorders, tableBorderInfo, this.tblStyle, UnitConverter.twipsToPixels); const tableVerticalBorder = new LayoutTableBorder(0, 0, 0, brd ? brd.getLayoutBorder(this.colorProvider) : null); if (tableVerticalBorder.borderInfo) tableVerticalBorder.xPos = getX(tableVerticalBorder.borderInfo.width); return tableVerticalBorder; } getHorizontalBordersByRow(rowIndex, isRowFirstInLayoutColumn, isRowLastInLayoutColumn) { const linesInRow = []; if (this.rowCellSpacing(rowIndex) > 0) { if (isRowFirstInLayoutColumn || rowIndex == 0) linesInRow.push(this.collectTableHorizontalBorders(this.tblbrdProvider.topBorder, rowIndex)); linesInRow.push(this.collectOneCellAndTableHorizontalBorders(rowIndex, TableCellPropertiesMergerBorderTop, rowIndex - 1, this.tblbrdProvider.horizontalBorder, !isRowFirstInLayoutColumn)); linesInRow.push(this.collectOneCellAndTableHorizontalBorders(rowIndex, TableCellPropertiesMergerBorderBottom, rowIndex + 1, this.tblbrdProvider.horizontalBorder, !isRowLastInLayoutColumn)); if (isRowLastInLayoutColumn) linesInRow.push(this.collectTableHorizontalBorders(this.tblbrdProvider.bottomBorder, rowIndex)); } else { if (isRowFirstInLayoutColumn || rowIndex == 0) linesInRow.push(this.collectOneCellAndTableHorizontalBorders(rowIndex, TableCellPropertiesMergerBorderTop, rowIndex - 1, this.tblbrdProvider.topBorder, false)); else linesInRow.push(this.collectThreeBorders(rowIndex, TableCellPropertiesMergerBorderTop, TableCellPropertiesMergerBorderBottom, this.tblbrdProvider.horizontalBorder)); if (isRowLastInLayoutColumn) linesInRow.push(this.collectOneCellAndTableHorizontalBorders(rowIndex, TableCellPropertiesMergerBorderBottom, rowIndex + 1, this.tblbrdProvider.bottomBorder, false)); } return linesInRow; } collectTableHorizontalBorders(tableBorderInfo, rowIndex) { const cells = this.grid.table.rows[rowIndex].cells; const lastCellIndex = cells.length - 1; const lastCell = cells[lastCellIndex]; const firstCellGridInfo = this.grid.tableCellInfos[rowIndex][0]; const lastCellGridInfo = this.grid.tableCellInfos[rowIndex][lastCellIndex]; const horizBorder = new LayoutTableHorizontalBorder(); horizBorder.borderInfo = tableBorderInfo.getLayoutBorder(this.colorProvider); horizBorder.xPosition = Math.floor(this.grid.columns.positions[firstCellGridInfo.getGridCellIndex()]); horizBorder.length = Math.ceil(this.grid.columns.positions[lastCellGridInfo.getGridCellIndex() + lastCell.columnSpan]) - horizBorder.xPosition; const horizBordersInfo = new HorizontalLineBordersInfo(true); horizBordersInfo.borders.push(horizBorder); horizBordersInfo.updateWidth(tableBorderInfo ? tableBorderInfo.width : 0); return horizBordersInfo; } collectOneCellAndTableHorizontalBorders(cellBorderRowIndex, getCurrCellBorderMerger, tableBorderRowIndex, tableBorderInfo, isTableBorderRowIndexValid) { const rows = this.grid.table.rows; const cells = rows[cellBorderRowIndex].cells; const rowCellSpacing = this.rowCellSpacing(cellBorderRowIndex); const horizBordersInfo = new HorizontalLineBordersInfo(true); for (let cellIndex = 0, cell; cell = cells[cellIndex]; cellIndex++) { const currCellGridTableInfo = this.grid.tableCellInfos[cellBorderRowIndex][cellIndex]; const currCellGridStartIndex = currCellGridTableInfo.getGridCellIndex(); const tableCellGridTableInfo = isTableBorderRowIndexValid ? this.grid.tableCellGridInfos[tableBorderRowIndex][currCellGridStartIndex] : null; if (currCellGridTableInfo == tableCellGridTableInfo) continue; const currCellXPosition = Math.floor(this.grid.columns.positions[currCellGridStartIndex] + rowCellSpacing * (cellIndex == 0 ? 2 : 1)); const currCellEndGridColumn = Math.ceil(this.grid.columns.positions[currCellGridStartIndex + cell.columnSpan] - rowCellSpacing * (cellIndex == cells.length - 1 ? 2 : 1)); const currCellBorder = getCurrCellBorderMerger ? TableBorderInfoProvider.borderConvertToPixels((new getCurrCellBorderMerger(cell.parentRow.tablePropertiesException, !isTableBorderRowIndexValid)) .getProperty(cell.properties, this.tblStyle, cell.conditionalFormatting, null), UnitConverter.twipsToPixels) : null; const horizBorder = new LayoutTableHorizontalBorder(); horizBorder.xPosition = currCellXPosition; horizBorder.length = currCellEndGridColumn - currCellXPosition; const brd = (currCellBorder ? currCellBorder : tableBorderInfo); horizBorder.borderInfo = brd ? brd.getLayoutBorder(this.colorProvider) : null; horizBordersInfo.borders.push(horizBorder); horizBordersInfo.updateWidth(horizBorder.borderInfo ? horizBorder.borderInfo.width : 0); } return horizBordersInfo; } collectThreeBorders(rowIndex, getCurrCellBorderMerger, getTopCellBorderMerger, tableBorderInfo) { const horizBordersInfo = new HorizontalLineBordersInfo(true); const gridColumnsNum = this.grid.columns.numColumns; const prevRowIndex = rowIndex - 1; const gridInfosPrevRow = this.grid.tableCellGridInfos[prevRowIndex]; const gridInfosCurrRow = this.grid.tableCellGridInfos[rowIndex]; const prevRowCells = this.grid.table.rows[prevRowIndex].cells; const currRowCells = this.grid.table.rows[rowIndex].cells; for (let cellGridIndex = 0; cellGridIndex < gridColumnsNum; cellGridIndex++) { const prevCellInfo = gridInfosPrevRow[cellGridIndex]; const currCellInfo = gridInfosCurrRow[cellGridIndex]; if (!prevCellInfo && !currCellInfo || prevCellInfo && prevRowIndex != prevCellInfo.getStartRowIndex() + prevCellInfo.getNumRowsInCell() - 1) continue; let prevCell = prevCellInfo ? prevRowCells[prevCellInfo.getCellIndex(prevRowIndex - prevCellInfo.getStartRowIndex())] : null; let currCell = currCellInfo ? currRowCells[currCellInfo.getCellIndex(rowIndex - currCellInfo.getStartRowIndex())] : null; const horizBorder = new LayoutTableHorizontalBorder(); horizBorder.xPosition = Math.floor(this.grid.columns.positions[cellGridIndex]); horizBorder.length = Math.ceil(this.grid.columns.width[cellGridIndex]); const brd = BorderHelper.mergeThreeBorders(this.colorProvider, currCell, getCurrCellBorderMerger, prevCell, getTopCellBorderMerger, false, tableBorderInfo, this.tblStyle, UnitConverter.twipsToPixels); horizBorder.borderInfo = brd ? brd.getLayoutBorder(this.colorProvider) : null; horizBordersInfo.updateWidth(horizBorder.borderInfo ? horizBorder.borderInfo.width : 0); horizBordersInfo.borders.push(horizBorder); } return horizBordersInfo; } getCellEndGridPosition(rowIndex, cellIndex) { const cellGridStartPos = this.grid.tableCellInfos[rowIndex][cellIndex].getGridCellIndex(); const cellGridColumnEndIndex = cellGridStartPos + this.grid.table.rows[rowIndex].cells[cellIndex].columnSpan; return Math.ceil(this.grid.columns.positions[cellGridColumnEndIndex]); } static mergeThreeBorders(colorProvider, cellA, mergerCellA, cellB, mergerCellB, isOutsideCellBorders, tableBorderInfo, tblStyle, converter) { let tblBrd; if (cellA && cellB) { tblBrd = TableBorderCalculator.getPowerfulBorder(colorProvider, TableBorderInfoProvider.borderConvertToPixels((new mergerCellA(cellA.parentRow.tablePropertiesException, isOutsideCellBorders)) .getProperty(cellA.properties, tblStyle, cellA.conditionalFormatting, null), converter), TableBorderInfoProvider.borderConvertToPixels((new mergerCellB(cellB.parentRow.tablePropertiesException, isOutsideCellBorders)) .getProperty(cellB.properties, tblStyle, cellB.conditionalFormatting, null), converter)); } else { const cell = cellA || cellB; if (cell) tblBrd = TableBorderInfoProvider.borderConvertToPixels((new (mergerCellA || mergerCellB)(cell.parentRow.tablePropertiesException, isOutsideCellBorders)) .getProperty(cell.properties, tblStyle, cell.conditionalFormatting, null), converter); } if (!tblBrd) tblBrd = tableBorderInfo; return tblBrd; } static getLeftBorder(colorProvider, pos, siblingCell, tblBrdProv, converter) { return BorderHelper.getRightLeftBorder(colorProvider, pos, siblingCell, tblBrdProv, converter, pos.cellIndex == 0, TableCellPropertiesMergerBorderLeft, TableCellPropertiesMergerBorderRight); } static getRightBorder(colorProvider, pos, siblingCell, tblBrdProv, converter) { return BorderHelper.getRightLeftBorder(colorProvider, pos, siblingCell, tblBrdProv, converter, pos.cellIndex == pos.row.cells.length - 1, TableCellPropertiesMergerBorderRight, TableCellPropertiesMergerBorderLeft); } static getRightLeftBorder(colorProvider, pos, siblingCell, tblBrdProv, converter, isOutsideBorder, currentMerger, siblingMerger) { const tableStyle = pos.table.style; return tblBrdProv.cellSpacings[pos.rowIndex] > 0 ? BorderHelper.mergeThreeBorders(colorProvider, pos.cell, currentMerger, null, null, isOutsideBorder, isOutsideBorder ? tblBrdProv.leftBorder : tblBrdProv.verticalBorder, tableStyle, converter) : (isOutsideBorder ? BorderHelper.mergeThreeBorders(colorProvider, pos.cell, currentMerger, null, null, isOutsideBorder, tblBrdProv.leftBorder, tableStyle, converter) : BorderHelper.mergeThreeBorders(colorProvider, pos.cell, currentMerger, siblingCell, siblingMerger, isOutsideBorder, tblBrdProv.verticalBorder, tableStyle, converter)); } }