devexpress-richedit
Version:
DevExpress Rich Text Editor is an advanced word-processing tool designed for working with rich text documents.
164 lines (163 loc) • 10.2 kB
JavaScript
import { UnitConverter } from '@devexpress/utils/lib/class/unit-converter';
import { Errors } from '@devexpress/utils/lib/errors';
import { FixedInterval } from '@devexpress/utils/lib/intervals/fixed';
import { LayoutBox, LayoutBoxType } from '../../layout/main-structures/layout-boxes/layout-box';
import { LayoutRowStateFlags } from '../../layout/main-structures/layout-row';
import { SubDocumentNumberingListCountersManager } from '../../model/numbering-lists/piece-table-numbering-list-counters-manager';
import { Log } from '../../rich-utils/debug/logger/base-logger/log';
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 { BoxIterator } from '../box/box-iterator';
import { BoxWrap } from '../box/box-wrap';
import { RowFormatterResult, RowFormatterResultFlag } from './result';
import { RowSizesManager } from './size-engine/row-sizes-manager';
import { RowBaseFormatterState, RowEndedWithPageBreakState, RowEndedWithParagraphMarkFormatterState } from './states';
import { RowTabInfo } from './tab-info';
import { WordHolderInfo } from './word-holder';
export var TextRowFormatterState;
(function (TextRowFormatterState) {
TextRowFormatterState[TextRowFormatterState["None"] = 0] = "None";
TextRowFormatterState[TextRowFormatterState["Base"] = 1] = "Base";
TextRowFormatterState[TextRowFormatterState["EndedWithPageBreak"] = 2] = "EndedWithPageBreak";
TextRowFormatterState[TextRowFormatterState["EndedWithParagraphMark"] = 3] = "EndedWithParagraphMark";
})(TextRowFormatterState || (TextRowFormatterState = {}));
export class RowFormatter {
constructor(formatterManager, subDocumentId) {
this.rowFormatting = true;
this.manager = formatterManager;
this.iterator = new BoxIterator(this.manager, subDocumentId);
this.numberingListCountersManager = new SubDocumentNumberingListCountersManager(this.subDocument);
this.stateMap = {};
this.stateMap[TextRowFormatterState.Base] = new RowBaseFormatterState(this);
this.stateMap[TextRowFormatterState.EndedWithPageBreak] = new RowEndedWithPageBreakState(this);
this.stateMap[TextRowFormatterState.EndedWithParagraphMark] = new RowEndedWithParagraphMarkFormatterState(this);
if (RowFormatter.addBoxFunctionMap)
return;
RowFormatter.addBoxFunctionMap = {};
RowFormatter.addBoxFunctionMap[LayoutBoxType.Space] = function () { this.currentState.addSpaceBox(); };
RowFormatter.addBoxFunctionMap[LayoutBoxType.NonBreakingSpace] = function () { this.currentState.addTextBox(); };
RowFormatter.addBoxFunctionMap[LayoutBoxType.Dash] = function () { this.currentState.addDashBox(); };
RowFormatter.addBoxFunctionMap[LayoutBoxType.Text] = function () { this.currentState.addTextBox(); };
RowFormatter.addBoxFunctionMap[LayoutBoxType.Picture] = function () { this.currentState.addPictureBox(); };
RowFormatter.addBoxFunctionMap[LayoutBoxType.ParagraphMark] = function () { this.currentState.addParagraphBox(); };
RowFormatter.addBoxFunctionMap[LayoutBoxType.LineBreak] = function () { this.currentState.addLineBreakBox(); };
RowFormatter.addBoxFunctionMap[LayoutBoxType.SectionMark] = function () { this.currentState.addSectionBox(); };
RowFormatter.addBoxFunctionMap[LayoutBoxType.PageBreak] = function () { this.currentState.addPageBreakBox(); };
RowFormatter.addBoxFunctionMap[LayoutBoxType.ColumnBreak] = function () { this.currentState.addColumnBreakBox(); };
RowFormatter.addBoxFunctionMap[LayoutBoxType.TabSpace] = function () { this.currentState.addTabulationBox(); };
RowFormatter.addBoxFunctionMap[LayoutBoxType.FieldCodeStart] = function () { this.currentState.addTextBox(); };
RowFormatter.addBoxFunctionMap[LayoutBoxType.FieldCodeEnd] = function () { this.currentState.addTextBox(); };
RowFormatter.addBoxFunctionMap[LayoutBoxType.LayoutDependent] = function () { this.currentState.addTextBox(); };
RowFormatter.addBoxFunctionMap[LayoutBoxType.AnchorTextBox] = function () { this.currentState.addAnchorObject(); };
RowFormatter.addBoxFunctionMap[LayoutBoxType.AnchorPicture] = function () { this.currentState.addAnchorObject(); };
}
get subDocument() { return this.iterator.subDocument; }
;
get row() { return this.result.row; }
get paragraph() { return this.subDocument.paragraphs[this.result.paragraphIndex]; }
get paragraphProps() { return this.paragraph.getParagraphMergedProperties(); }
getNextBoxWrapInfo() { return this.iterator.getWrap(false); }
setPosition(position, forceResetBoxInfos, checkStartTable) { this.iterator.setPosition(position, forceResetBoxInfos, checkStartTable); }
getPosition() { return this.iterator.getPosition(); }
documentStart() { this.iterator.documentStart(); }
formatRow(minY, paragraphHorizontalBounds, rowSpacingBeforeApplier) {
this.paragraphHorizontalBounds = paragraphHorizontalBounds;
this.initResult(minY);
Log.print(LogSource.RowFormatter, "formatRow", `paragraphHorizontalBounds: ${LogObjToStr.fixedInterval(paragraphHorizontalBounds)}, firstBoxOffset: ${this.currBox.rowOffset}`);
const prevRow = this.manager.activeFormatter.lastRowInfo.row;
const isFirstRowInParagraph = !prevRow || prevRow.flags.get(LayoutRowStateFlags.ParagraphEnd) ||
this.manager.activeFormatter.lastRowInfo.paragraphIndex != this.currWrapInfo.paragraphIndex;
const rowParagraphLeftIndent = isFirstRowInParagraph ?
UnitConverter.twipsToPixelsF(this.paragraphProps.getLeftIndentForFirstRow()) :
UnitConverter.twipsToPixelsF(this.paragraphProps.getLeftIndentForOtherRow());
const rowContentHorizontalBounds = FixedInterval.fromPositions(paragraphHorizontalBounds.start + rowParagraphLeftIndent, paragraphHorizontalBounds.end - UnitConverter.twipsToPixelsF(this.paragraphProps.rightIndent));
this.rowSizesManager = new RowSizesManager(this, rowContentHorizontalBounds, minY, rowSpacingBeforeApplier, this.manager.activeFormatter.layoutRowBoundsCalculator.getRectangleBounds(this.manager), isFirstRowInParagraph);
this.tabInfo = new RowTabInfo(this, paragraphHorizontalBounds.start);
this.wordHolder = new WordHolderInfo(this);
this.rowFormatting = true;
let cycleCounter = 0;
while (this.rowFormatting) {
this.setState(TextRowFormatterState.Base);
this.innerFormatRow();
if (++cycleCounter > 10000)
throw new Error(Errors.InternalException);
}
}
innerFormatRow() {
while (this.rowFormatting && this.currBox) {
const oldWrapTablPos = this.currWrapInfo.tablePosition;
RowFormatter.addBoxFunctionMap[this.currBox.getType()].call(this);
if (this.currWrapInfo) {
if (!oldWrapTablPos && this.currWrapInfo.tablePosition) {
this.finishRow();
break;
}
if (oldWrapTablPos && (!this.currWrapInfo.tablePosition || !this.currWrapInfo.equalsTablePositions(oldWrapTablPos))) {
this.wordHolder.pushBoxes();
this.row.flags.set(LayoutRowStateFlags.CellTableEnd, true);
this.finishRow();
break;
}
}
else {
if (oldWrapTablPos)
this.row.flags.set(LayoutRowStateFlags.CellTableEnd, true);
}
}
if (!this.currBox) {
if (!this.iterator.allBoxesGiven()) {
this.iterator.setPosition(this.startPos, false, false);
this.result.flags.set(RowFormatterResultFlag.NotEnoughChunks, true);
this.rowFormatting = false;
return;
}
this.row.flags.set(LayoutRowStateFlags.DocumentEnd, true);
}
this.rowFormatting = !this.rowSizesManager.finishRow();
}
finishRow() {
if (!this.result.row.boxes.length && !this.result.newAnchoredObjects.length)
throw new Error(Errors.InternalException);
this.rowFormatting = false;
}
setState(state) {
this.currentState = this.stateMap[state];
}
addAnchorObject() {
const ancBox = this.currBox;
this.setBoxInfo(true);
const existingObj = this.manager.activeFormatter.layoutPosition.page.anchoredObjectHolder.getObjById(ancBox.objectId);
if (existingObj) {
existingObj.isApproved = true;
return;
}
if (ancBox.getType() == LayoutBoxType.AnchorTextBox) {
const calculator = this.manager.anchoredObjectsManager.textBoxContextSizeCalculators[ancBox.objectId];
calculator.calculateSize(this.manager.boundsCalculator);
ancBox.setContentSize(calculator.layoutSize);
}
this.result.newAnchoredObjects.push(ancBox);
}
initResult(minY) {
this.result = new RowFormatterResult(this, minY);
this.setBoxInfo(false);
if (this.currBox)
this.startPos = this.currBox.rowOffset;
this.result.paragraphIndex = this.currWrapInfo.paragraphIndex;
this.result.sectionIndex = this.currWrapInfo.sectionIndex;
}
setBoxInfo(getNextWrap) {
let wrap = this.iterator.getWrap(getNextWrap);
if (!wrap) {
this.currBox = null;
this.currWrapInfo = null;
return;
}
this.currBox = wrap.box.getType() == LayoutBoxType.TabSpace ? wrap.box : wrap.box.clone();
this.currWrapInfo = wrap.info;
if (this.currBox.getType() == LayoutBoxType.LayoutDependent) {
this.currBox.calculateText(this.manager);
LayoutBox.initializeWithMeasurer([new BoxWrap(this.currBox, null)], this.manager.measurer, false);
}
}
}