devexpress-richedit
Version:
DevExpress Rich Text Editor is an advanced word-processing tool designed for working with rich text documents.
329 lines (328 loc) • 12.9 kB
JavaScript
import { MapCreator } from '../../../utils/map-creator';
import { Log } from '../../../rich-utils/debug/logger/base-logger/log';
import { ChunkedText } from '@devexpress/utils/lib/class/chunked-text';
import { Errors } from '@devexpress/utils/lib/errors';
import { ColorUtils } from '@devexpress/utils/lib/utils/color';
import { boolToString } from '@devexpress/utils/lib/utils/common';
import { RtfExportSR } from '../translation-table/rtf-export-sr';
import { byteToHex } from '../../../utils/hexadecimal-converter';
import { Characters } from './characters';
import { ListUtils } from '@devexpress/utils/lib/utils/list';
import { StringUtils } from '@devexpress/utils/lib/utils/string';
export class RtfBuilder {
static specialMarks = CreateSpecialMarksTable();
static byteToHexString = CreateByteToHexString();
static specialPCDataMarks = CreateSpecialPCDataMarksTable(RtfBuilder.byteToHexString);
isPreviousWriteCommand;
rowLength;
rowLengthBound = 200;
hexMode;
rtfContent;
unicodeTextBuilder;
constructor() {
this.init();
}
clear() {
this.init();
}
init() {
this.rtfContent = new ChunkedText("");
this.unicodeTextBuilder = new ChunkedText("");
this.isPreviousWriteCommand = false;
this.rowLength = 0;
this.hexMode = false;
}
writeCommandCore(command, writeParam) {
this.rtfContent.addText(command);
writeParam();
this.isPreviousWriteCommand = true;
this.increaseRowLength(command.length);
}
writeCommand(command, param) {
this.writeCommandCore(command, () => {
if (param != undefined)
if (typeof param === "boolean")
this.rtfContent.addText(boolToString(param));
else
this.rtfContent.addText(param.toString());
});
}
writeIntegerCommand(command, param) {
const intValue = Math.floor(param);
if (Log.isDebug && intValue !== param)
throw new Error(Errors.InternalException);
this.writeCommandCore(command, () => this.rtfContent.addText(intValue.toString()));
}
createKeyword(propertyName) {
return "\\" + propertyName;
}
createOptionalKeyword(propertyName) {
return "\\*" + propertyName;
}
writeAnsiText(text) {
const count = text.length;
if (this.isPreviousWriteCommand)
this.rtfContent.addText(RtfExportSR.Space);
for (let i = 0; i < count; i++) {
const ch = text[i];
const str = this.correctAnsiString(ch);
this.rtfContent.addText(str);
}
this.isPreviousWriteCommand = false;
this.increaseRowLength(text.length);
}
writeText(text, specialMarks = RtfBuilder.specialMarks) {
const count = text.length;
for (var i = 0; i < count; i++) {
const ch = text[i];
const specialMark = specialMarks[ch];
if (specialMark)
this.writeTextDirect(specialMark);
else
this.writeChar(ch);
}
}
writeTextDirect(text, makeStringUnicodeCompatible = false) {
if (this.isPreviousWriteCommand)
this.rtfContent.addText(RtfExportSR.Space);
if (makeStringUnicodeCompatible)
this.rtfContent.addText(this.getUnicodeCompatibleStringDirect(text));
else
this.rtfContent.addText(text);
this.isPreviousWriteCommand = false;
this.increaseRowLength(text.length);
}
writeTextDirectUnsafe(text) {
if (this.isPreviousWriteCommand)
this.rtfContent.addText(RtfExportSR.Space);
const textLength = text.textLength;
this.rtfContent.addText(text.getText());
this.isPreviousWriteCommand = false;
this.increaseRowLength(textLength);
}
writeChar(ch) {
if (this.isPreviousWriteCommand)
this.rtfContent.addText(RtfExportSR.Space);
const str = this.getUnicodeCompatibleString(ch);
this.rtfContent.addText(str);
this.isPreviousWriteCommand = false;
this.increaseRowLength(str.length);
}
openGroup() {
this.rtfContent.addText(RtfExportSR.OpenGroup);
this.isPreviousWriteCommand = false;
this.increaseRowLength(RtfExportSR.OpenGroup.length);
}
closeGroup() {
this.rtfContent.addText(RtfExportSR.CloseGroup);
this.isPreviousWriteCommand = false;
this.increaseRowLength(RtfExportSR.CloseGroup.length);
}
static isSpecialSymbol(ch) {
return ch == '{' || ch == '}' || ch == '\\';
}
textHasNonASCIISymbol(text) {
const count = text.length;
for (let i = 0; i < count; i++) {
let code = text.charCodeAt(i);
if (!this.isASCII(code))
return true;
}
return false;
}
isASCII(code) {
return code >= 0 && code <= 127;
}
isANSI(code) {
return code >= 0 && code <= 255;
}
containsNonAnsiChar(str) {
return ListUtils.anyOf(str.split(''), (ch) => !this.isANSI(this.getCode(ch)));
}
correctAnsiString(ch) {
this.unicodeTextBuilder = new ChunkedText("");
const code = this.getCode(ch);
if (this.isANSI(code)) {
if (RtfBuilder.isSpecialSymbol(ch))
this.unicodeTextBuilder.addText("\\");
this.unicodeTextBuilder.addText(ch);
}
else
this.unicodeTextBuilder.addText('?');
return this.unicodeTextBuilder.getText();
}
getUnicodeCompatibleString(ch) {
this.unicodeTextBuilder = new ChunkedText("");
const code = this.getCode(ch);
if (this.isASCII(code)) {
if (RtfBuilder.isSpecialSymbol(ch))
this.unicodeTextBuilder.addText("\\");
this.unicodeTextBuilder.addText(ch);
}
else
this.appendUnicodeCompatibleCharCore(code, ch);
return this.unicodeTextBuilder.getText();
}
getUnicodeCompatibleStringDirect(text) {
this.unicodeTextBuilder = new ChunkedText("");
const length = text.length;
for (var i = 0; i < length; i++) {
const ch = text[i];
const code = this.getCode(ch);
if (this.isASCII(code))
this.unicodeTextBuilder.addText(ch);
else
this.appendUnicodeCompatibleCharCore(code, ch);
}
return this.unicodeTextBuilder.getText();
}
appendUnicodeCompatibleCharCore(code, ch) {
const bytes = toUTF8Array(ch);
this.unicodeTextBuilder.addText(`\\u${code}\\'${RtfBuilder.byteToHexString[bytes[0]]}`);
}
writePictureBytes(binString) {
this.hexMode = true;
this.rtfContent.addText(RtfExportSR.Space);
this.rtfContent.addText(binString);
this.hexMode = false;
}
writeByteArrayAsHex(bytes, offset = 0, length = bytes.length) {
if (this.isPreviousWriteCommand)
this.rtfContent.addText(RtfExportSR.Space);
this.hexMode = true;
const count = offset + length;
for (let i = offset; i < count; i++) {
this.rtfContent.addText(byteToHex(bytes[i]));
}
this.isPreviousWriteCommand = true;
this.hexMode = false;
}
writeShapeColorProperty(propertyName, propertyValue) {
this.writeShapeProperty(propertyName, this.getIntColorValue(propertyValue).toString());
}
getIntColorValue(color) {
return ColorUtils.getRed(color) | (ColorUtils.getGreen(color) << 8) | (ColorUtils.getBlue(color) << 16);
}
writeShapeProperty(propertyName, propertyValue) {
this.openGroup();
this.writeCommand(RtfExportSR.ShapeProperty);
this.writeShapePropertyName(propertyName);
this.writeShapePropertyValue(propertyValue);
this.closeGroup();
}
writePCDATAShapeProperty(propertyName, propertyValue) {
this.writeShapeProperty(propertyName, () => this.writePCData(propertyValue));
}
writePCData(text) {
this.writeText(text, RtfBuilder.specialPCDataMarks);
}
writeShapePropertyName(propertyName) {
this.openGroup();
this.writeCommand(RtfExportSR.ShapePropertyName);
this.writeTextDirect(propertyName);
this.closeGroup();
}
writeShapePropertyValue(propertyValue) {
this.openGroup();
this.writeCommand(RtfExportSR.ShapePropertyValue);
if (typeof propertyValue == "string")
this.writeTextDirect(propertyValue, true);
else
propertyValue();
this.closeGroup();
}
writeShapePropertyWithOptionalGroup(propertyName, propertyValue, optionalPropertyName, optionalPropertyValue) {
this.openGroup();
this.writeCommand(RtfExportSR.ShapeProperty);
this.writeShapePropertyName(propertyName);
this.writeShapePropertyValue(propertyValue);
this.openGroup();
this.writeCommand(this.createOptionalKeyword(optionalPropertyName) + " ");
this.writeCommand(optionalPropertyValue);
this.closeGroup();
this.closeGroup();
}
writeShapeIntegerProperty(propertyName, propertyValue) {
this.writeShapeProperty(propertyName, Math.round(propertyValue).toString());
}
writeShapeBoolProperty(propertyName, propertyValue) {
this.writeShapeProperty(propertyName, boolToString(propertyValue));
}
increaseRowLength(delta) {
this.rowLength += delta;
if (this.rowLength >= this.rowLengthBound && this.hexMode) {
this.rtfContent.addText(RtfExportSR.CLRF);
this.isPreviousWriteCommand = false;
this.rowLength = 0;
}
}
getCode(ch) {
const charCode = ch.charCodeAt(0);
return charCode > 32767 ? charCode - 65536 : charCode;
}
writeDescription(nonVisualDrawingObjectInfo) {
let result = '';
if (!StringUtils.isNullOrEmpty(nonVisualDrawingObjectInfo.title)) {
result += "Title: " + nonVisualDrawingObjectInfo.title;
if (!StringUtils.isNullOrEmpty(nonVisualDrawingObjectInfo.description))
result += " - Description: " + nonVisualDrawingObjectInfo.description;
}
else if (!StringUtils.isNullOrEmpty(nonVisualDrawingObjectInfo.description))
result = nonVisualDrawingObjectInfo.description;
if (!StringUtils.isNullOrEmpty(result))
this.writeShapeProperty("wzDescription", result);
}
}
export function CreateSpecialMarksTable() {
const result = new MapCreator()
.add(Characters.EmSpace, "\\u8195\\'3f")
.add(Characters.EnSpace, "\\u8194\\'3f")
.add(Characters.Hyphen, "")
.add(Characters.LineBreak, "\\line ")
.add(Characters.PageBreak, "\\page ")
.add(Characters.ColumnBreak, "\\column ")
.add(Characters.QmSpace, "\\u8197\\'3f")
.add(Characters.TabMark, "\\tab ")
.get();
return result;
}
export function CreateSpecialPCDataMarksTable(byteToHexString) {
const result = new MapCreator().get();
for (let i = 0; i < 31; i++) {
AddHexToMarkTable(result, i.toString(), byteToHexString);
}
AddHexToMarkTable(result, '\\', byteToHexString);
AddHexToMarkTable(result, '{', byteToHexString);
AddHexToMarkTable(result, '}', byteToHexString);
return result;
}
export function AddHexToMarkTable(result, ch, byteToHexString) {
result[ch] = "\'" + byteToHexString[ch];
}
export function CreateByteToHexString() {
const byteToHexString = [];
for (var i = 0; i < 256; i++)
byteToHexString[i] = i.toString(16);
return byteToHexString;
}
export function toUTF8Array(str) {
var utf8 = [];
for (var i = 0; i < str.length; i++) {
var charcode = str.charCodeAt(i);
if (charcode < 0x80)
utf8.push(charcode);
else if (charcode < 0x800) {
utf8.push(0xc0 | (charcode >> 6), 0x80 | (charcode & 0x3f));
}
else if (charcode < 0xd800 || charcode >= 0xe000) {
utf8.push(0xe0 | (charcode >> 12), 0x80 | ((charcode >> 6) & 0x3f), 0x80 | (charcode & 0x3f));
}
else {
i++;
charcode = 0x10000 + (((charcode & 0x3ff) << 10)
| (str.charCodeAt(i) & 0x3ff));
utf8.push(0xf0 | (charcode >> 18), 0x80 | ((charcode >> 12) & 0x3f), 0x80 | ((charcode >> 6) & 0x3f), 0x80 | (charcode & 0x3f));
}
}
return utf8;
}