devexpress-richedit
Version:
DevExpress Rich Text Editor is an advanced word-processing tool designed for working with rich text documents.
193 lines (192 loc) • 8.94 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 get emptyPictureSize() { return new Size(CacheImageInfo.emptyPicDimension, CacheImageInfo.emptyPicDimension); }
get isLoaded() { return this._referenceInfo ? this._referenceInfo._isLoaded : this._isLoaded; }
set isLoaded(val) { this._isLoaded = 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() { var _a; return (_a = this._convertedBase64) !== null && _a !== void 0 ? _a : this.base64; }
get referenceInfo() { return this._referenceInfo; }
set referenceInfo(val) {
this._referenceInfo = val;
this._base64 = undefined;
this._size = undefined;
this._isLoaded = undefined;
this.file = undefined;
}
constructor(base64, actualId, tmpId, imageUrl, file, referenceInfo, size, isLoaded) {
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.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);
}
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));
}
}
CacheImageInfo.emptyPicDimension = UnitConverter.pixelsToTwips(32);
export class ImageCache {
get emptyImage() { return this.cache[this.emptyImageId]; }
constructor() {
this.emptyImageId = 0;
this.lastTmpId = 0;
this.lastActualId = 1;
this.cache = {};
const onePixelSize = UnitConverter.pixelsToTwips(1);
const emptyImage = this.createUnloadedInfoByBase64(ImageCache.transparentWhiteImage1_1, new Size(onePixelSize, onePixelSize));
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;
}
}
ImageCache.transparentWhiteImage1_1 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAANSURBVBhXY/j///9/AAn7A/0FQ0XKAAAAAElFTkSuQmCC';