UNPKG

devexpress-richedit

Version:

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

421 lines (420 loc) 21.9 kB
import { UnitConverter } from '@devexpress/utils/lib/class/unit-converter'; import { SimpleFormattersManager } from '@devexpress/utils/lib/formatters/manager'; import { ListUtils } from '@devexpress/utils/lib/utils/list'; import { NumberMapUtils } from '@devexpress/utils/lib/utils/map/number'; import { SearchUtils } from '@devexpress/utils/lib/utils/search'; import { MaskedCharacterPropertiesBundle } from '../rich-utils/properties-bundle'; import { BorderInfo } from './borders/border-info'; import { BorderLineStyle } from './borders/enums'; import { DocumentCache } from './caches/caches'; import { MaskedCharacterProperties } from './character/character-properties'; import { Chunk } from './chunk'; import { ColorHelper } from './color/color'; import { ColorProvider } from './color/color-provider'; import { SubDocumentInfoType } from './enums'; import { DocumentProtectionType } from './json/enums/json-document-enums'; import { JSONMaskedCharacterPropertiesConverter } from './json/importers/json-masked-character-properties-converter'; import { JSONMaskedParagraphPropertiesConverter } from './json/importers/json-masked-paragraph-properties-converter'; import { NumberingListCountersCalculator } from './numbering-lists/numbering-list-counters-calculator'; import { AbstractNumberingListIdProvider, NumberingListIdProvider } from './numbering-lists/numbering-list-id-provider'; import { DocumentProtectionProperties } from './options/document-protection'; import { Paragraph } from './paragraph/paragraph'; import { MaskedParagraphProperties, ParagraphPropertiesMask } from './paragraph/paragraph-properties'; import { PasswordHashCodeCalculator } from './protection/password-hash-code-calculator'; import { RichUtils } from './rich-utils'; import { ParagraphRun } from './runs/simple-runs'; import { StylesManager } from './styles-manager'; import { SubDocument, SubDocumentInterval } from './sub-document'; import { SubDocumentInfoBase } from './sub-document-infos'; import { WebSettings } from './web-settings'; import { ModelComparer } from './model-checks/comparer'; import { FieldsWaitingForUpdate } from './fields/tree-creator'; import { FieldCodeParserHyperlink } from './fields/parsers/field-code-parser-hyperlink'; import { FindReplaceHelper, FindReplaceState, SearchDirection } from './find-replace-helper'; import { AnchoredPictureRun } from './runs/anchored-picture-run'; import { InlinePictureRun } from './runs/inline-picture-run'; import { SubDocumentCollection } from './sub-document-collection'; import { NotePosition, NoteSeparators } from './footnotes/footnote'; export class DocumentModel { get subDocuments() { return this.subDocumentsCollection.filteredCollection; } get options() { return this.modelOptions.control; } ; get isDocumentProtectionEnabled() { const properties = this.documentProtectionProperties; return this.aspxIsDocumentProtectionEnabled || (properties.enforceProtection && properties.protectionType != DocumentProtectionType.None); } constructor(modelOptions, subDocumentsIdCounter = 1) { this.mirrorMargins = false; this.aspxIsDocumentProtectionEnabled = false; this.documentEndNotePosition = NotePosition.EndOfDocument; this.sections = []; this.headers = []; this.footers = []; this.characterStyles = []; this.paragraphStyles = []; this.numberingListStyles = []; this.tableStyles = []; this.tableCellStyles = []; this.subDocumentsCollection = new SubDocumentCollection(); this.abstractNumberingListTemplates = []; this.abstractNumberingLists = []; this.numberingLists = []; this.abstractNumberingListsIdProvider = new AbstractNumberingListIdProvider(this); this.numberingListsIdProvider = new NumberingListIdProvider(this); this.footNoteSeparators = new NoteSeparators(this); this.endNoteSeparators = new NoteSeparators(this); this.repositoryBorderItem = new BorderInfo(); this.compatSettings = []; this.subDocumentsIdCounter = -1; this.loaded = false; this.documentProtectionProperties = new DocumentProtectionProperties(); this.modelOptions = modelOptions; this.simpleFormattersManager = new SimpleFormattersManager(modelOptions.cultureOpts); this.cache = new DocumentCache(); this.mainSubDocument = this.importSubDocument(SubDocumentInfoBase.create(SubDocumentInfoType.Main, SubDocument.MAIN_SUBDOCUMENT_ID, -1)); this.stylesManager = new StylesManager(this); this.subDocumentsIdCounter = subDocumentsIdCounter; this.colorProvider = new ColorProvider(this.cache.colorModelInfoCache); this.initRepositoryBorderItem(); this.webSettings = new WebSettings(); this.compatibilitySettings = new CompatibilitySettings(); this.docVariables = new DocumentVariables(); } setPageColor(value) { this.displayBackgroundShape = true; this.pageBackColor = value; } getAllBookmarks(getHiddenToo) { const result = []; NumberMapUtils.forEach(this.subDocuments, (subDoc) => { if (getHiddenToo) ListUtils.addListOnTail(result, subDoc.bookmarks); else ListUtils.forEach(subDoc.bookmarks, (bm) => { if (!bm.isHidden()) result.push(bm); }); }); return result; } getAllImages() { const result = []; NumberMapUtils.forEach(this.subDocuments, (subDoc) => { NumberMapUtils.forEach(subDoc.chunks, (chunk) => { NumberMapUtils.forEach(chunk.textRuns, (run) => { if (run instanceof AnchoredPictureRun || run instanceof InlinePictureRun) result.push(run.cacheInfo); }); }); }); return result; } initRepositoryBorderItem() { this.repositoryBorderItem.color = this.colorProvider.getModelColorFromRgba(ColorHelper.AUTOMATIC_COLOR); this.repositoryBorderItem.style = BorderLineStyle.Single; this.repositoryBorderItem.width = UnitConverter.pixelsToTwipsF(1); } getCharacterStyleByName(name) { return this.stylesManager.getCharacterStyleByName(name); } getParagraphStyleByName(name) { return this.stylesManager.getParagraphStyleByName(name); } getNumberingListStyleByName(name) { return this.stylesManager.getNumberingListStyleByName(name); } getTableStyleByName(name) { return this.stylesManager.getTableStyleByName(name); } getTableCellStyleByName(name) { return this.stylesManager.getTableCellStyleByName(name); } getDefaultCharacterStyle() { return this.stylesManager.getDefaultCharacterStyle(); } getDefaultParagraphStyle() { return this.stylesManager.getDefaultParagraphStyle(); } getDefaultTableStyle() { return this.stylesManager.getDefaultTableStyle(); } getDefaultTableCellStyle() { return this.stylesManager.getDefaultTableCellStyle(); } setDefaultCharacterProperties(obj) { const serverProps = JSONMaskedCharacterPropertiesConverter.convertFromJSON(obj, this.cache.colorModelInfoCache, this.cache.shadingInfoCache, this.cache.fontInfoCache); serverProps.setAllUse(); this.defaultCharacterProperties = this.cache.maskedCharacterPropertiesCache.getItem(serverProps); } setDefaultParagraphProperties(obj) { const serverProps = JSONMaskedParagraphPropertiesConverter.convertFromJSON(obj, this.cache.colorModelInfoCache, this.cache.shadingInfoCache); serverProps.setUseValue(ParagraphPropertiesMask.UseAll, true); this.defaultParagraphProperties = this.cache.maskedParagraphPropertiesCache.getItem(serverProps); } getSectionsByInterval(interval) { var result = [], section; var endPosition = interval.end; var sectionIndex = SearchUtils.normedInterpolationIndexOf(this.sections, (s) => s.startLogPosition.value, interval.start); for (; section = this.sections[sectionIndex]; sectionIndex++) { if (section.startLogPosition.value > endPosition) break; result.push(section); } return result; } getSectionIndicesByIntervals(intervals) { let result = []; for (var i = 0, interval; interval = intervals[i]; i++) { let sectionIndex = SearchUtils.normedInterpolationIndexOf(this.sections, (s) => s.startLogPosition.value, interval.start); let intervalEnd = interval.end; result.push(sectionIndex++); for (let section; section = this.sections[sectionIndex]; sectionIndex++) { if (section.startLogPosition.value < intervalEnd) result.push(sectionIndex); else break; } } return ListUtils.uniqueNumber(result); } getSectionByPosition(position) { var sectionIndex = SearchUtils.normedInterpolationIndexOf(this.sections, (s) => s.startLogPosition.value, position); return this.sections[sectionIndex]; } getCurrentLength() { var lastChunk = this.mainSubDocument.getLastChunk(); var lastRun = lastChunk.textRuns[lastChunk.textRuns.length - 1]; return lastChunk.startLogPosition.value + lastRun.startOffset + lastRun.getLength(); } isLoaded() { if (this.loaded) return true; const lastChunk = this.mainSubDocument.getLastChunk(); this.loaded = lastChunk ? lastChunk.isLast : false; return this.loaded; } getActualPageBackgroundColor() { if (!this.displayBackgroundShape) return ColorHelper.NO_COLOR; return this.pageBackColor; } getNumberingListIndexById(id) { for (var i = 0, numberingList; numberingList = this.numberingLists[i]; i++) { if (numberingList.getId() === id) return i; } return -1; } getAbstractNumberingListIndexById(id) { for (var i = 0, abstractNumberingList; abstractNumberingList = this.abstractNumberingLists[i]; i++) { if (abstractNumberingList.getId() === id) return i; } return -1; } getRangeListCounters(paragraph) { var calculator = new NumberingListCountersCalculator(paragraph.getAbstractNumberingList()); return calculator.calculateCounters(paragraph); } resetMergedFormattingCache(type) { this.mainSubDocument.resetMergedFormattingCache(type); } getPreviousSection(section) { var sectionIndex = SearchUtils.normedInterpolationIndexOf(this.sections, s => s.startLogPosition.value, section.startLogPosition.value); return this.sections[sectionIndex - 1]; } getNextSection(section) { var sectionIndex = SearchUtils.normedInterpolationIndexOf(this.sections, s => s.startLogPosition.value, section.startLogPosition.value); return this.sections[sectionIndex + 1]; } importSubDocument(info) { return this.createSubDocumentInternal(info, true); } updateHyperlinkFields(processor, interval, newSubDocuments) { const subDocIntervals = [ interval, ...newSubDocuments.map(s => new SubDocumentInterval(s, s.interval)) ]; NumberMapUtils.forEach(subDocIntervals, (subDocInterval) => { for (let field of subDocInterval.subDocument.fields) { if (subDocInterval.interval.containsInterval(field.getAllFieldInterval())) { const fieldParser = FieldsWaitingForUpdate.getParser(processor.modelManager, processor.layoutFormatterManager, processor.createFieldRequestManager(), subDocInterval.subDocument, field); if (fieldParser instanceof FieldCodeParserHyperlink && !field.getHyperlinkInfo()) { fieldParser.parseCodeCurrentFieldInternal(null); } } } }); } createSubDocument(subDocumentInfoType, parentSubDocumentId, disableInit = false) { const id = this.subDocumentsIdCounter++; const info = SubDocumentInfoBase.create(subDocumentInfoType, id, parentSubDocumentId); if (info.getType() !== subDocumentInfoType) throw new Error("SubDocument.type doesn't equal to info.type"); return this.createSubDocumentInternal(info, disableInit); } createSubDocumentInternal(info, disableInit) { if (this.subDocuments[info.subDocumentId]) throw new Error("SubDocument with this ID already exists"); const subDocument = new SubDocument(this, info); if (!disableInit) this.initNewSubDocument(subDocument); this.subDocumentsCollection.add(subDocument); return subDocument; } initNewSubDocument(subDocument) { subDocument.chunks = [new Chunk(subDocument.positionManager.registerPosition(0), "", true)]; subDocument.paragraphs.push(new Paragraph(subDocument, subDocument.positionManager.registerPosition(0), 1, this.getDefaultParagraphStyle(), MaskedParagraphProperties.createDefault(this))); subDocument.chunks[0].textRuns.push(new ParagraphRun(0, subDocument.paragraphs[0], new MaskedCharacterPropertiesBundle(MaskedCharacterProperties.createDefault(this), this.getDefaultCharacterStyle()))); subDocument.chunks[0].textBuffer = RichUtils.specialCharacters.ParagraphMark; } checkPasswordHash(hash, expectedHash) { if (expectedHash == null || expectedHash.length <= 0) return true; return PasswordHashCodeCalculator.compareByteArrays(hash, expectedHash); } checkLegacyDocumentProtectionPassword(calculator, password) { const hash = calculator.calculateLegacyPasswordHash(password); return this.checkPasswordHash(hash, this.documentProtectionProperties.word2003PasswordHash); } checkOpenXmlDocumentProtectionPassword(calculator, password) { const hash = calculator.calculatePasswordHash(password, this.documentProtectionProperties.passwordPrefix, this.documentProtectionProperties.hashIterationCount, this.documentProtectionProperties.hashAlgorithmType); return this.checkPasswordHash(hash, this.documentProtectionProperties.passwordHash); } checkDocumentProtectionPassword(password) { const calculator = new PasswordHashCodeCalculator(); if (this.documentProtectionProperties.passwordPrefix && this.documentProtectionProperties.passwordHash) return this.checkOpenXmlDocumentProtectionPassword(calculator, password); this.checkLegacyDocumentProtectionPassword(calculator, password); } findAll(searchSettings) { const foundIntervals = []; const findReplaceHelper = new FindReplaceHelper(searchSettings.modelManager, searchSettings.formatterManager, searchSettings.pageIndex, searchSettings.subDocument, searchSettings.layout, searchSettings.storeSelection); findReplaceHelper.setSearchParams(searchSettings.text, null, SearchDirection.All, searchSettings.matchCase, false, 0, false); while (findReplaceHelper.findNext() !== FindReplaceState.SearchEnd) { const lastFound = findReplaceHelper.getLastFound(); foundIntervals.push(lastFound); } return foundIntervals; } clone() { const model = new DocumentModel(this.modelOptions, this.subDocumentsIdCounter); model.modelOptions = this.modelOptions.clone(); model.defaultTabWidth = this.defaultTabWidth; model.differentOddAndEvenPages = this.differentOddAndEvenPages; model.displayBackgroundShape = this.displayBackgroundShape; model.pageBackColor = this.pageBackColor; model.aspxIsDocumentProtectionEnabled = this.aspxIsDocumentProtectionEnabled; model.webSettings = this.webSettings.clone(); model.compatSettings = ListUtils.deepCopy(this.compatSettings); model.compatibilitySettings = this.compatibilitySettings.clone(); model.cache = this.cache.clone(); model.defaultCharacterProperties = this.defaultCharacterProperties.clone(); model.defaultParagraphProperties = this.defaultParagraphProperties.clone(); model.defaultTableProperties = this.defaultTableProperties.clone(); model.defaultTableRowProperties = this.defaultTableRowProperties.clone(); model.defaultTableCellProperties = this.defaultTableCellProperties.clone(); model.stylesManager = this.stylesManager.clone(model); model.characterStyles = ListUtils.deepCopy(this.characterStyles); model.paragraphStyles = ListUtils.deepCopy(this.paragraphStyles); model.numberingListStyles = ListUtils.deepCopy(this.numberingListStyles); model.tableStyles = ListUtils.deepCopy(this.tableStyles); model.tableCellStyles = ListUtils.deepCopy(this.tableCellStyles); model.headers = ListUtils.deepCopy(this.headers); model.footers = ListUtils.deepCopy(this.footers); model.sections = ListUtils.map(this.sections, s => s.cloneToNewModel(model)); model.abstractNumberingListTemplates = ListUtils.map(this.abstractNumberingListTemplates, list => list.clone(model)); model.abstractNumberingLists = ListUtils.map(this.abstractNumberingLists, list => list.clone(model)); model.numberingLists = ListUtils.map(this.numberingLists, list => list.clone(model)); model.abstractNumberingListsIdProvider = this.abstractNumberingListsIdProvider.clone(model); model.numberingListsIdProvider = this.numberingListsIdProvider.clone(model); model.repositoryBorderItem = this.repositoryBorderItem.clone(); model.colorProvider = this.colorProvider.clone(model.cache.colorModelInfoCache); model.simpleFormattersManager = new SimpleFormattersManager(model.modelOptions.cultureOpts); model.documentProtectionProperties = this.documentProtectionProperties.clone(); model.subDocumentsIdCounter = this.subDocumentsIdCounter; model.subDocumentsCollection = this.subDocumentsCollection.clone(model); model.mainSubDocument = model.subDocuments[SubDocument.MAIN_SUBDOCUMENT_ID]; model.loaded = this.loaded; return model; } compare(source, format = null) { return new ModelComparer(this, source, format).compareAll(); } } export class SearchSettings { constructor(modelManager, formatterManager, layout, subDocument, text, matchCase, highlightResults, pageIndex, storeSelection) { this.modelManager = modelManager; this.formatterManager = formatterManager; this.layout = layout; this.subDocument = subDocument; this.text = text; this.matchCase = matchCase; this.highlightResults = highlightResults; this.pageIndex = pageIndex; this.storeSelection = storeSelection; } } export var ResetFormattingCacheType; (function (ResetFormattingCacheType) { ResetFormattingCacheType[ResetFormattingCacheType["Character"] = 1] = "Character"; ResetFormattingCacheType[ResetFormattingCacheType["Paragraph"] = 2] = "Paragraph"; ResetFormattingCacheType[ResetFormattingCacheType["All"] = 2147483647] = "All"; })(ResetFormattingCacheType || (ResetFormattingCacheType = {})); export var CompatibilityMode; (function (CompatibilityMode) { CompatibilityMode[CompatibilityMode["Word2003"] = 11] = "Word2003"; CompatibilityMode[CompatibilityMode["Word2007"] = 12] = "Word2007"; CompatibilityMode[CompatibilityMode["Word2010"] = 14] = "Word2010"; CompatibilityMode[CompatibilityMode["Word2013"] = 15] = "Word2013"; })(CompatibilityMode || (CompatibilityMode = {})); export class CompatibilitySettings { constructor() { this.dontJustifyLinesEndingInSoftLineBreak = false; this.compatibilityMode = CompatibilityMode.Word2013; } get allowParagraphSpacingAfterPageBreak() { return this.compatibilityMode <= CompatibilityMode.Word2010; } get layoutInTableCell() { return this.compatibilityMode >= CompatibilityMode.Word2013; } get matchHorizontalTableIndentsToTextEdge() { return this.compatibilityMode >= CompatibilityMode.Word2013; } getActualLayoutInTableCell(anchorInfo) { if (this.layoutInTableCell) return true; return anchorInfo.layoutTableCell; } clone() { const result = new CompatibilitySettings(); result.dontJustifyLinesEndingInSoftLineBreak = this.dontJustifyLinesEndingInSoftLineBreak; result.compatibilityMode = this.compatibilityMode; return result; } } export class DocumentVariables { constructor() { this._map = {}; } get count() { return Object.keys(this._map).length; } contains(name) { return Object.prototype.hasOwnProperty.call(this._map, name.toLowerCase()); } getValue(name) { return this._map[name.toLowerCase()]; } addValue(name, value) { this._map[name.toLowerCase()] = value; } foreach(callback) { for (let name in this._map) callback(name, this._map[name]); } }