UNPKG

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
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}`; } }