devexpress-richedit
Version:
DevExpress Rich Text Editor is an advanced word-processing tool designed for working with rich text documents.
168 lines (167 loc) • 10.7 kB
JavaScript
import { UnitConverter } from '@devexpress/utils/lib/class/unit-converter';
import { Rectangle } from '@devexpress/utils/lib/geometry/rectangle';
import { IntervalAlgorithms } from '@devexpress/utils/lib/intervals/algorithms';
import { FixedInterval } from '@devexpress/utils/lib/intervals/fixed';
import { ListUtils } from '@devexpress/utils/lib/utils/list';
import { NumberMapUtils } from '@devexpress/utils/lib/utils/map/number';
import { LayoutTableColumnInfo, LayoutTableInfo } from '../../../layout/table/layout-table-info';
import { TablePosition, TablePositionIndexes } from '../../../model/tables/main-structures/table';
import { TableCellPropertiesMergerMarginLeft, TableCellPropertiesMergerMarginRight } from '../../../model/tables/properties-mergers/table-cell-properties-merger';
import { TablePropertiesMergerShadingInfo } from '../../../model/tables/properties-mergers/table-properties-merger';
import { ConditionalTableStyleFormatting, TableLayoutType, TableRowAlignment } from '../../../model/tables/secondary-structures/table-base-structures';
import { TableWidthUnitType } from '../../../model/tables/secondary-structures/table-units';
import { BorderHelper } from '../borders/border-helper';
import { createGrid } from '../grid-engine/creator';
import { CellInfo } from './cell-info';
import { CellOrderHelper } from './cell-order-helper';
import { RowInfo } from './row-info';
export class TableInfo {
get table() { return this.position.table; }
get tableStyle() { return this.table.style; }
get tblProps() { return this.table.properties; }
get defaultTblProps() { return this.model.defaultTableProperties; }
get defaultTblRowProps() { return this.model.defaultTableRowProperties; }
get defaultTblCellProps() { return this.model.defaultTableCellProperties; }
get currRowInfo() { return this.rows[this.position.rowIndex]; }
get currCellInfo() { return this.currRowInfo.cells[this.position.cellIndex]; }
get isThisTableRowFirstInColumn() { return this.currLayoutTableColumnInfo.tableRows.length == 0; }
get isCurrRowLastInTable() { return this.position.rowIndex == this.grid.table.rows.length - 1; }
get currTablePositionIndexes() {
const actCellInfo = this.currCellInfo;
return new TablePositionIndexes(actCellInfo.rowInfo.rowIndex, actCellInfo.cellIndex);
}
get isSimpleView() {
return this.rowFormatter.manager.innerClientProperties.viewsSettings.isSimpleView;
}
constructor(rowFormatter, table, tableMaxWidth, xPosition, yOffset, pageIndexFromWhichTableWasMoved) {
this.minRowIndex = 0;
this.verticalBorders = [];
this.verticalCursorBorders = [];
this.isThisColumnFirstInTable = true;
this.pageIndexFromWhichTableWasMoved = null;
this.rowFormatter = rowFormatter;
this.model = this.rowFormatter.manager.model;
this.maxWidth = tableMaxWidth;
this.columnWidth = tableMaxWidth;
this.position = new TablePosition(table, 0, 0).init();
this.bordersHelper = new BorderHelper(this, this.model);
this.pageIndexFromWhichTableWasMoved = pageIndexFromWhichTableWasMoved;
this.init(xPosition, yOffset);
this.rows = ListUtils.map(this.table.rows, (_row, rowIndex) => new RowInfo(this, rowIndex));
this.verticalBorders = this.bordersHelper.getVerticalBorders();
this.verticalCursorBorders = this.bordersHelper.getVerticalCursorBorders();
for (let rowInfo of this.rows)
rowInfo.cells = ListUtils.map(rowInfo.row.cells, (_cell, cellIndex) => new CellInfo(cellIndex, rowInfo));
this.cellOrderHelper = new CellOrderHelper(this);
}
static checkIsTableCannotBePlacedOnCurrentPage(rowFormatter, table, yOffset, maxWidth) {
return this.moveRowDownToFitTable(rowFormatter, table, yOffset, TableInfo.getEstimatedTableWidth(table, maxWidth), maxWidth).needNextPage;
}
init(xPosition, yOffset) {
let firstCellLeftMargin = 0;
let firstCellRightMargin = 0;
if (this.table.nestedLevel == 0 && !this.model.compatibilitySettings.matchHorizontalTableIndentsToTextEdge && !this.isSimpleView) {
const firstRow = this.table.rows[0];
const firstCell = firstRow.cells[0];
firstCellLeftMargin = new TableCellPropertiesMergerMarginLeft(this.table, this.model, firstRow.tablePropertiesException)
.getProperty(firstCell.properties, this.tableStyle, firstCell.conditionalFormatting, null)
.asNumberNoPercentType(UnitConverter.twipsToPixels);
firstCellRightMargin = new TableCellPropertiesMergerMarginRight(this.table, this.model, firstRow.tablePropertiesException)
.getProperty(firstCell.properties, this.tableStyle, firstCell.conditionalFormatting, null)
.asNumberNoPercentType(UnitConverter.twipsToPixels);
}
const diff = TableInfo.moveRowDownToFitTable(this.rowFormatter, this.table, yOffset, TableInfo.getEstimatedTableWidth(this.table, this.maxWidth), this.maxWidth);
this.maxWidth = this.maxWidth + (diff.xDiff ? -diff.xDiff : firstCellLeftMargin + firstCellRightMargin);
this.grid = createGrid(this.table, this.rowFormatter.iterator, this.maxWidth, this.rowFormatter.manager.innerClientProperties);
let tableIndent = this.table.getActualTableIndent(this.defaultTblProps);
if (this.isSimpleView && this.table.nestedLevel == 0 && tableIndent.value < 0) {
tableIndent = tableIndent.clone();
tableIndent.value = 0;
}
const tableIndentInPixels = tableIndent.asNumberNoPercentType(UnitConverter.twipsToPixels);
this.xPositionStart = xPosition + tableIndentInPixels + (diff.xDiff ? diff.xDiff : -firstCellLeftMargin) +
this.getShiftHorizontalPosition(xPosition);
this.yPositionStart = yOffset + diff.yDiff;
}
getShiftHorizontalPosition(xPosition) {
const freeSpaceFromTable = this.columnWidth - this.grid.commonWidth;
if (freeSpaceFromTable >= 0)
return 0;
const tableAlignment = this.table.getActualTableAlignment();
if (tableAlignment == TableRowAlignment.Center) {
let result = freeSpaceFromTable / 2 - xPosition;
if (result < 0 && this.isSimpleView)
result = 0;
return result;
}
else if (tableAlignment == TableRowAlignment.Right)
return freeSpaceFromTable - xPosition;
return 0;
}
static getEstimatedTableWidth(table, maxWidth) {
const preferredWidth = table.preferredWidth;
const minWidth = 3 * table.rows[0].getTotalCellsInRowConsiderGrid();
if (table.properties.layoutType == TableLayoutType.Autofit && (preferredWidth.type == TableWidthUnitType.Auto || preferredWidth.type == TableWidthUnitType.Nil))
return minWidth;
else
return Math.min(table.preferredWidth ? table.preferredWidth.asNumber(maxWidth, UnitConverter.twipsToPixels) : minWidth, maxWidth);
}
static getRelationByColumnY(lp, absY) {
return absY - lp.pageArea.y - lp.column.y;
}
static getRelationByColumnX(lp, absX) {
return absX - lp.pageArea.x - lp.column.x;
}
static moveRowDownToFitTable(rowFormatter, table, yOffset, getEstimatedTableWidth, maxAvalibleWidth) {
const lp = rowFormatter.manager.activeFormatter.layoutPosition;
let xDiff = 0;
let currentYOffset = yOffset;
const expectedTableHeight = Math.min(rowFormatter.iterator.getWrap(false).box.height * table.rows.length, lp.column.bottom - currentYOffset);
const maxYOffset = lp.column.bottom - expectedTableHeight - this.empiricalOffset;
let needNextPage = false;
if (table.nestedLevel == 0 && lp.pageArea.subDocument.isMain()) {
const anchoredObjects = [];
NumberMapUtils.forEach(lp.page.anchoredObjectHolder.objects, (ancObj) => {
var _a;
if (ancObj.isInText() && yOffset <= this.getRelationByColumnY(lp, ancObj.y + ancObj.height)
&& ((_a = ancObj.parentCell) === null || _a === void 0 ? void 0 : _a.parentRow.parentTable) !== table)
return anchoredObjects.push(ancObj);
});
anchoredObjects.sort((a, b) => a.y - b.y);
const suitableAnchorObjectWidth = maxAvalibleWidth - getEstimatedTableWidth;
xDiff = this.getXDiff(anchoredObjects, currentYOffset, lp, expectedTableHeight);
for (let i = 0; i < anchoredObjects.length && xDiff > suitableAnchorObjectWidth; i++) {
const ancObj = anchoredObjects[i];
const nextYPos = this.getRelationByColumnY(lp, ancObj.y + ancObj.height) + this.empiricalOffset;
if (maxYOffset < nextYPos) {
xDiff = 0;
needNextPage = true;
break;
}
currentYOffset = nextYPos;
xDiff = this.getXDiff(anchoredObjects, currentYOffset, lp, expectedTableHeight);
}
}
return { xDiff: xDiff, yDiff: currentYOffset - yOffset, needNextPage };
}
static getXDiff(anchoredObjects, yOffset, lp, expectedTableHeight) {
let xDiff = 0;
anchoredObjects.forEach((ancObj) => {
if (ancObj.isInText() && IntervalAlgorithms.getIntersectionNonNullLength(new FixedInterval(this.getRelationByColumnY(lp, ancObj.y), ancObj.height), new FixedInterval(yOffset, expectedTableHeight))) {
xDiff = Math.max(xDiff, this.getRelationByColumnX(lp, ancObj.right) + this.empiricalOffset);
}
});
return xDiff;
}
initLayoutInfo(yPos, parentCell, column) {
this.currColumnHorizontalBorders = [];
const logicInfo = this.currLayoutTableColumnInfo ?
this.currLayoutTableColumnInfo.logicInfo :
new LayoutTableInfo(new TablePropertiesMergerShadingInfo()
.getProperty(this.tblProps, this.tableStyle, ConditionalTableStyleFormatting.WholeTable, this.defaultTblProps)
.getActualColor(this.model.colorProvider), this.grid, this.pageIndexFromWhichTableWasMoved);
this.currLayoutTableColumnInfo = new LayoutTableColumnInfo(parentCell, logicInfo, new Rectangle(this.xPositionStart, yPos, this.grid.commonWidth, 0));
this.rows[this.minRowIndex].initLayoutInfo(true, column);
}
}
TableInfo.empiricalOffset = UnitConverter.inchesToPixels(0.07);