devexpress-richedit
Version:
DevExpress Rich Text Editor is an advanced word-processing tool designed for working with rich text documents.
343 lines (342 loc) • 22.4 kB
JavaScript
import { LayoutBoxIteratorMainSubDocument } from '../../layout-engine/layout-box-iterator/layout-box-iterator-main-sub-document';
import { LayoutPositionCreatorConflictFlags, LayoutPositionMainSubDocumentCreator, LayoutPositionOtherSubDocumentCreator } from '../../layout-engine/layout-position-creator';
import { DocumentLayoutDetailsLevel } from '../../layout/document-layout-details-level';
import { LayoutPageFlags } from '../../layout/main-structures/layout-page';
import { CharacterPropertiesMask } from '../../model/character/enums';
import { AnchorInfo } from '../../model/floating-objects/anchor-info';
import { AnchorObjectHorizontalPositionAlignment, AnchorObjectHorizontalPositionType, AnchorObjectVerticalPositionAlignment, AnchorObjectVerticalPositionType } from '../../model/floating-objects/enums';
import { RunType } from '../../model/runs/run-type';
import { ConditionalTableStyleFormatting, TableCellMergingState } from '../../model/tables/secondary-structures/table-base-structures';
import { Log } from './logger/base-logger/log';
import { LogListHelper } from './logger/base-logger/log-list-helper';
import { LogObjToStr } from './logger/base-logger/log-obj-to-str';
import { UnitConverter } from '@devexpress/utils/lib/class/unit-converter';
import { IntervalAlgorithms } from '@devexpress/utils/lib/intervals/algorithms';
import { FixedInterval } from '@devexpress/utils/lib/intervals/fixed';
import { EnumUtils } from '@devexpress/utils/lib/utils/enum';
import { ListUtils } from '@devexpress/utils/lib/utils/list';
import { NumberMapUtils } from '@devexpress/utils/lib/utils/map/number';
import { StringUtils } from '@devexpress/utils/lib/utils/string';
import { ModelScrollManager } from '../../scroll/model-scroll-manager';
export class TEST_CLASS {
static pagesIntervals(control) {
console.log(ListUtils.map(control.layout.pages, TEST_CLASS.pageIntervals).join("\n"));
}
static pageIntervals(page) {
return "\tPage[" + page.index + "]Intervals: " +
LogListHelper.level_1(LogObjToStr.fixedInterval, page.getContentIntervals(), "\t\t", "\t")();
}
static getLayoutPosition(control, pos) {
const subDocument = control.selection.activeSubDocument;
const selection = control.selection;
return (subDocument.isMain()
? new LayoutPositionMainSubDocumentCreator(control.layout, subDocument, pos, DocumentLayoutDetailsLevel.Character)
: new LayoutPositionOtherSubDocumentCreator(control.layout, subDocument, pos, selection.pageIndex, DocumentLayoutDetailsLevel.Character))
.create(new LayoutPositionCreatorConflictFlags().setDefault(selection.endOfLine), new LayoutPositionCreatorConflictFlags().setDefault(false));
}
static recalcPageIntervals(control) {
for (let page of control.layout.pages) {
page.flags.set(LayoutPageFlags.IsIntervalsCorrect, false);
page.calculateContentIntervals(control.layout.anchorObjectsPositionInfo, false);
page.getContentIntervals();
}
}
static clearAllRunMergedProperties(control) {
ListUtils.forEach(control.selection.activeSubDocument.chunks, (chunk) => ListUtils.forEach(chunk.textRuns, (run) => run.resetCharacterMergedProperties()));
control.layoutFormatterManager.invalidator.onIntervalChanged(control.selection.activeSubDocument.id, new FixedInterval(0, control.selection.activeSubDocument.getDocumentEndPosition()));
while (!control.layout.isFullyFormatted)
control.layoutFormatterManager.forceFormatPage(control.layout.validPageCount);
}
static checkLayoutPage(layout, page) {
const pagePos = page.getPosition();
let collectedIntervals = [];
const pageAreas = page.mainSubDocumentPageAreas;
if (pageAreas[0].pageOffset != 0)
console.log(page.index, page, "First page area offset != 0");
for (let pageAreaIndex = 0, pageArea; pageArea = pageAreas[pageAreaIndex]; pageAreaIndex++) {
const pageAreaPos = pagePos + pageArea.pageOffset;
const columns = pageArea.columns;
if (columns[0].pageAreaOffset != 0)
console.log(page.index, page, pageAreaIndex, pageArea, "First column offset != 0");
for (let columnIndex = 0, column; column = columns[columnIndex]; columnIndex++) {
const columnPos = pageAreaPos + column.pageAreaOffset;
const rows = column.rows;
if (rows[0].columnOffset != 0)
console.log(page.index, page, pageAreaIndex, pageArea, columnIndex, column, "First row offset != 0");
for (let rowIndex = 0, row; row = rows[rowIndex]; rowIndex++) {
const rowPos = columnPos + row.columnOffset;
collectedIntervals.push(new FixedInterval(rowPos, row.getLastBoxEndPositionInRow()));
const boxes = row.boxes;
if (boxes[0].rowOffset != 0)
console.log(page.index, page, pageAreaIndex, pageArea, columnIndex, column, row, rowIndex, "First box offset != 0");
}
}
}
let lastEndPos = 0;
for (let interval of collectedIntervals) {
if (interval.start < lastEndPos)
console.log("Intervals of row not sorted", page, collectedIntervals);
lastEndPos = interval.end;
}
const mergedIntervals = IntervalAlgorithms.getMergedIntervals(collectedIntervals, true);
let pageIntervalIndex = 0;
page.calculateContentIntervals(layout.anchorObjectsPositionInfo, false);
const pageIntervals = page.getContentIntervals();
let currPageInterval = pageIntervals[pageIntervalIndex];
for (let mergedIntervalIndex = 0, mgInt; mgInt = mergedIntervals[mergedIntervalIndex]; mergedIntervalIndex++) {
if (!currPageInterval.containsInterval(mgInt)) {
pageIntervalIndex++;
currPageInterval = pageIntervals[pageIntervalIndex];
if (!currPageInterval || !currPageInterval.containsInterval(mgInt))
console.log("current page contentIntervals not consider some row intervals", [mgInt.start, mgInt.end], mergedIntervals, pageIntervals, collectedIntervals, page);
}
}
}
static checkLayout(_model, layout) {
const pages = layout.pages;
for (let pageIndex = 0, page; page = pages[pageIndex]; pageIndex++) {
TEST_CLASS.checkLayoutPage(layout, page);
}
}
static checkModel(model) {
NumberMapUtils.forEach(model.subDocuments, (subDocument, sid) => {
let prevParagraphEnd = 0;
for (let pIndex = 0, paragraph; paragraph = subDocument.paragraphs[pIndex]; pIndex++) {
if (paragraph.startLogPosition.value !== prevParagraphEnd) {
console.log(`paragraphs[${pIndex}].length !== prevParagraphEnd`);
}
prevParagraphEnd = paragraph.getEndPosition();
if (paragraph.length === 0) {
console.log(`paragraphs[${pIndex}].length == 0`);
continue;
}
let endParRun = subDocument.getRunByPosition(paragraph.getEndPosition() - 1);
if (endParRun.getType() !== RunType.ParagraphRun) {
console.log(`The last run of paragraph ${pIndex} is not ParagraphRun`);
continue;
}
}
if (subDocument.paragraphs[subDocument.paragraphs.length - 1].getEndPosition() !== subDocument.getLastChunk().getEndPosition())
console.log(`paragraphs.length !== chunks.length in sid=${sid}`);
let prevTableStartPosition = -1;
for (let tIndex = 0, table; table = subDocument.tables[tIndex]; tIndex++) {
if (table.getStartPosition() < prevTableStartPosition)
console.log(`tables are not sorted. tables[${tIndex}].getStartPosition() < prevTableStartPosition in sid=${sid}`);
prevTableStartPosition = table.getStartPosition();
if (table.index !== tIndex)
console.log(`subDocument.tables[${tIndex}] !== subDocument.tables[${tIndex}].index`);
if (table.nestedLevel == 0 && table.parentCell)
console.log(`subDocument.tables[${tIndex}].parentCell exists but nestedLevel===0`);
else if (table.nestedLevel > 0 && !table.parentCell)
console.log(`subDocument.tables[${tIndex}].parentCell doesn't exist but nestedLevel>0`);
else if (table.parentCell && table.parentCell.parentRow.parentTable.index >= table.index)
console.log(`subDocument.tables[${tIndex}].parentCell.parentRow.parentTable.index >= table.index`);
let prevRowEndPosition = -1;
let prevColumnsCount = -1;
for (let rIndex = 0, row; row = table.rows[rIndex]; rIndex++) {
let currentColumnsCount = row.gridAfter + row.gridBefore;
if (row.cells.length === 0)
console.log(`tables[${tIndex}].rows.length === 0`);
if (prevRowEndPosition >= 0 && prevRowEndPosition !== row.getStartPosition())
console.log(`tables[${tIndex}].rows[${rIndex}].getStartPosition() != prevRowEndPosition`);
if (row.parentTable !== table)
console.log(`tables[${tIndex}].rows[${rIndex}].parentTable != table`);
prevRowEndPosition = row.getEndPosition();
for (let cIndex = 0, cell; cell = row.cells[cIndex]; cIndex++) {
currentColumnsCount += cell.columnSpan;
if (cell.startParagraphPosition.value >= cell.endParagrapPosition.value)
console.log(`tables[${tIndex}].rows[${rIndex}].cells[${cIndex}].startParagraphPosition.value >= cell.endParagrapPosition.value`);
let startParagraph = subDocument.getParagraphByPosition(cell.startParagraphPosition.value);
let endParagraph = subDocument.getParagraphByPosition(cell.endParagrapPosition.value - 1);
if (cell.startParagraphPosition.value !== startParagraph.startLogPosition.value)
console.log(`tables[${tIndex}].rows[${rIndex}].cells[${cIndex}] doesn't start with paragraph`);
if (cell.endParagrapPosition.value !== endParagraph.getEndPosition())
console.log(`tables[${tIndex}].rows[${rIndex}].cells[${cIndex}] doesn't end with paragraph`);
}
if (rIndex > 0 && currentColumnsCount !== prevColumnsCount)
console.log(`tables[${tIndex}].rows[${rIndex}].columnsCount(${currentColumnsCount}) != prevColumnsCount(${prevColumnsCount})`);
prevColumnsCount = currentColumnsCount;
}
}
});
}
static getPlaceActualTableProperties(table, prefix = "") {
const prefix_1 = `${prefix}\t`;
const prefix_2 = `${prefix_1}\t`;
const prefix_3 = `${prefix_2}\t`;
const prefix_4 = `${prefix_3}\t`;
const prefix_5 = `${prefix_4}\t`;
let cellHandler = (cell, cellIndex) => `${prefix_3}cell[${cellIndex}]\n${LogObjToStr.tableCellMembers(cell, prefix_4)}\n${prefix_4}cellProperties\n${LogObjToStr.tableCellProperties(cell.properties, prefix_5)}`;
let rowHandler = (row, rowIndex) => `${prefix_1}row[${rowIndex}]:\n${LogObjToStr.tableRowMembers(row, prefix_2)}\n${prefix_2}rowProperties:\n${LogObjToStr.tableRowProperties(row.properties, prefix_3)}\n${prefix_2}cells:\n${Log.join("\n", Log.map(cellHandler, row.cells))}`;
const result = [];
result.push(`${Log.TRIPLE_SOLID_BLOCK}\n${LogObjToStr.tableMembers(table, prefix)}`);
result.push(`${prefix}tableProperties\n${LogObjToStr.tableProperties(table.properties, prefix_1)}`);
result.push(`${prefix}rows\n${Log.join("\n", Log.map(rowHandler, table.rows))}`);
result.push(`${prefix}styles\n${LogObjToStr.tableStyle(table.style, prefix_1)}`);
console.log(result.join("\n"));
}
static getTableStructure(subDocument, index) {
let result = "";
let table = subDocument.tables[index];
result += `<(${index})`;
for (let rowIndex = 0, row; row = table.rows[rowIndex]; rowIndex++) {
if (rowIndex > 0)
result += "\n";
result += "\t";
result += StringUtils.repeat("→ ", row.gridBefore);
for (let cellIndex = 0, cell; cell = row.cells[cellIndex]; cellIndex++) {
result += ` [${cell.startParagraphPosition.value} `;
if (cell.columnSpan > 1)
result += Array(cell.columnSpan).join("→");
if (cell.verticalMerging === TableCellMergingState.Restart)
result += "↓";
if (cell.verticalMerging === TableCellMergingState.Continue)
result += "↑";
for (let ind = index + 1, nextTable; nextTable = subDocument.tables[ind]; ind++) {
if (nextTable.nestedLevel == table.nestedLevel + 1 &&
cell.interval.containsInterval(nextTable.interval))
result += `\n\t${this.getTableStructure(subDocument, ind)}\n`;
}
result += ` ${cell.endParagrapPosition.value}] `;
}
result += StringUtils.repeat("← ", row.gridAfter);
}
result += ">";
return result;
}
static getTableIndexesInfoByLayoutCell(cellInfo) {
const tti = new TEST_TABLE_INFO();
const rowInfo = cellInfo.parentRow;
const tableInfo = rowInfo.parentTable;
const logicInfo = tableInfo.logicInfo.grid;
tti.modelTableIndex = logicInfo.table.index;
tti.nestedLevel = logicInfo.table.nestedLevel;
tti.layoutRowIndex = tableInfo.tableRows.indexOf(rowInfo);
tti.layoutCellIndex = rowInfo.rowCells.indexOf(cellInfo);
return tti;
}
static anchorInfoToString(ancInfo) {
const result = [];
if (ancInfo.isUsedHorizontalAbsolutePosition())
result.push(`Horiz ABS pos ${Log.mask(AnchorObjectHorizontalPositionType, ancInfo.horizontalPositionType)} ${UnitConverter.twipsToPixels(ancInfo.offset.x)}px`);
if (ancInfo.isUsedHorizontalAlignment() || ancInfo.isUsedHorizontalBookLayout()) {
result.push(ancInfo.isUsedHorizontalAlignment() ? "Horiz ALIGNMENT " : "Horiz BookLayout ");
result.push(Log.mask(AnchorObjectHorizontalPositionType, ancInfo.horizontalPositionType));
result.push(" ");
result.push(Log.mask(AnchorObjectHorizontalPositionAlignment, ancInfo.horizontalPositionAlignment));
}
if (ancInfo.isUsedHorizontalRelativePosition())
result.push(`Horiz RELATIVE pos ${ancInfo.percentOffset.x / AnchorInfo.RELATIVE_COEFF}%`);
if (ancInfo.isUsedVerticalAbsolutePosition())
result.push(`\nVert ABS pos ${Log.mask(AnchorObjectVerticalPositionType, ancInfo.verticalPositionType)} ${UnitConverter.twipsToPixels(ancInfo.offset.y)}px`);
if (ancInfo.isUsedVerticalAlignment()) {
result.push("\nVert ALIGNMENT pos");
result.push(Log.mask(AnchorObjectVerticalPositionType, ancInfo.verticalPositionType));
result.push(" ");
result.push(Log.mask(AnchorObjectVerticalPositionAlignment, ancInfo.verticalPositionAlignment));
}
if (ancInfo.isUsedVerticalRelativePosition())
result.push(`\nVert RELATIVE pos ${ancInfo.percentOffset.y / AnchorInfo.RELATIVE_COEFF}%`);
result.push(`\nDistanceFromText\n`);
result.push(`\t Top: ${UnitConverter.twipsToPixels(ancInfo.topDistance)}px \n`);
result.push(`\t Bottom: ${UnitConverter.twipsToPixels(ancInfo.bottomDistance)}px \n`);
result.push(`\t Left: ${UnitConverter.twipsToPixels(ancInfo.leftDistance)}px \n`);
result.push(`\t Right: ${UnitConverter.twipsToPixels(ancInfo.rightDistance)}px \n`);
return result.join("");
}
static selectionInfo(selection) {
const res = [];
ListUtils.forEach(selection.intervals, (curr) => res.push(LogObjToStr.fixedInterval(curr)));
res.push(`pageIndex=${selection.pageIndex}`);
res.push(`activeSubDoc=${LogObjToStr.subDocumentInfoBase(selection.activeSubDocument.info, "", " ")}`);
if (selection.specialRunInfo.isSelected()) {
if (selection.specialRunInfo.isPictureSelected()) {
res.push(selection.specialRunInfo.isSelectedAnchorObject ? "anchored picture" : "inline picture");
res.push(`pos=${selection.specialRunInfo.getPicturePosition()}`);
}
else {
res.push(selection.specialRunInfo.isSelectedAnchorObject ? "anchored text box" : "inline text box");
res.push(`pos=${selection.specialRunInfo.getTextBoxPosition()}`);
res.push(`internalSubDocId=${selection.specialRunInfo.textBoxInnerSubDocumentId}`);
}
res.push(`parentSubDoc=${LogObjToStr.subDocumentInfoBase(selection.specialRunInfo.getParentSubDocument().info, "", " ")}`);
}
return res.join("\n");
}
}
export class TEST_CLASS_PROPS_ANALYZE {
static analyzeContainer(container, getPropFromContainer, enumObject, enumProperty) {
return container.getUseValue(enumProperty) ?
`Consider property ${enumObject[enumProperty]} with value ${getPropFromContainer(container)}` :
"";
}
static analyzeTableConditionalStyles(tableStyle, getContainerFromCondStyle, getValue, enumObject, enumProperty) {
const result = [];
EnumUtils.forEach(ConditionalTableStyleFormatting, (enumKey) => {
const condStyle = tableStyle.conditionalStyles[enumKey];
if (condStyle)
result.push(`ConditionalStyle ${ConditionalTableStyleFormatting[enumKey]}: ${TEST_CLASS_PROPS_ANALYZE.analyzeContainer(getContainerFromCondStyle(condStyle), getValue, enumObject, enumProperty)}`);
});
if (tableStyle.baseConditionalStyle)
result.push(`ConditionalStyle BASE: ${TEST_CLASS_PROPS_ANALYZE.analyzeContainer(getContainerFromCondStyle(tableStyle.baseConditionalStyle), getValue, enumObject, enumProperty)}`);
return result.length ? result.join("\n") : "Dont consider that property";
}
static analyzeTableConditionalStyleByCharacterPropertyFontSize(tableStyle) {
return TEST_CLASS_PROPS_ANALYZE.analyzeTableConditionalStyles(tableStyle, (tblCondStyle) => tblCondStyle.maskedCharacterProperties, (c) => c.fontSize, CharacterPropertiesMask, CharacterPropertiesMask.UseDoubleFontSize);
}
static analyzeTableConditionalStyleByCharacterPropertyColor(tableStyle) {
return TEST_CLASS_PROPS_ANALYZE.analyzeTableConditionalStyles(tableStyle, (tblCondStyle) => tblCondStyle.maskedCharacterProperties, (c) => c.textColor, CharacterPropertiesMask, CharacterPropertiesMask.UseForeColorIndex);
}
}
export class BOX_ITERATOR_VISUALIZER {
constructor(control) {
this.canRunNext = true;
this.canRunPrev = true;
this.control = control;
this.reset();
}
reset() {
this.it = new LayoutBoxIteratorMainSubDocument(this.control.selection.activeSubDocument, this.control.layout, this.control.selection.lastSelectedInterval.start, this.control.selection.lastSelectedInterval.end);
}
next() {
if (this.canRunNext) {
if (this.it.moveNext(new LayoutPositionCreatorConflictFlags().setDefault(false), new LayoutPositionCreatorConflictFlags().setDefault(false))) {
const posStart = this.it.position.getLogPosition(DocumentLayoutDetailsLevel.Character);
const posEnd = this.it.position.getLogPosition(DocumentLayoutDetailsLevel.Box) + this.it.position.box.getLength();
this.control.selection.deprecatedSetSelection(posStart, posEnd, false, -1, true, true, true, ModelScrollManager.StandartScrollPosition);
this.canRunPrev = true;
return true;
}
else {
this.canRunNext = false;
return false;
}
}
return false;
}
prev() {
if (this.canRunPrev) {
if (this.it.movePrev(new LayoutPositionCreatorConflictFlags().setDefault(false), new LayoutPositionCreatorConflictFlags().setDefault(false))) {
const posStart = this.it.position.getLogPosition(DocumentLayoutDetailsLevel.Character);
const posEnd = this.it.position.getLogPosition(DocumentLayoutDetailsLevel.Box) + this.it.position.box.getLength();
const docEndPos = this.control.selection.activeSubDocument.getDocumentEndPosition();
if (posStart == posEnd && posStart == docEndPos)
this.control.selection.deprecatedSetSelection(docEndPos - 1, docEndPos, false, -1, true, true, true, ModelScrollManager.StandartScrollPosition);
else
this.control.selection.deprecatedSetSelection(posStart, posEnd, false, -1, true, true, true, ModelScrollManager.StandartScrollPosition);
this.canRunNext = true;
return true;
}
else {
this.canRunPrev = false;
return false;
}
}
return false;
}
}
export class TEST_TABLE_INFO {
toString() {
return `modelTableIndex: ${this.modelTableIndex}, nestedLevel: ${this.nestedLevel}, layoutRowIndex: ${this.layoutRowIndex}, layoutCellIndex: ${this.layoutCellIndex}`;
}
}