devexpress-richedit
Version:
DevExpress Rich Text Editor is an advanced word-processing tool designed for working with rich text documents.
353 lines (352 loc) • 15 kB
JavaScript
import { FormatImagesImporter } from '../../utils/images-import';
import { RunType } from '../../../model/runs/run-type';
import { Stack } from '@devexpress/utils/lib/class/stack';
import { Constants } from '@devexpress/utils/lib/constants';
import { Errors } from '@devexpress/utils/lib/errors';
import { StringUtils } from '@devexpress/utils/lib/utils/string';
import { SkipDestination } from './destination/base/skip-destination';
import { FieldDestination } from './destination/fields/field-destination';
import { TableContentFieldDestination } from './destination/fields/table-content-field-destination';
import { ShapeDestination } from './destination/shape/shape-destination';
import { DefaultDestination } from './destination/sub-document/default-destination';
import { DecoderHelper } from './dx-decoding/decoder-helper';
import { ImportersCollection } from './importers/importers-collection';
import { KeywordTableHolder } from './keyword-tables/keyword-table-holder';
import { RtfParsingState } from './model/enums';
import { RtfDocumentProperties } from './model/rtf-document-properties';
import { RtfSectionProperties } from './model/section/rtf-section-properties';
import { RtfTableReader } from './table/table-reader';
import { ClientModelManager } from '../../../model-manager';
import { EmptyBatchUpdatableObject } from '@devexpress/utils/lib/class/batch-updatable';
export class RtfPositionState {
constructor(subDocument, paragraph, fields, bookmarks, rangePermissions, sectionProperties, tableReader) {
this.subDocument = subDocument;
this.paragraph = paragraph;
this.fields = fields;
this.bookmarks = bookmarks;
this.rangePermissions = rangePermissions;
this.sectionProperties = sectionProperties;
this.tableReader = tableReader;
}
}
export class RtfImportData {
get destination() { return this._destination; }
set destination(newDestination) {
if (newDestination.subDocument !== this.destination.subDocument) {
this.beforeChangeSubDocument(newDestination.subDocument);
this.subDocument = newDestination.subDocument;
this.onChangeSubDocument();
}
this._destination = newDestination;
}
get savedStatesCount() { return this.savedDestinations.count; }
get formattingInfo() {
return this.importers.character.characterFormatting.rtfFormattingInfo;
}
constructor(rtfText, importOptions, documentModel, richOptions) {
this.intervalsToDelete = [];
this.documentModel = documentModel;
this.modelManager = new ClientModelManager(this.documentModel, richOptions, new EmptyBatchUpdatableObject());
this.formatImagesImporter = new FormatImagesImporter();
this.subDocument = this.documentModel.mainSubDocument;
this.rtfText = rtfText;
this.importerOptions = importOptions;
this.richOptions = richOptions;
this.savedDestinations = new Stack();
this.documentProperties = new RtfDocumentProperties();
this.keywordHTHolder = new KeywordTableHolder();
this.skipCount = 0;
this.binCharCount = 0;
this.parsingState = RtfParsingState.Normal;
this.rtfDocumentModelType = RtfDocumentModelType.WithStyle;
this.importers = new ImportersCollection(this);
this._destination = this.createDefaultDestination();
this.savedDestinations.push(this._destination);
this.positionStates = new Stack();
this.addEmptyPositionState();
}
beforeChangeSubDocument(newSubDocument) {
if (this.positionStates.count == 1)
return;
const endNestedSubDocument = this.positionStates.getPrevious().subDocument == newSubDocument;
if (endNestedSubDocument)
this.importers.finalizeSubDocument();
}
onChangeSubDocument() {
this.importers.startImportSubDocument();
const previousState = this.positionStates.getPrevious();
const endNestedSubDocument = this.positionStates.count > 1 && previousState.subDocument == this.subDocument;
if (endNestedSubDocument)
this.positionStates.pop();
else
this.addEmptyPositionState();
}
addEmptyPositionState() {
new Stack().push(new RtfSectionProperties());
return this.positionStates.push(new RtfPositionState(this.subDocument, this.importers.paragraph.createEmptyParagraph(), new Stack(), {}, {}, new Stack(), new RtfTableReader(this)));
}
import() {
this.beforeImport();
while (this.rtfText.moveToNextChar()) {
const ch = this.rtfText.currChar;
if (this.parsingState == RtfParsingState.BinData) {
this.parseBinChar(ch);
continue;
}
switch (ch) {
case '{':
this.flushDecoder();
this.pushState();
break;
case '}':
this.flushDecoder();
this.popRtfState();
break;
case '\\':
if (this.parseRtfKeyword() != ' ')
this.rtfText.moveToPrevChar();
break;
case '\r':
case '\n':
case '\0':
break;
default:
if (this.parsingState == RtfParsingState.Normal)
this.parseChar(ch);
else
this.parseHexChar(ch);
}
}
if (this.binCharCount != 0)
this.throwUnexpectedEndOfFile();
if (this.importers.field.fields.count != 0)
this.throwInvalidRtfFile();
if (!(this.destination instanceof DefaultDestination))
throw new Error(Errors.InternalException);
if (this.subDocument.getLastRun().getType() != RunType.ParagraphRun)
this.importers.paragraph.insertParagraph();
this.importers.section.setLastSectionLength();
this.importers.finalizeSubDocument();
this.afterImport();
}
afterImport() {
while (this.intervalsToDelete.length > 0) {
const interval = this.intervalsToDelete.pop();
this.modelManager.modelManipulator.range.removeInterval(interval, true, false);
}
}
parseRtfKeyword() {
let ch = this.readChar();
if (!StringUtils.isAlpha(ch)) {
this.translateControlChar(ch);
return ' ';
}
const keywordChars = [];
while (StringUtils.isAlpha(ch)) {
keywordChars.push(ch);
ch = this.readChar();
}
let paramValueAsString = [];
const isNegative = ch == '-';
if (isNegative) {
paramValueAsString.push(ch);
ch = this.readChar();
}
if (isNegative && !StringUtils.isDigit(ch)) {
paramValueAsString = [];
}
else {
while (StringUtils.isDigit(ch)) {
paramValueAsString.push(ch);
ch = this.readChar();
}
}
const paramValue = parseInt(paramValueAsString.join(''));
const keyword = keywordChars.join('');
if (this.isUnicodeSymbolDefinition(keyword)) {
const ch = String.fromCharCode(paramValue & 0xFFFF);
this.parseUnicodeChar(ch);
}
else {
this.flushDecoder();
this.translateKeyword(keyword, isNaN(paramValue) ? 0 : paramValue, paramValueAsString.length != 0);
}
return ch;
}
isUnicodeSymbolDefinition(keyword) {
return keyword === 'u';
}
flushDecoder() {
this.importers.character.characterFormatting.rtfFormattingInfo.activeDecoder.flush(this);
}
switchToCodePageDecoder() {
this.setActiveDecoder(this.formattingInfo.codePageDecoder);
}
switchToUnicodeDecoder() {
this.setActiveDecoder(this.formattingInfo.unicodeDecoder);
}
setActiveDecoder(decoder) {
if (this.formattingInfo.activeDecoder !== decoder) {
this.flushDecoder();
this.formattingInfo.activeDecoder = decoder;
}
}
throwUnexpectedEndOfFile() {
this.importerOptions.throwInvalidFile("Unexpected end of file");
}
throwInvalidRtfFile() {
this.importerOptions.throwInvalidFile("Invalid RTF file");
}
static throwInvalidRtfFile() {
throw new Error("Invalid RTF file");
}
beforeImport() {
this.documentModel.defaultCharacterProperties.fontInfo = this.documentModel.cache.fontInfoCache.getItemByName("Times New Roman");
this.documentModel.defaultCharacterProperties.fontSize = 12;
this.documentModel.numberingLists = [];
}
translateControlChar(ch) {
if (this.skipCount == 0 || ch == '\'')
this.destination.processControlChar(ch);
else
this.decreaseSkipCount();
}
translateKeyword(keyword, parameterValue, hasParameter) {
if (this.skipCount == 0 || keyword == "bin") {
const keywordProcessed = this.destination.processKeyword(keyword, parameterValue, hasParameter);
if (keywordProcessed) {
if (!(this.destination instanceof SkipDestination))
this.optionalGroupLevel = Constants.MAX_SAFE_INTEGER;
}
else {
if (this.optionalGroupLevel < Constants.MAX_SAFE_INTEGER)
this.destination = new SkipDestination(this);
}
}
else
this.decreaseSkipCount();
}
parseBinChar(ch) {
this.destination.processBinChar(ch);
this.binCharCount--;
if (this.binCharCount <= 0) {
this.parsingState = RtfParsingState.Normal;
this.decreaseSkipCount();
}
}
decreaseSkipCount() {
this.skipCount = Math.max(0, this.skipCount - 1);
}
popRtfState() {
const oldDestination = this.destination;
if (this.savedStatesCount == this.optionalGroupLevel)
this.optionalGroupLevel = Constants.MAX_SAFE_INTEGER;
this.popState();
if (this.savedStatesCount >= this.optionalGroupLevel)
this.destination = new SkipDestination(this);
oldDestination.afterPopRtfState();
this.skipCount = 0;
if (this.savedStatesCount == 0)
this.rtfText.resetToEnd();
}
parseHexChar(ch) {
this.parsingState = RtfParsingState.Normal;
const hiValue = this.hexToInt(ch, false);
const loValue = this.hexToInt(this.readChar(), false);
if (hiValue < 0 || loValue < 0)
return;
const hexValue = (hiValue << 4) + loValue;
this.parseChar(DecoderHelper.decode(String.fromCharCode(hexValue), this.codePage));
}
parseChar(ch) {
if (this.skipCount == 0) {
if (ch == '\r' && this.destination.canProcessSpecialHexChar()) {
this.flushDecoder();
this.destination.processSpecialHexChar(ch);
}
else {
this.switchToCodePageDecoder();
this.formattingInfo.activeDecoder.processChar(this, ch);
}
}
else
this.decreaseSkipCount();
}
readChar() {
if (!this.rtfText.moveToNextChar())
this.throwUnexpectedEndOfFile();
return this.rtfText.currChar;
}
hexToInt(ch, throwException) {
if (StringUtils.isDigit(ch))
return ch.charCodeAt(0) - '0'.charCodeAt(0);
else {
if (StringUtils.stringInLowerCase(ch)) {
if (ch < 'a' || ch > 'f') {
if (throwException)
this.importerOptions.throwInvalidFile("Invalid hex value");
else
return -1;
}
return 10 + ch.charCodeAt(0) - 'a'.charCodeAt(0);
}
else {
if (ch < 'A' || ch > 'F') {
if (throwException)
this.importerOptions.throwInvalidFile("Invalid hex value");
else
return -1;
}
return 10 + ch.charCodeAt(0) - 'A'.charCodeAt(0);
}
}
}
parseUnicodeChar(ch) {
this.switchToUnicodeDecoder();
this.formattingInfo.activeDecoder.processChar(this, ch);
this.skipCount = this.importers.character.characterFormatting.rtfFormattingInfo.unicodeCharacterByteCount;
}
processChar(ch) {
this.destination.processChar(ch);
}
setCodePage(codePage) {
this.importers.character.characterFormatting.rtfFormattingInfo.codePage = codePage;
}
get codePage() {
return this.importers.character.characterFormatting.rtfFormattingInfo.codePage;
}
parseCharWithoutDecoding(ch) {
this.flushDecoder();
if (this.skipCount == 0)
this.processChar(ch);
this.decreaseSkipCount();
}
createTableContentFieldDestination(createField) { return new TableContentFieldDestination(this, createField); }
createShapeDestination() { return new ShapeDestination(this); }
createFieldDestination() { return new FieldDestination(this); }
createDefaultDestination() { return new DefaultDestination(this, this.subDocument); }
pushState() {
this.importers.pushState();
this.savedDestinations.push(this.destination);
this.destination = this.destination.clone();
this.destination.increaseGroupLevel();
}
popState() {
const nestedDestination = this.destination;
const parentDestination = this.savedDestinations.peek();
parentDestination.beforeNestedGroupFinished(nestedDestination);
nestedDestination.beforePopRtfState();
this.importers.popState();
this.destination = parentDestination;
this.destination.nestedGroupFinished(nestedDestination);
this.savedDestinations.pop();
this.subDocument = parentDestination.subDocument;
this.destination.afterNestedGroupFinished(nestedDestination);
}
}
export var RtfDocumentModelType;
(function (RtfDocumentModelType) {
RtfDocumentModelType[RtfDocumentModelType["None"] = 0] = "None";
RtfDocumentModelType[RtfDocumentModelType["WithoutStyle"] = 1] = "WithoutStyle";
RtfDocumentModelType[RtfDocumentModelType["WithStyle"] = 2] = "WithStyle";
})(RtfDocumentModelType || (RtfDocumentModelType = {}));