UNPKG

devexpress-richedit

Version:

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

190 lines (189 loc) 11 kB
import { CommandBase } from '../../common/commands/command-base'; import { SimpleCommandState } from '../../common/commands/command-states'; import { MergeMode } from '../../common/commands/dialogs/dialog-finish-and-merge-command'; import { ClientModelManager } from '../../common/model-manager'; import { FieldCodeParserMailMerge } from '../../common/model/fields/parsers/field-code-parser-merge-field'; import { FieldsWaitingForUpdate } from '../../common/model/fields/tree-creator'; import { InsertParagraphManipulatorParams } from '../../common/model/manipulators/paragraph-manipulator/insert-paragraph-manipulator-params'; import { RangeCopy } from '../../common/model/manipulators/range/create-range-copy-operation'; import { InsertTextManipulatorParams } from '../../common/model/manipulators/text-manipulator/insert-text-manipulator-params'; import { ControlOptions, DocumentCapability } from '../../common/model/options/control'; import { RichUtils } from '../../common/model/rich-utils'; import { RunType } from '../../common/model/runs/run-type'; import { SectionStartType } from '../../common/model/section/enums'; import { SubDocumentIntervals, SubDocumentPosition } from '../../common/model/sub-document'; import { MaskedCharacterPropertiesBundle } from '../../common/rich-utils/properties-bundle'; import { EmptyBatchUpdatableObject } from '@devexpress/utils/lib/class/batch-updatable'; import { Errors } from '@devexpress/utils/lib/errors'; import { FixedInterval } from '@devexpress/utils/lib/intervals/fixed'; 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 { DocxExportOptions } from '../formats/docx/export/docx-export-options'; import { DocxExporter } from '../formats/docx/export/exporter'; import { Importer } from '../formats/docx/import/importer'; import { ImporterOptions } from '../formats/docx/import/importer-options'; import { exportModelToBlob } from '../model-api/formats/exporter'; import { FieldCodeParserIf } from '../../common/model/fields/parsers/field-code-parser-if'; export class MailMergeCommand extends CommandBase { getState() { var state = new SimpleCommandState(this.isEnabled()); state.visible = this.control.modelManager.richOptions.control.fields !== DocumentCapability.Hidden; return state; } isEnabledInReadOnlyMode() { return true; } isEnabled() { return (super.isEnabled() && ControlOptions.isEnabled(this.control.modelManager.richOptions.control.fields) && this.control.modelManager.model.mainSubDocument.getDocumentEndPosition() > 1 && !!this.getDataSource()); } canModify() { return true; } executeCore(_state, options) { const docxExporter = new DocxExporter(this.control.modelManager.modelManipulator, new DocxExportOptions()); docxExporter.exportToBlob((blob) => { const docxImporter = new Importer(new ImporterOptions()); docxImporter.importFromFile(blob, this.control.modelManager.richOptions, (documentModel, formatImagesImporter) => { const exportModelOptions = this.control.getExportModelOptions({ documentFormat: options.param.documentFormat, modelManager: this.createModelManager(documentModel), }); this.control.commandManager.formatImagesImporters.push(formatImagesImporter); formatImagesImporter.whenAllPicturesLoaded((successLoadedAllPictures) => { const index = this.control.commandManager.formatImagesImporters.indexOf(formatImagesImporter); if (index >= 0) this.control.commandManager.formatImagesImporters.splice(index, 1); if (!successLoadedAllPictures) throw new Error(Errors.InternalException); const param = options.param; this.prepareMergedDocument(exportModelOptions.modelManager, param); exportModelToBlob(exportModelOptions, (blob) => param.callback(blob)); }, 3000); formatImagesImporter.import(exportModelOptions.modelManager.modelManipulator); }, () => { }); }); return true; } prepareMergedDocument(modelManager, param) { const subDoc = modelManager.model.mainSubDocument; const subDocumentIntervals = new SubDocumentIntervals(subDoc, [new FixedInterval(0, subDoc.getDocumentEndPosition() - 1)]); const rangeCopy = RangeCopy.create(subDocumentIntervals); const dataSource = this.getDataSource(); const exportToIndex = Math.min(param.exportFrom + param.exportRecordsCount, dataSource.totalCount()) - 1; if (param.exportFrom > exportToIndex) modelManager.modelManipulator.range.removeIntervalWithoutHistory(subDoc, subDocumentIntervals.intervals[0], false); let lastProcessedPositionInMainSubDocument = 0; const processedSubDocIds = []; for (let index = param.exportFrom; index <= exportToIndex; index++) { if (index > param.exportFrom) rangeCopy.insertTo(modelManager.modelManipulator, new SubDocumentPosition(subDoc, subDoc.getDocumentEndPosition() - 1)); const record = dataSource.items()[index]; this.replaceFieldsInModel(modelManager, record, lastProcessedPositionInMainSubDocument, processedSubDocIds); if (index < exportToIndex) this.insertSeparator(modelManager, param.mergeMode); lastProcessedPositionInMainSubDocument = subDoc.getDocumentEndPosition() - 1; } } getDataSource() { return this.control.owner.dataSource; } replaceFieldsInModel(modelManager, record, lastProcessedPositionInMainSubDocument, processedSubDocIds) { NumberMapUtils.forEach(modelManager.model.subDocuments, (subDoc) => { if (!ListUtils.anyOf(processedSubDocIds, (id) => id === subDoc.id)) { this.replaceMergeFieldsInSubDocument(modelManager, record, lastProcessedPositionInMainSubDocument, subDoc); this.replaceIfFieldsInSubDocument(modelManager, lastProcessedPositionInMainSubDocument, subDoc); if (!subDoc.isMain()) processedSubDocIds.push(subDoc.id); } }); } processFieldsInSubDocument(modelManager, lastProcessedPositionInMainSubDocument, subDoc, processField) { for (let i = subDoc.fields.length - 1; i >= 0; i--) { const field = subDoc.fields[i]; if (subDoc.isMain() && field.getFieldStartPosition() < lastProcessedPositionInMainSubDocument) { return; } const parser = FieldsWaitingForUpdate.getParser(modelManager, null, null, subDoc, field); if (parser) { try { const text = processField(parser); if (text !== null) { this.replaceFieldWithText(modelManager, subDoc, field, text); } } finally { parser.destructor(); } } } } replaceMergeFieldsInSubDocument(modelManager, record, lastProcessedPositionInMainSubDocument, subDoc) { const processField = (parser) => { if (parser instanceof FieldCodeParserMailMerge) { const fieldName = parser.getMergeFieldName(); return RichUtils.replaceParagraphEndCharsWithLineBreak(this.getResultByFieldName(record, fieldName)); } return null; }; this.processFieldsInSubDocument(modelManager, lastProcessedPositionInMainSubDocument, subDoc, processField); } replaceIfFieldsInSubDocument(modelManager, lastProcessedPositionInMainSubDocument, subDoc) { const processField = (parser) => { if (parser instanceof FieldCodeParserIf) { parser.parseSwitchesAndArgs(); return parser.getResult(); } return null; }; this.processFieldsInSubDocument(modelManager, lastProcessedPositionInMainSubDocument, subDoc, processField); } replaceFieldWithText(modelManager, subDoc, field, text) { if (!StringUtils.isNullOrEmpty(text)) { const pos = field.getFieldStartPosition(); const runProps = subDoc.getRunByPosition(pos).getCharPropsBundle(modelManager.model); const insertParams = new InsertTextManipulatorParams(new SubDocumentPosition(subDoc, pos), runProps, RunType.TextRun, text); modelManager.modelManipulator.text.insertTextInner(insertParams); } modelManager.modelManipulator.range.removeIntervalWithoutHistory(subDoc, field.getAllFieldInterval(), false); } getResultByFieldName(record, fieldName) { const keys = Object.keys(record); for (let i = 0, key; (key = keys[i]); i++) { if (key.toLowerCase() == fieldName.toLowerCase()) return record[key] + ''; } return ''; } insertSeparator(modelManager, mergeMode) { const subDoc = modelManager.model.mainSubDocument; const position = subDoc.getDocumentEndPosition() - 1; const firstRun = subDoc.getRunByPosition(0); const characterStyle = firstRun.characterStyle; const maskedCharacterProperties = firstRun.maskedCharacterProperties.clone(); const maskedCharacterPropertiesBundle = new MaskedCharacterPropertiesBundle(maskedCharacterProperties, characterStyle); switch (mergeMode) { case MergeMode.NewParagraph: modelManager.modelManipulator.paragraph.insertParagraphViaHistory(new InsertParagraphManipulatorParams(new SubDocumentPosition(subDoc, position), maskedCharacterPropertiesBundle)); break; case MergeMode.NewSection: modelManager.modelManipulator.section.insertSectionAndSetStartType(position, SectionStartType.NextPage, maskedCharacterPropertiesBundle); break; } } createModelManager(model) { return new ClientModelManager(model, this.control.modelManager.richOptions, new EmptyBatchUpdatableObject()); } } export class MailMergeCommandParameters { constructor(callback, mergeMode, documentFormat, exportFrom, exportRecordsCount) { this.callback = callback; this.mergeMode = mergeMode; this.documentFormat = documentFormat; this.exportFrom = exportFrom ? exportFrom : 0; this.exportRecordsCount = exportRecordsCount ? exportRecordsCount : Infinity; } }