devexpress-richedit
Version:
DevExpress Rich Text Editor is an advanced word-processing tool designed for working with rich text documents.
208 lines (207 loc) • 11.1 kB
JavaScript
import { UnitConverter } from '@devexpress/utils/lib/class/unit-converter';
import { Size } from '@devexpress/utils/lib/geometry/size';
import { Base64Utils } from '@devexpress/utils/lib/utils/base64';
import { NumberMapUtils } from '@devexpress/utils/lib/utils/map/number';
import { isDefined } from '@devexpress/utils/lib/utils/common';
export class CacheImageInfo {
static emptyPicDimension = UnitConverter.pixelsToTwips(32);
static get emptyPictureSize() { return new Size(CacheImageInfo.emptyPicDimension, CacheImageInfo.emptyPicDimension); }
_referenceInfo;
_base64;
_convertedBase64;
_size;
_isLoaded;
_isSizeDefined;
tmpId;
actualId;
imageUrl;
file;
get isLoaded() { return this._referenceInfo ? this._referenceInfo._isLoaded : this._isLoaded; }
set isLoaded(val) { this._isLoaded = val; }
get isSizeDefined() { return this._referenceInfo ? this._referenceInfo._isSizeDefined : this._isSizeDefined; }
set isSizeDefined(val) { this._isSizeDefined = val; }
get size() { return this._referenceInfo ? this._referenceInfo._size : this._size; }
set size(val) { this._size = val; }
get currId() { return this.actualId !== undefined ? this.actualId : this.tmpId; }
get base64() { return this._referenceInfo ? this._referenceInfo._base64 : this._base64; }
set base64(val) { this._base64 = Base64Utils.normalizeToDataUrl(val, "image/png"); }
get pdfCompatibleBase64() { return this._convertedBase64 ?? this.base64; }
get referenceInfo() { return this._referenceInfo; }
set referenceInfo(val) {
this._referenceInfo = val;
this._base64 = undefined;
this._size = undefined;
this._isLoaded = undefined;
this._isSizeDefined = undefined;
this.file = undefined;
}
constructor(base64, actualId, tmpId, imageUrl, file, referenceInfo, size, isLoaded, isActualSize) {
this._base64 = base64 !== undefined ? Base64Utils.normalizeToDataUrl(base64, "image/png") : undefined;
this.actualId = actualId;
this.tmpId = tmpId;
this._referenceInfo = referenceInfo;
this._size = size ? size : CacheImageInfo.emptyPictureSize;
this._isLoaded = isLoaded !== undefined ? isLoaded : false;
this._isSizeDefined = isActualSize ? isActualSize : !!size;
this.imageUrl = imageUrl;
this.file = file;
}
equals(obj) {
return (isDefined(this._referenceInfo) && this._referenceInfo === obj._referenceInfo) ||
this.actualId == obj.actualId &&
this.tmpId == obj.tmpId &&
this.base64 === obj.base64 &&
this.imageUrl === obj.imageUrl &&
this.file === obj.file &&
this.size.equals(obj.size);
}
clone() {
return new CacheImageInfo(this._base64, this.actualId, this.tmpId, this.imageUrl, this.file, this._referenceInfo, this._size, this._isLoaded, this._isSizeDefined);
}
shouldMakeImagePdfCompatible() {
if (isDefined(this._convertedBase64))
return false;
return !this.isPdfCompatible();
}
isPdfCompatible() {
if (isDefined(this._convertedBase64))
return true;
if (isDefined(this.base64)) {
const data = this.getImageHeader();
if (data[0] === 0xff && data[1] === 0xd8)
return true;
else if (data[0] === 0x89 && this.toString(data, 1, 4) === 'PNG')
return true;
}
return false;
}
toString(data, start, end) {
return Array.from(data.slice(start, end)).map(b => String.fromCharCode(b)).join("");
}
setPdfCompatibleBase64(val) {
this._convertedBase64 = Base64Utils.normalizeToDataUrl(val, "image/png");
}
getImageHeader() {
const match = /^data:.+;base64,(.*)$/.exec(this.base64);
const header = match[1].substring(0, 8);
return Uint8Array.from(atob(header), c => c.charCodeAt(0));
}
}
export class ImageCache {
static emptyImageBase64 = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/Pg0KPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgdmlld0JveD0iMCAwIDE2IDE2IiB3aWR0aD0iMTYiIGhlaWdodD0iMTYiPgogIDxwYXRoIHN0eWxlPSJmaWxsOnZhcigtLWR4LWljb24tY29sb3ItcmVkLCAjRUQzRDNCKSIgZD0iTTIuMzE4MDIgNy4zMTgwMkMwLjU2MDY2IDkuMDc1MzggMC41NjA2NiAxMS45MjQ2IDIuMzE4MDIgMTMuNjgyQzQuMDc1MzggMTUuNDM5MyA2LjkyNDYyIDE1LjQzOTMgOC42ODE5OCAxMy42ODJDMTAuNDM5MyAxMS45MjQ2IDEwLjQzOTMgOS4wNzUzOCA4LjY4MTk4IDcuMzE4MDJDNi45MjQ2MiA1LjU2MDY2IDQuMDc1MzggNS41NjA2NiAyLjMxODAyIDcuMzE4MDJaTTcuMjY3NzcgMTIuMjY3OEM3LjA3MjUgMTIuNDYzIDYuNzU1OTIgMTIuNDYzIDYuNTYwNjYgMTIuMjY3OEw1LjUgMTEuMjA3MUw0LjQzOTM0IDEyLjI2NzhDNC4yNDQwOCAxMi40NjMgMy45Mjc1IDEyLjQ2MyAzLjczMjIzIDEyLjI2NzhDMy41MzY5NyAxMi4wNzI1IDMuNTM2OTcgMTEuNzU1OSAzLjczMjIzIDExLjU2MDdMNC43OTI4OSAxMC41TDMuNzMyMjMgOS40MzkzNEMzLjUzNjk3IDkuMjQ0MDggMy41MzY5NyA4LjkyNzUgMy43MzIyMyA4LjczMjIzQzMuOTI3NSA4LjUzNjk3IDQuMjQ0MDggOC41MzY5NyA0LjQzOTM0IDguNzMyMjNMNS41IDkuNzkyODlMNi41NjA2NiA4LjczMjIzQzYuNzU1OTIgOC41MzY5NyA3LjA3MjUgOC41MzY5NyA3LjI2Nzc3IDguNzMyMjNDNy40NjMwMyA4LjkyNzUgNy40NjMwMyA5LjI0NDA4IDcuMjY3NzcgOS40MzkzNEw2LjIwNzExIDEwLjVMNy4yNjc3NyAxMS41NjA3QzcuNDYzMDMgMTEuNzU1OSA3LjQ2MzAzIDEyLjA3MjUgNy4yNjc3NyAxMi4yNjc4WiIvPgogIDxwYXRoIHN0eWxlPSJmaWxsOnZhcigtLWR4LWljb24tY29sb3ItYmxhY2ssICMzQTNBMzgpIiBkPSJNNCAzQzQgMS44OTU0MyA0Ljg5NTQzIDEgNiAxSDkuNTg1NzlDOS45ODM2MSAxIDEwLjM2NTEgMS4xNTgwNCAxMC42NDY0IDEuNDM5MzRMMTMuNTYwNyA0LjM1MzU1QzEzLjg0MiA0LjYzNDg2IDE0IDUuMDE2MzkgMTQgNS40MTQyMVYxM0MxNCAxNC4xMDQ2IDEzLjEwNDYgMTUgMTIgMTVIOC42NjMwOEM5LjA2NjIxIDE0LjcxNjEgOS40Mjk0OCAxNC4zNzk0IDkuNzQyODQgMTRIMTJDMTIuNTUyMyAxNCAxMyAxMy41NTIzIDEzIDEzVjZIMTAuNUM5LjY3MTU3IDYgOSA1LjMyODQzIDkgNC41VjJINkM1LjQ0NzcyIDIgNSAyLjQ0NzcyIDUgM1Y1LjAyMjQyQzQuNjU2NTkgNS4wNTMzNyA0LjMyMjI4IDUuMTE1ODggNCA1LjIwNzAzVjNaTTEwLjUgNUgxMi43OTI5TDEwIDIuMjA3MTFWNC41QzEwIDQuNzc2MTQgMTAuMjIzOSA1IDEwLjUgNVoiLz4KPHNjcmlwdCB4bWxucz0iIi8+PC9zdmc+';
cache;
emptyImageId = 0;
lastTmpId = 0;
lastActualId = 1;
get emptyImage() { return this.cache[this.emptyImageId]; }
constructor() {
this.cache = {};
const emptyImageSize = UnitConverter.pixelsToTwips(16);
const emptyImage = this.createUnloadedInfoByBase64(ImageCache.emptyImageBase64, new Size(emptyImageSize, emptyImageSize));
emptyImage.isLoaded = false;
}
getPictureData(id) {
return this.cache[id];
}
createUnloadedInfoByUrl(imageUrl, size) {
const info = this.findInfoByUrl(imageUrl);
if (info)
return info;
return this.registerPictureData(new CacheImageInfo(this.cache[this.emptyImageId].base64, undefined, this.lastTmpId--, imageUrl, undefined, undefined, this.origSizeCorrect(size) ? size : undefined));
}
createUnloadedInfoByFile(file) {
return this.registerPictureData(new CacheImageInfo(this.cache[this.emptyImageId].base64, undefined, this.lastTmpId--, undefined, file));
}
registerFromAnotherModel(imageInfo) {
imageInfo.isLoaded = false;
imageInfo.actualId = undefined;
imageInfo.base64 = this.cache[this.emptyImageId].base64;
imageInfo.tmpId = this.lastTmpId--;
}
createUnloadedInfoByBase64(base64, size) {
const info = this.findInfoByBase64(base64);
if (info)
return info;
const origSizeCorrect = this.origSizeCorrect(size);
return this.registerPictureData(new CacheImageInfo(base64, undefined, this.lastTmpId--, undefined, undefined, undefined, origSizeCorrect ? size : undefined));
}
createLoadedInfo(base64, size, id) {
const info = this.findInfoByBase64(base64);
if (info) {
return id === undefined || id === info.actualId ?
info :
this.registerPictureData(new CacheImageInfo(undefined, id, undefined, undefined, undefined, info, undefined));
}
if (id === undefined)
id = this.getNextActualId();
return this.registerPictureData(new CacheImageInfo(base64, id, undefined, undefined, undefined, undefined, size, !!size));
}
createUnloadedByBase64OrUrl(data, size) {
return Base64Utils.checkPrependDataUrl(data) ?
this.createUnloadedInfoByBase64(data, size) :
this.createUnloadedInfoByUrl(data, size);
}
finalizeLoading(existingInfo, loadedInfo) {
existingInfo.isLoaded = true;
existingInfo.size = loadedInfo.size;
existingInfo.actualId = loadedInfo.actualId;
if (existingInfo.actualId === undefined)
existingInfo.actualId = this.getNextActualId();
this.registerPictureData(existingInfo);
if (existingInfo.referenceInfo)
return;
const base64 = Base64Utils.normalizeToDataUrl(loadedInfo.base64, "image/png");
if (!NumberMapUtils.containsBy(this.cache, cacheElem => {
const isReference = cacheElem.base64 == base64 && cacheElem !== existingInfo && cacheElem.isLoaded;
if (isReference)
existingInfo.referenceInfo = cacheElem.referenceInfo ? cacheElem.referenceInfo : cacheElem;
return isReference;
}))
existingInfo.base64 = base64;
}
getNextActualId() {
return this.lastActualId++;
}
isDataRegistered(data) {
return !!this.cache[data.actualId] || !!this.cache[data.tmpId];
}
registerPictureData(data) {
let existingData = this.cache[data.actualId];
if (!existingData)
existingData = this.cache[data.tmpId];
if (!existingData)
existingData = data;
if (data.actualId !== undefined)
this.cache[data.actualId] = existingData;
if (data.tmpId !== undefined)
this.cache[data.tmpId] = existingData;
return existingData;
}
loadAllPictures(picture) {
NumberMapUtils.forEach(this.cache, cacheInfo => {
if (this.emptyImageId != cacheInfo.actualId)
picture.loader.load(cacheInfo);
});
}
findInfoByBase64(base64) {
base64 = Base64Utils.normalizeToDataUrl(base64, "image/png");
return NumberMapUtils.elementBy(this.cache, info => info.base64 == base64);
}
findInfoByUrl(imageUrl) {
return NumberMapUtils.elementBy(this.cache, info => info.imageUrl == imageUrl);
}
origSizeCorrect(size) {
return size && !!size.width && !!size.height;
}
clone() {
const result = new ImageCache();
result.lastTmpId = this.lastTmpId;
result.lastActualId = this.lastActualId;
result.cache = NumberMapUtils.deepCopy(this.cache);
NumberMapUtils.forEach(result.cache, el => {
if (el.referenceInfo)
el.referenceInfo = result.cache[el.currId];
});
return result;
}
}