UNPKG

devexpress-richedit

Version:

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

302 lines (301 loc) 18.1 kB
import { UnitConverter } from '@devexpress/utils/lib/class/unit-converter'; import { Constants } from '@devexpress/utils/lib/constants'; import { FixedInterval } from '@devexpress/utils/lib/intervals/fixed'; import { isEven } from '@devexpress/utils/lib/utils/common'; import { ListUtils } from '@devexpress/utils/lib/utils/list'; import { DocumentLayoutDetailsLevel } from '../../layout/document-layout-details-level'; import { LayoutPosition } from '../../layout/layout-position'; import { LayoutPage, LayoutPageFlags } from '../../layout/main-structures/layout-page'; import { LayoutRowStateFlags } from '../../layout/main-structures/layout-row'; import { LayoutTableColumnInfo } from '../../layout/table/layout-table-info'; import { SectionStartType } from '../../model/section/enums'; import { Section } from '../../model/section/section'; import { SectionProperties } from '../../model/section/section-properties'; import { SubDocument } from '../../model/sub-document'; import { Log } from '../../rich-utils/debug/logger/base-logger/log'; import { LogListHelper } from '../../rich-utils/debug/logger/base-logger/log-list-helper'; import { LogObjToStr } from '../../rich-utils/debug/logger/base-logger/log-obj-to-str'; import { LogSource } from '../../rich-utils/debug/logger/base-logger/log-source'; import { LogObjToStrLayout } from '../../rich-utils/debug/logger/layout-logger/log-obj-to-str-layout'; import { PageChangesCollector } from '../changes/engine/page-changes-collector'; import { BaseFormatter } from './base-formatter'; import { LayoutFormatterState } from './enums'; var TryReusePageResult; (function (TryReusePageResult) { TryReusePageResult[TryReusePageResult["CreateNewPage"] = 1] = "CreateNewPage"; TryReusePageResult[TryReusePageResult["UseOldPage"] = 2] = "UseOldPage"; TryReusePageResult[TryReusePageResult["RestartFromTableStartPosition"] = 3] = "RestartFromTableStartPosition"; })(TryReusePageResult || (TryReusePageResult = {})); export class MainFormatter extends BaseFormatter { constructor(formatterManager) { super(formatterManager, SubDocument.MAIN_SUBDOCUMENT_ID); this.state = LayoutFormatterState.DocumentStart; this.stateMap[LayoutFormatterState.DocumentStart] = this.processStateDocumentStart; this.stateMap[LayoutFormatterState.PageStart] = this.processStatePageStart; this.stateMap[LayoutFormatterState.PageAreaEnd] = this.processStatePageAreaEnd; this.stateMap[LayoutFormatterState.PageEnd] = this.processStatePageEnd; this.stateMap[LayoutFormatterState.DocumentEnd] = this.processStateDocumentEnd; this.stateMap[LayoutFormatterState.End] = this.processStateEnd; } formatNext() { do { if (!this.stateMap[this.state].call(this)) return false; } while (MainFormatter.notAllowBreakOnState[this.state](this.layoutPosition)); return true; } processStateDocumentStart() { this.manager.activeFormatter = this; this.initDocumentStart(); this.layoutPosition = new LayoutPosition(DocumentLayoutDetailsLevel.None); this.layoutPosition.pageIndex = 0; this.state = LayoutFormatterState.PageStart; return true; } processStatePageStart() { this.manager.activeFormatter = this; const wrap = this.rowFormatter.iterator.getWrap(false); if (!wrap) return false; Log.print(LogSource.LayoutFormatter, "processStatePageStart", () => `SubDocId: ${this.subDocument.id}, LayPos: ${LogObjToStrLayout.layoutPositionShort(this.layoutPosition)}`); const pages = this.manager.layout.pages; const boundsCalculator = this.manager.boundsCalculator; const prevPage = pages[this.layoutPosition.pageIndex - 1]; const layoutPageIndex = prevPage ? prevPage.layoutPageIndex + 1 : 0; boundsCalculator.initWhenPageStart(layoutPageIndex); const prevPageSection = !prevPage ? null : this.manager.model.getSectionByPosition(prevPage.getPosition()); const currentPageSectionIndex = wrap.info.sectionIndex; const currentPageSection = this.manager.model.sections[currentPageSectionIndex]; const isFirstPageOfSection = prevPageSection != currentPageSection; const isMirrorMargins = this.manager.model.mirrorMargins; if (this.manager.innerClientProperties.viewsSettings.isSimpleView) { const widthForSimpleView = this.manager.innerClientProperties.viewsSettings.widthOfPage; const section = new Section(currentPageSection.documentModel, currentPageSection.startLogPosition, currentPageSection.getLength(), SectionProperties.createSimpleSectionProperties(widthForSimpleView, Constants.MAX_SAFE_INTEGER)); section.sectionProperties.margins.copyFrom(this.manager.innerClientProperties.viewsSettings.paddings); section.sectionProperties.margins.applyConverter(UnitConverter.pixelsToTwips); boundsCalculator.init(section); boundsCalculator.setHeaderBounds(0); boundsCalculator.setFooterBounds(0); } else if (isFirstPageOfSection || isMirrorMargins) boundsCalculator.init(currentPageSection); boundsCalculator.calculatePageBounds(prevPage ? prevPage.bottom : 0); const tryReuseResult = this.manager.floatingRestartInfoHolder.isRestartByAnchorObjects ? [TryReusePageResult.CreateNewPage] : this.tryReusePage(boundsCalculator.pageBounds); switch (tryReuseResult[0]) { case TryReusePageResult.CreateNewPage: this.createNextPage(boundsCalculator.pageBounds); this.manager.floatingRestartInfoHolder.setCalculatedObjects(this.layoutPosition); break; case TryReusePageResult.UseOldPage: break; case TryReusePageResult.RestartFromTableStartPosition: this.manager.invalidator.onIntervalChanged(this.subDocument.id, new FixedInterval(tryReuseResult[1], 1)); return true; } const pageIndex = this.layoutPosition.pageIndex; const actualPage = this.layoutPosition.page; actualPage.flags.set(LayoutPageFlags.IsFirstPageOfSection, isFirstPageOfSection); actualPage.index = pageIndex; actualPage.layoutPageIndex = layoutPageIndex; actualPage.pageOrdinal = this.calculatePageOrdinal(prevPage, isFirstPageOfSection, currentPageSection); actualPage.startPageSectionIndex = currentPageSectionIndex; const isEvenPage = isEven(layoutPageIndex); if (prevPage && isFirstPageOfSection && ((currentPageSection.sectionProperties.startType === SectionStartType.EvenPage && !isEvenPage) || (currentPageSection.sectionProperties.startType === SectionStartType.OddPage && isEvenPage))) actualPage.layoutPageIndex++; if (!this.manager.innerClientProperties.viewsSettings.isSimpleView) { this.formatOtherHeaderFooterPageArea(currentPageSection.headers, (obj) => this.manager.otherPageAreaFormatter.formatHeaderPageArea(actualPage, obj)); if (this.manager.floatingRestartInfoHolder.isRestartByAnchorObjects) return true; this.formatOtherHeaderFooterPageArea(currentPageSection.footers, (obj) => this.manager.otherPageAreaFormatter.formatFooterPageArea(actualPage, obj)); if (this.manager.floatingRestartInfoHolder.isRestartByAnchorObjects) return true; } boundsCalculator.calculateMainPageAreaBounds(-1); boundsCalculator.calculateColumnBounds(ListUtils.last(boundsCalculator.mainPageAreasBounds)); this.pageAreaBounds = ListUtils.last(boundsCalculator.mainPageAreasBounds); this.columnBounds = ListUtils.last(boundsCalculator.mainColumnsBounds); return true; } calculatePageOrdinal(prevPage, firstPageOfSection, currentSection) { var _a; const nextPageOrdinalType = currentSection.sectionProperties.startType; let pageOrdinal; if (firstPageOfSection && !currentSection.sectionProperties.continueNumbering) pageOrdinal = currentSection.sectionProperties.firstPageNumber; else pageOrdinal = ((_a = prevPage === null || prevPage === void 0 ? void 0 : prevPage.pageOrdinal) !== null && _a !== void 0 ? _a : 0) + 1; const isPageOrdinalOdd = ((pageOrdinal) % 2) != 0; switch (nextPageOrdinalType) { case SectionStartType.OddPage: if (isPageOrdinalOdd) return pageOrdinal; else return pageOrdinal + 1; case SectionStartType.EvenPage: if (!isPageOrdinalOdd) return pageOrdinal; else return pageOrdinal + 1; default: case SectionStartType.Continuous: return pageOrdinal; } } processStatePageAreaEnd() { this.manager.activeFormatter = this; BaseFormatter.correctColumnOffsets(this.layoutPosition.pageArea); Log.print(LogSource.LayoutFormatter, "processStatePageAreaEnd", () => `SubDocId: ${this.subDocument.id}, LayPos: ${LogObjToStrLayout.layoutPositionShort(this.layoutPosition)}`); this.layoutPosition.detailsLevel = DocumentLayoutDetailsLevel.Page; this.state = LayoutFormatterState.PageEnd; return true; } processStatePageEnd() { this.manager.activeFormatter = this; const layout = this.manager.layout; Log.print(LogSource.LayoutFormatter, "processStatePageEnd", () => `SubDocId: ${this.subDocument.id}, LayPos: ${LogObjToStrLayout.layoutPositionShort(this.layoutPosition)}`); const createdPage = this.layoutPosition.page; createdPage.anchoredObjectHolder.removeOrphanedObjects(this); createdPage.isValid = true; MainFormatter.correctPageOffsets(createdPage); this.state = this.lastRowInfo.row.flags.get(LayoutRowStateFlags.DocumentEnd) ? LayoutFormatterState.DocumentEnd : LayoutFormatterState.PageStart; if (!this.tableFormatter) { layout.validPageCount = this.layoutPosition.pageIndex + 1; layout.lastMaxNumPages = Math.max(layout.lastMaxNumPages, createdPage.layoutPageIndex + 1); } createdPage.calculateContentIntervals(this.manager.layout.anchorObjectsPositionInfo, false); createdPage.setRenderLevelCalculator(this.manager.layout.anchorObjectsPositionInfo, this.manager.model.compatibilitySettings.compatibilityMode); const pageChange = PageChangesCollector.collectPageChanges(layout.pages, createdPage); this.manager.changesManager.addPageChange(pageChange); this.copyFlagsFromOldPage(layout.pages[createdPage.index], createdPage); layout.pages[createdPage.index] = createdPage; const tblCellInfo = ListUtils.last(createdPage.mainSubDocumentPageAreas[0].columns[0].rows).tableCellInfo; if (!tblCellInfo || tblCellInfo.parentRow.rowIndex == tblCellInfo.parentRow.parentTable.logicInfo.grid.table.rows.length - 1 && !tblCellInfo.parentRow.parentTable.isBoundWithNext()) this.manager.onPagesReady(); this.layoutPosition.detailsLevel = DocumentLayoutDetailsLevel.None; this.layoutPosition.pageIndex++; this.layoutPosition.page = null; return true; } processStateDocumentEnd() { this.manager.activeFormatter = this; Log.print(LogSource.LayoutFormatter, "processStateDocumentEnd", () => `SubDocId: ${this.subDocument.id}, LayPos: ${LogObjToStrLayout.layoutPositionShort(this.layoutPosition)}`); const layout = this.manager.layout; this.manager.removeRedundantHelper.removeRedundantPage(layout, layout.validPageCount); layout.isFullyFormatted = true; layout.lastMaxNumPages = ListUtils.last(this.manager.layout.pages).layoutPageIndex + 1; this.manager.layoutDependentRunCache.recalculateHeaderFooterPageAreas(); this.manager.onPagesReady(); Log.print(LogSource.LayoutFormatter, "processStateDocumentEnd(end)\n", Log.w(3, Log.join)("\n")(Log.map((page, index) => `\tPage[${index}]Intervals: ${LogListHelper.level_1(LogObjToStr.fixedInterval, page.getContentIntervals(), "\t\t", "\t")()}`, this.manager.layout.pages))); this.state = LayoutFormatterState.End; this.manager.anchoredObjectsManager.reset(); return false; } processStateEnd() { return false; } copyFlagsFromOldPage(oldPage, createdPage) { if (oldPage) { const oldFlags = oldPage.flags.clone(); oldFlags.set(LayoutPageFlags.IsFirstPageOfSection, createdPage.flags.get(LayoutPageFlags.IsFirstPageOfSection)); oldFlags.set(LayoutPageFlags.IsIntervalsCorrect, createdPage.flags.get(LayoutPageFlags.IsIntervalsCorrect)); createdPage.flags = oldFlags; } } createNextPage(pageBounds) { const newPage = new LayoutPage(); newPage.setGeomerty(pageBounds); newPage.setAbsolutePosition(this.rowFormatter.getPosition()); this.state = LayoutFormatterState.PageAreaStart; this.layoutPosition.page = newPage; this.layoutPosition.pageAreaIndex = 0; this.layoutPosition.pageArea = null; this.layoutPosition.detailsLevel = DocumentLayoutDetailsLevel.Page; } tryReusePage(pageBounds) { const pages = this.manager.layout.pages; const page = pages[this.layoutPosition.pageIndex]; if (!page || !page.isValid || this.tableFormatter || !page.equals(pageBounds) || page.getPosition() != this.rowFormatter.getPosition()) return [TryReusePageResult.CreateNewPage]; this.layoutPosition.pageAreaIndex = page.mainSubDocumentPageAreas.length - 1; const pageArea = ListUtils.last(page.mainSubDocumentPageAreas); const column = pageArea.getLastColumn(); this.lastRowInfo.setRowInfo(column.getLastRow(), page.getPosition() + pageArea.pageOffset + column.pageAreaOffset, false); const firstBoundWithNextCellOnPage = LayoutTableColumnInfo.getFirstCellInPageThatBoundWithCellPlacedInNextPage(column.tablesInfo); const nextBoxIteratorPosition = firstBoundWithNextCellOnPage ? page.getPosition() + pageArea.pageOffset + column.pageAreaOffset + firstBoundWithNextCellOnPage.getEndPosition() : page.getEndPosition(); const nextPage = this.manager.layout.pages[page.index + 1]; const tableColumn = this.getLastWidowTableColumn(column); if (tableColumn && (!nextPage || !nextPage.isValid || nextBoxIteratorPosition != nextPage.getPosition())) { return [TryReusePageResult.RestartFromTableStartPosition, this.getTableRestartPosition(tableColumn)]; } this.rowFormatter.setPosition(nextBoxIteratorPosition, false, !this.tableFormatter); this.state = LayoutFormatterState.PageEnd; this.layoutPosition.detailsLevel = DocumentLayoutDetailsLevel.Page; this.layoutPosition.page = page.deepCopy(); return [TryReusePageResult.UseOldPage]; } getTableRestartPosition(tableColumn) { return tableColumn.getTopLevelColumn().logicInfo.grid.table.interval.start; } getLastWidowTableColumn(column) { if (!column.tablesInfo[0]) return null; const index = this.getWidowTableLastColumnIndex(column); return column.tablesInfo[index]; } getWidowTableLastColumnIndex(column) { const lastTable = column.tablesInfo[0].logicInfo.grid.table; for (let i = 1; i < column.tablesInfo.length; i++) { if (lastTable != column.tablesInfo[i].logicInfo.grid.table) return i - 1; } return column.tablesInfo.length - 1; } formatOtherHeaderFooterPageArea(container, format) { const isEvenPage = isEven(this.layoutPosition.page.layoutPageIndex); const isFirstPageOfSection = this.layoutPosition.page.flags.get(LayoutPageFlags.IsFirstPageOfSection); const actualObject = container.getActualObject(isFirstPageOfSection, isEvenPage); format(actualObject); } setParamsForRestart(state, newLp, newLastRowInfo, resetTableFormatter) { this.state = state; this.layoutPosition = newLp; this.lastRowInfo = newLastRowInfo; if (resetTableFormatter) this.tableFormatter = null; } setBoundsForRestart(pageAreaBounds, columnBounds) { this.pageAreaBounds = pageAreaBounds; this.columnBounds = columnBounds; } static correctPageOffsets(page) { const pageAreas = page.mainSubDocumentPageAreas; if (!pageAreas.length) return; const offsetFirstPageAreaFromPage = pageAreas[0].pageOffset; if (offsetFirstPageAreaFromPage != 0) { page.setAbsolutePosition(page.getPosition() + offsetFirstPageAreaFromPage); for (let pageArea of pageAreas) pageArea.pageOffset -= offsetFirstPageAreaFromPage; } } } MainFormatter.notAllowBreakOnState = { [LayoutFormatterState.DocumentStart]: (_lp) => true, [LayoutFormatterState.PageStart]: (lp) => lp.pageIndex == 0, [LayoutFormatterState.PageAreaStart]: (lp) => lp.pageAreaIndex == 0, [LayoutFormatterState.ColumnStart]: (lp) => lp.columnIndex == 0, [LayoutFormatterState.RowFormatting]: (lp) => lp.rowIndex == 0, [LayoutFormatterState.DocumentEnd]: (_lp) => false, [LayoutFormatterState.PageEnd]: (_lp) => false, [LayoutFormatterState.PageAreaEnd]: (_lp) => false, [LayoutFormatterState.ColumnEnd]: (_lp) => false, };