diff --git a/HEADER.js b/HEADER.js index c17e9670f90..7909f2e408e 100644 --- a/HEADER.js +++ b/HEADER.js @@ -1,11 +1,12 @@ -/*! Fabric.js Copyright 2008-2015, Printio (Juriy Zaytsev, Maxim Chernyak) */ +import { cache } from './src/cache'; import { config } from './src/config'; import { iMatrix, VERSION } from './src/constants'; var fabric = fabric || { version: VERSION, config, + cache, iMatrix }; @@ -88,28 +89,6 @@ config.configure({ 1 }); -/** - * Cache Object for widths of chars in text rendering. - */ -fabric.charWidthsCache = {}; - -/** - * This object contains the result of arc to bezier conversion for faster retrieving if the same arc needs to be converted again. - * It was an internal variable, is accessible since version 2.3.4 - */ -fabric.arcToSegmentsCache = { }; - -/** - * This object keeps the results of the boundsOfCurve calculation mapped by the joined arguments necessary to calculate it. - * It does speed up calculation, if you parse and add always the same paths, but in case of heavy usage of freedrawing - * you do not get any speed benefit and you get a big object in memory. - * The object was a private variable before, while now is appended to the lib so that you have access to it and you - * can eventually clear it. - * It was an internal variable, is accessible since version 2.3.4 - */ -fabric.boundsOfCurveCache = { }; - - fabric.initFilterBackend = function() { if (config.enableGLFiltering && fabric.isWebglSupported && fabric.isWebglSupported(config.textureSize)) { console.log(`fabric: max texture size: ${config.maxTextureSize}`); diff --git a/src/cache.ts b/src/cache.ts new file mode 100644 index 00000000000..ff56089cbf8 --- /dev/null +++ b/src/cache.ts @@ -0,0 +1,64 @@ + +export class Cache { + /** + * Cache of widths of chars in text rendering. + */ + charWidthsCache: Record>> = {} + + /** + * @return {Object} reference to cache + */ + getFontCache({ fontFamily, fontStyle, fontWeight }: { fontFamily: string, fontStyle: string, fontWeight: string | number }) { + fontFamily = fontFamily.toLowerCase(); + if (!this.charWidthsCache[fontFamily]) { + this.charWidthsCache[fontFamily] = {}; + } + const fontCache = this.charWidthsCache[fontFamily]; + const cacheKey = `${fontStyle.toLowerCase()}_${(fontWeight + '').toLowerCase()}`; + if (!fontCache[cacheKey]) { + fontCache[cacheKey] = {}; + } + return fontCache[cacheKey]; + } + + + /** + * Clear char widths cache for the given font family or all the cache if no + * fontFamily is specified. + * Use it if you know you are loading fonts in a lazy way and you are not waiting + * for custom fonts to load properly when adding text objects to the canvas. + * If a text object is added when its own font is not loaded yet, you will get wrong + * measurement and so wrong bounding boxes. + * After the font cache is cleared, either change the textObject text content or call + * initDimensions() to trigger a recalculation + * @memberOf fabric.util + * @param {String} [fontFamily] font family to clear + */ + clearFontCache(fontFamily?: string) { + fontFamily = (fontFamily || '').toLowerCase(); + if (!fontFamily) { + this.charWidthsCache = {}; + } + else if (this.charWidthsCache[fontFamily]) { + delete this.charWidthsCache[fontFamily]; + } + } + + /** + * This object contains the result of arc to bezier conversion for faster retrieving if the same arc needs to be converted again. + * It was an internal variable, is accessible since version 2.3.4 + */ + arcToSegmentsCache = {} + + /** + * This object keeps the results of the boundsOfCurve calculation mapped by the joined arguments necessary to calculate it. + * It does speed up calculation, if you parse and add always the same paths, but in case of heavy usage of freedrawing + * you do not get any speed benefit and you get a big object in memory. + * The object was a private variable before, while now is appended to the lib so that you have access to it and you + * can eventually clear it. + * It was an internal variable, is accessible since version 2.3.4 + */ + boundsOfCurveCache = {} +} + +export const cache = new Cache(); diff --git a/src/shapes/text.class.ts b/src/shapes/text.class.ts index c8a90f46486..3ceb33bd871 100644 --- a/src/shapes/text.class.ts +++ b/src/shapes/text.class.ts @@ -1,5 +1,6 @@ //@ts-nocheck +import { cache } from "../cache"; import { DEFAULT_SVG_FONT_SIZE } from "../constants"; @@ -695,27 +696,6 @@ import { DEFAULT_SVG_FONT_SIZE } from "../constants"; this._removeShadow(ctx); }, - /** - * @private - * @param {Object} decl style declaration for cache - * @param {String} decl.fontFamily fontFamily - * @param {String} decl.fontStyle fontStyle - * @param {String} decl.fontWeight fontWeight - * @return {Object} reference to cache - */ - getFontCache: function(decl) { - var fontFamily = decl.fontFamily.toLowerCase(); - if (!fabric.charWidthsCache[fontFamily]) { - fabric.charWidthsCache[fontFamily] = { }; - } - var cache = fabric.charWidthsCache[fontFamily], - cacheProp = decl.fontStyle.toLowerCase() + '_' + (decl.fontWeight + '').toLowerCase(); - if (!cache[cacheProp]) { - cache[cacheProp] = { }; - } - return cache[cacheProp]; - }, - /** * measure and return the width of a single character. * possibly overridden to accommodate different measure logic or @@ -728,7 +708,7 @@ import { DEFAULT_SVG_FONT_SIZE } from "../constants"; */ _measureChar: function(_char, charStyle, previousChar, prevCharStyle) { // first i try to return from cache - var fontCache = this.getFontCache(charStyle), fontDeclaration = this._getFontDeclaration(charStyle), + var fontCache = cache.getFontCache(charStyle), fontDeclaration = this._getFontDeclaration(charStyle), previousFontDeclaration = this._getFontDeclaration(prevCharStyle), couple = previousChar + _char, stylesAreEqual = fontDeclaration === previousFontDeclaration, width, coupleWidth, previousWidth, fontMultiplier = charStyle.fontSize / this.CACHE_FONT_SIZE, kernedWidth; diff --git a/src/util/misc/misc.ts b/src/util/misc/misc.ts index 705907ed936..f532c8bcded 100644 --- a/src/util/misc/misc.ts +++ b/src/util/misc/misc.ts @@ -227,28 +227,6 @@ import { return _isTransparent; }, - /** - * Clear char widths cache for the given font family or all the cache if no - * fontFamily is specified. - * Use it if you know you are loading fonts in a lazy way and you are not waiting - * for custom fonts to load properly when adding text objects to the canvas. - * If a text object is added when its own font is not loaded yet, you will get wrong - * measurement and so wrong bounding boxes. - * After the font cache is cleared, either change the textObject text content or call - * initDimensions() to trigger a recalculation - * @memberOf fabric.util - * @param {String} [fontFamily] font family to clear - */ - clearFabricFontCache: function(fontFamily) { - fontFamily = (fontFamily || '').toLowerCase(); - if (!fontFamily) { - fabric.charWidthsCache = { }; - } - else if (fabric.charWidthsCache[fontFamily]) { - delete fabric.charWidthsCache[fontFamily]; - } - }, - /** * Given current aspect ratio, determines the max width and height that can * respect the total allowed area for the cache. diff --git a/src/util/path.ts b/src/util/path.ts index 883eb49c0b0..8a2988f3b6f 100644 --- a/src/util/path.ts +++ b/src/util/path.ts @@ -1,4 +1,6 @@ //@ts-nocheck + +import { cache } from "../cache"; import { config } from "../config"; import { fabric } from '../../HEADER'; import { halfPI, PiBy180 } from "../constants"; @@ -137,8 +139,8 @@ export function getBoundsOfCurve(x0, y0, x1, y1, x2, y2, x3, y3) { if (config.cachesBoundsOfCurve) { // eslint-disable-next-line argsString = [...arguments].join(); - if (fabric.boundsOfCurveCache[argsString]) { - return fabric.boundsOfCurveCache[argsString]; + if (cache.boundsOfCurveCache[argsString]) { + return cache.boundsOfCurveCache[argsString]; } } @@ -206,7 +208,7 @@ export function getBoundsOfCurve(x0, y0, x1, y1, x2, y2, x3, y3) { } ]; if (config.cachesBoundsOfCurve) { - fabric.boundsOfCurveCache[argsString] = result; + cache.boundsOfCurveCache[argsString] = result; } return result; } diff --git a/test/unit/text.js b/test/unit/text.js index 03690ac24f3..779621fd248 100644 --- a/test/unit/text.js +++ b/test/unit/text.js @@ -390,7 +390,7 @@ QUnit.test('getFontCache works with fontWeight numbers', function(assert) { var text = new fabric.Text('xxx', { fontWeight: 400 }); text.initDimensions(); - var cache = fabric.charWidthsCache[text.fontFamily.toLowerCase()]; + var cache = fabric.cache.charWidthsCache[text.fontFamily.toLowerCase()]; var cacheProp = text.fontStyle + '_400'; assert.equal(cacheProp in cache, true, '400 is converted to string'); }); @@ -400,8 +400,8 @@ text.initDimensions(); var text2 = new fabric.Text('xxx', { fontWeight: 'bOLd', fontStyle: 'nORMAl' }); text2.initDimensions(); - var cache = text.getFontCache(text); - var cache2 = text2.getFontCache(text2); + var cache = fabric.cache.getFontCache(text); + var cache2 = fabric.cache.getFontCache(text2); assert.equal(cache, cache2, 'you get the same cache'); }); // moved @@ -741,13 +741,13 @@ }); QUnit.test('_measureChar handles 0 width chars', function(assert) { - fabric.charWidthsCache = {}; + fabric.cache.clearFontCache(); var zwc = '\u200b'; var text = new fabric.Text(''); var style = text.getCompleteStyleDeclaration(0, 0); var box = text._measureChar('a', style, zwc, style); var box2 = text._measureChar('a', style, zwc, style); - assert.equal(fabric.charWidthsCache[text.fontFamily.toLowerCase()].normal_normal[zwc], 0, 'zwc is a 0 width char'); + assert.equal(fabric.cache.charWidthsCache[text.fontFamily.toLowerCase()].normal_normal[zwc], 0, 'zwc is a 0 width char'); assert.equal(box.kernedWidth, box2.kernedWidth, '2 measurements of the same string return the same number'); }); diff --git a/test/unit/util.js b/test/unit/util.js index 7bf474c37ca..4e0e43cd85e 100644 --- a/test/unit/util.js +++ b/test/unit/util.js @@ -701,21 +701,21 @@ assert.equal(fabric.util.getKlass('Sepia2', 'fabric.Image.filters'), fabric.Image.filters.Sepia2); }); - QUnit.test('clearFabricFontCache', function(assert) { - assert.ok(typeof fabric.util.clearFabricFontCache === 'function'); - fabric.charWidthsCache = { arial: { some: 'cache'}, helvetica: { some: 'cache'} }; - fabric.util.clearFabricFontCache('arial'); - assert.equal(fabric.charWidthsCache.arial, undefined, 'arial cache is deleted'); - assert.equal(fabric.charWidthsCache.helvetica.some, 'cache', 'helvetica cache is still available'); - fabric.util.clearFabricFontCache(); - assert.deepEqual(fabric.charWidthsCache, { }, 'all cache is deleted'); - }); - - QUnit.test('clearFabricFontCache wrong case', function(assert) { - fabric.charWidthsCache = { arial: { some: 'cache'}, helvetica: { some: 'cache'} }; - fabric.util.clearFabricFontCache('ARIAL'); - assert.equal(fabric.charWidthsCache.arial, undefined, 'arial cache is deleted'); - assert.equal(fabric.charWidthsCache.helvetica.some, 'cache', 'helvetica cache is still available'); + QUnit.test('clearFontCache', function(assert) { + assert.ok(typeof fabric.cache.clearFontCache === 'function'); + fabric.cache.charWidthsCache = { arial: { some: 'cache'}, helvetica: { some: 'cache'} }; + fabric.cache.clearFontCache('arial'); + assert.equal(fabric.cache.charWidthsCache.arial, undefined, 'arial cache is deleted'); + assert.equal(fabric.cache.charWidthsCache.helvetica.some, 'cache', 'helvetica cache is still available'); + fabric.cache.clearFontCache(); + assert.deepEqual(fabric.cache.charWidthsCache, { }, 'all cache is deleted'); + }); + + QUnit.test('clearFontCache wrong case', function(assert) { + fabric.cache.charWidthsCache = { arial: { some: 'cache'}, helvetica: { some: 'cache'} }; + fabric.cache.clearFontCache('ARIAL'); + assert.equal(fabric.cache.charWidthsCache.arial, undefined, 'arial cache is deleted'); + assert.equal(fabric.cache.charWidthsCache.helvetica.some, 'cache', 'helvetica cache is still available'); }); QUnit.test('parsePreserveAspectRatioAttribute', function(assert) {