/* * (c) Copyright Ascensio System SIA 2010-2024 * * This program is a free software product. You can redistribute it and/or * modify it under the terms of the GNU Affero General Public License (AGPL) * version 3 as published by the Free Software Foundation. In accordance with * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect * that Ascensio System SIA expressly excludes the warranty of non-infringement * of any third-party rights. * * This program is distributed WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html * * You can contact Ascensio System SIA at 20A-6 Ernesta Birznieka-Upish * street, Riga, Latvia, EU, LV-1050. * * The interactive user interfaces in modified source and object code versions * of the Program must display Appropriate Legal Notices, as required under * Section 5 of the GNU AGPL version 3. * * Pursuant to Section 7(b) of the License you must retain the original Product * logo when distributing the program. Pursuant to Section 7(e) we decline to * grant you any rights under trademark law for use of our trademarks. * * All the Product's GUI elements, including illustrations and icon sets, as * well as technical writing content are licensed under the terms of the * Creative Commons Attribution-ShareAlike 4.0 International. See the License * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode * */ "use strict"; (function (window, undefined) { //window.measureTime = 0; //window.rasterTime = 0; var AscFonts = window['AscFonts']; var AscCommon = window['AscCommon']; AscFonts.FT_Load_Mode = { FT_LOAD_DEFAULT : 0, FT_LOAD_NO_SCALE : 1 << 0, FT_LOAD_NO_HINTING : 1 << 1, FT_LOAD_RENDER : 1 << 2, FT_LOAD_NO_BITMAP : 1 << 3, FT_LOAD_VERTICAL_LAYOUT : 1 << 4, FT_LOAD_FORCE_AUTOHINT : 1 << 5, FT_LOAD_CROP_BITMAP : 1 << 6, FT_LOAD_PEDANTIC : 1 << 7, FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH : 1 << 9, FT_LOAD_NO_RECURSE : 1 << 10, FT_LOAD_IGNORE_TRANSFORM : 1 << 11, FT_LOAD_MONOCHROME : 1 << 12, FT_LOAD_LINEAR_DESIGN : 1 << 13, FT_LOAD_NO_AUTOHINT : 1 << 15, FT_LOAD_COLOR : 1 << 20, FT_LOAD_COMPUTE_METRICS : 1 << 21, FT_LOAD_BITMAP_METRICS_ONLY : 1 << 22 }; AscFonts.FT_Render_Mode = { FT_RENDER_MODE_NORMAL : 0, FT_RENDER_MODE_LIGHT : 1, FT_RENDER_MODE_MONO : 2, FT_RENDER_MODE_LCD : 3, FT_RENDER_MODE_LCD_V : 4, FT_RENDER_MODE_MAX : 5 }; function FT_LOAD_TARGET(val) { return (val & 15) << 16; } AscFonts.FT_Load_Target_Mode = { FT_LOAD_TARGET_NORMAL : FT_LOAD_TARGET(AscFonts.FT_Render_Mode.FT_RENDER_MODE_NORMAL), FT_LOAD_TARGET_LIGHT : FT_LOAD_TARGET(AscFonts.FT_Render_Mode.FT_RENDER_MODE_LIGHT), FT_LOAD_TARGET_MONO : FT_LOAD_TARGET(AscFonts.FT_Render_Mode.FT_RENDER_MODE_MONO), FT_LOAD_TARGET_LCD : FT_LOAD_TARGET(AscFonts.FT_Render_Mode.FT_RENDER_MODE_LCD), FT_LOAD_TARGET_LCD_V : FT_LOAD_TARGET(AscFonts.FT_Render_Mode.FT_RENDER_MODE_LCD_V) }; AscFonts.LOAD_MODE_DEFAULT = AscFonts.FT_Load_Mode.FT_LOAD_DEFAULT | AscFonts.FT_Load_Mode.FT_LOAD_NO_HINTING | AscFonts.FT_Load_Mode.FT_LOAD_LINEAR_DESIGN | AscFonts.FT_Load_Mode.FT_LOAD_NO_AUTOHINT; AscFonts.LOAD_MODE_HINTING = AscFonts.FT_Load_Mode.FT_LOAD_DEFAULT | AscFonts.FT_Load_Mode.FT_LOAD_LINEAR_DESIGN | AscFonts.FT_Load_Mode.FT_LOAD_NO_AUTOHINT; AscFonts.isUseBitmapStrikes = function(symbol) { if (!AscFonts.mRanges) { AscFonts.mRanges = []; AscFonts.mRanges.push(0x1100); AscFonts.mRanges.push(0x11FF); AscFonts.mRanges.push(0x2E80); AscFonts.mRanges.push(0x9FFF); AscFonts.mRanges.push(0xAC00); AscFonts.mRanges.push(0xD7AF); AscFonts.mRanges.push(0xF900); AscFonts.mRanges.push(0xFAFF); AscFonts.mRanges.push(0xFF00); AscFonts.mRanges.push(0xFFEF); AscFonts.mRanges.push(0x20000); AscFonts.mRanges.push(0x2A6DF); AscFonts.mRanges.push(0x2F800); AscFonts.mRanges.push(0x2FA1F); } if (symbol < AscFonts.mRanges[0]) return false; var _m = AscFonts.mRanges; var _l = AscFonts.mRanges.length; for (var i = 0; i < _l; i += 2) { if (symbol >= _m[i] && symbol <= _m[i + 1]) return true; } return false; }; var raster_memory = AscFonts.raster_memory; var FONT_ITALIC_ANGLE = 0.3090169943749; var REND_MODE = AscFonts.FT_Render_Mode.FT_RENDER_MODE_NORMAL; var EGlyphState = { glyphstateNormal : 0, // символ отрисовался в нужном шрифте glyphstateDefault : 1, // символ отрисовался в дефолтовом шрифте glyphstateMiss : 2 // символ не отрисовался }; function get_raster_bounds_safe(rasterBitmap) { if (!rasterBitmap) return {dist_l: 0, dist_t: 0, dist_r: 0, dist_b: 0}; return get_raster_bounds(rasterBitmap.data, rasterBitmap.width, rasterBitmap.rows, rasterBitmap.pitch); } function get_raster_bounds(data, width, height, stride) { var ret = {dist_l: 0, dist_t: 0, dist_r: 0, dist_b: 0}; // left var bIsBreak = false; for (var i = 0; i < width; i++) { var _ind = i * 4 + 3; for (var j = 0; j < height; j++, _ind += stride) { if (data[_ind] != 0) { bIsBreak = true; break; } } if (bIsBreak) break; ret.dist_l++; } // right bIsBreak = false; for (var i = width - 1; i >= 0; i--) { var _ind = i * 4 + 3; for (var j = 0; j < height; j++, _ind += stride) { if (data[_ind] != 0) { bIsBreak = true; break; } } if (bIsBreak) break; ret.dist_r++; } // top var bIsBreak = false; for (var j = 0; j < height; j++) { var _ind = j * stride + 3; for (var i = 0; i < width; i++, _ind += 4) { if (data[_ind] != 0) { bIsBreak = true; break; } } if (bIsBreak) break; ret.dist_t++; } // bottom var bIsBreak = false; for (var j = height - 1; j >= 0; j--) { var _ind = j * stride + 3; for (var i = 0; i < width; i++, _ind += 4) { if (data[_ind] != 0) { bIsBreak = true; break; } } if (bIsBreak) break; ret.dist_b++; } // clear if (null != raster_memory.m_oBuffer) { var nIndexDst = 3; var nPitch = 4 * (raster_memory.width - width); var dst = raster_memory.m_oBuffer.data; for (var j = 0; j < height; j++) { for (var i = 0; i < width; i++) { dst[nIndexDst] = 0; nIndexDst += 4; } nIndexDst += nPitch; } } return ret; } function CGlyphData() { this.m_oCanvas = null; this.m_oContext = null; this.R = 0; this.G = 0; this.B = 0; this.RasterData = null; this.TempImage = null; } CGlyphData.prototype = { // not used (old devices) checkColorAppleDevices : function(r, g, b, w, h) { if ((r == this.R) && (g == this.G) && (b == this.B)) return; this.R = r; this.G = g; this.B = b; // full repaint this.TempImage = document.createElement("canvas"); this.TempImage.width = w; this.TempImage.height = h; var ctxD = AscCommon.AscBrowser.getContext2D(this.TempImage); var pixDst = null; if (this.m_oCanvas != null) { pixDst = this.m_oContext.getImageData(0, 0, w, h); var dataPx = pixDst.data; var cur = 0; var cnt = w * h; for (var i = 0; i < cnt; i++) { dataPx[cur++] = r; dataPx[cur++] = g; dataPx[cur++] = b; cur++; } } else { var _raster = this.RasterData; var _x = _raster.Line.Height * _raster.Index; var _y = _raster.Line.Y; pixDst = _raster.Chunk.CanvasCtx.getImageData(_x, _y, w, h); var dataPx = pixDst.data; var cur = 0; var cnt = w * h; for (var i = 0; i < cnt; i++) { dataPx[cur++] = r; dataPx[cur++] = g; dataPx[cur++] = b; cur++; } } ctxD.putImageData(pixDst, 0, 0, 0, 0, w, h); }, checkColorMozillaLinux : function(r, g, b, w, h) { if ((r == this.R) && (g == this.G) && (b == this.B)) return; this.R = r; this.G = g; this.B = b; if (this.m_oCanvas != null) { this.m_oContext.fillStyle = (this.R == 0xFF && this.G == 0xFF && this.B == 0xFF) ? "rgb(255,255,254)" : "rgb(" + this.R + "," + this.G + "," + this.B + ")"; this.m_oContext.fillRect(0, 0, w, h); } else { var _raster = this.RasterData; _raster.Chunk.CanvasCtx.fillStyle = (this.R == 0xFF && this.G == 0xFF && this.B == 0xFF) ? "rgb(255,255,254)" : "rgb(" + this.R + "," + this.G + "," + this.B + ")"; var _x = _raster.Line.Height * _raster.Index; var _y = _raster.Line.Y; this.RasterData.Chunk.CanvasCtx.fillRect(_x, _y, w, h); } }, checkColorNormal : function(r, g, b, w, h) { if ((r == this.R) && (g == this.G) && (b == this.B)) return; this.R = r; this.G = g; this.B = b; if (this.m_oCanvas != null) { this.m_oContext.fillStyle = "rgb(" + this.R + "," + this.G + "," + this.B + ")"; this.m_oContext.fillRect(0, 0, w, h); } else { var _raster = this.RasterData; _raster.Chunk.CanvasCtx.fillStyle = "rgb(" + this.R + "," + this.G + "," + this.B + ")"; var _x = _raster.Line.Height * _raster.Index; var _y = _raster.Line.Y; this.RasterData.Chunk.CanvasCtx.fillRect(_x, _y, w, h); } }, checkColor : undefined, init: function (width, height) { if (width == 0 || height == 0) return; this.m_oCanvas = document.createElement('canvas'); this.m_oCanvas.width = (width == 0) ? 1 : width; this.m_oCanvas.height = (height == 0) ? 1 : height; this.m_oContext = AscCommon.AscBrowser.getContext2D(this.m_oCanvas); this.m_oContext.globalCompositeOperation = "source-in"; } }; CGlyphData.prototype.checkColor = (AscCommon.AscBrowser.isMozilla && AscCommon.AscBrowser.isLinuxOS) ? CGlyphData.prototype.checkColorMozillaLinux : CGlyphData.prototype.checkColorNormal; function CGlyphBitmap() { this.nX = 0; // Сдвиг по X начальной точки для рисования символа this.nY = 0; // Сдвиг по Y начальной точки для рисования символа this.nWidth = 0; // Ширина символа this.nHeight = 0; // Высота символа this.oGlyphData = new CGlyphData(); } CGlyphBitmap.prototype = { fromAlphaMask: function (font_manager) { var bIsCanvas = false; var _chunk_size = (font_manager.RasterMemory == null) ? 0 : font_manager.RasterMemory.ChunkHeapSize; if (Math.max(this.nWidth, this.nHeight) > (_chunk_size / 10)) bIsCanvas = true; var _x = 0; var _y = 0; var ctx = null; if (bIsCanvas) { this.oGlyphData.init(this.nWidth, this.nHeight); ctx = this.oGlyphData.m_oContext; } else { this.oGlyphData.RasterData = font_manager.RasterMemory.Alloc(this.nWidth, this.nHeight); ctx = this.oGlyphData.RasterData.Chunk.CanvasCtx; _x = this.oGlyphData.RasterData.Line.Height * this.oGlyphData.RasterData.Index; _y = this.oGlyphData.RasterData.Line.Y; } if (true) { if (this.nWidth > 0 && this.nHeight > 0) ctx.putImageData(raster_memory.m_oBuffer, _x, _y, 0, 0, this.nWidth, this.nHeight); } else { var gamma = 1.1; var nIndexDst = 3; var nPitch = 4 * (raster_memory.width - this.nWidth); var dst = raster_memory.m_oBuffer.data; for (var j = 0; j < this.nHeight; j++) { for (var i = 0; i < this.nWidth; i++) { dst[nIndexDst] = Math.min((dst[nIndexDst] * gamma) >> 0, 255); nIndexDst += 4; } nIndexDst += nPitch; } if (this.nWidth > 0 && this.nHeight > 0) ctx.putImageData(raster_memory.m_oBuffer, _x, _y, 0, 0, this.nWidth, this.nHeight); } if (null != raster_memory.m_oBuffer) { var nIndexDst = 3; var nPitch = 4 * (raster_memory.width - this.nWidth); var dst = raster_memory.m_oBuffer.data; for (var j = 0; j < this.nHeight; j++) { for (var i = 0; i < this.nWidth; i++) { dst[nIndexDst] = 0; nIndexDst += 4; } nIndexDst += nPitch; } } }, draw: function (context2D, x, y) { var nW = this.nWidth; var nH = this.nHeight; if (null != this.oGlyphData.TempImage) { context2D.drawImage(this.oGlyphData.TempImage, 0, 0, nW, nH, x, y, nW, nH); this.oGlyphData.TempImage = null; } else if (null != this.oGlyphData.m_oCanvas) { // своя память context2D.drawImage(this.oGlyphData.m_oCanvas, 0, 0, nW, nH, x, y, nW, nH); } else { var _raster = this.oGlyphData.RasterData; var _x = _raster.Line.Height * _raster.Index; var _y = _raster.Line.Y; context2D.drawImage(_raster.Chunk.CanvasImage, _x, _y, nW, nH, x, y, nW, nH); } }, drawCrop: function (context2D, x, y, w, h, cx) { if (null != this.oGlyphData.TempImage) { context2D.drawImage(this.oGlyphData.TempImage, cx, 0, w, h, x, y, w, h); this.oGlyphData.TempImage = null; } else if (null != this.oGlyphData.m_oCanvas) { // своя память context2D.drawImage(this.oGlyphData.m_oCanvas, cx, 0, w, h, x, y, w, h); } else { var _raster = this.oGlyphData.RasterData; var _x = _raster.Line.Height * _raster.Index; var _y = _raster.Line.Y; context2D.drawImage(_raster.Chunk.CanvasImage, _x + cx, _y, w, h, x, y, w, h); } }, drawCropInRect: function (context2D, x, y, clipRect) { var _x = x; var _y = y; var _r = x + this.nWidth; var _b = y + this.nHeight; var _dstX = 0; var _dstY = 0; var _dstW = this.nWidth; var _dstH = this.nHeight; if (_x < clipRect.l) { _dstX = clipRect.l - _x; _x += _dstX; _dstW -= _dstX; } if (_y < clipRect.t) { _dstY = clipRect.t - _y; _y += _dstY; _dstH -= _dstY; } if (_r > clipRect.r) { _dstW -= (_r - clipRect.r); } if (_b > clipRect.b) { _dstH -= (_b - clipRect.b); } if (_dstW <= 0 || _dstH <= 0) return; if (null != this.oGlyphData.TempImage) { context2D.drawImage(this.oGlyphData.TempImage, _dstX, _dstY, _dstW, _dstH, _x, _y, _dstW, _dstH); this.oGlyphData.TempImage = null; } else if (null != this.oGlyphData.m_oCanvas) { // своя память context2D.drawImage(this.oGlyphData.m_oCanvas, _dstX, _dstY, _dstW, _dstH, _x, _y, _dstW, _dstH); } else { var _raster = this.oGlyphData.RasterData; var __x = _raster.Line.Height * _raster.Index; var __y = _raster.Line.Y; context2D.drawImage(_raster.Chunk.CanvasImage, __x + _dstX, __y + _dstY, _dstW, _dstH, _x, _y, _dstW, _dstH); } }, Free: function () { if (null != this.oGlyphData.RasterData) { this.oGlyphData.RasterData.Chunk.Free(this.oGlyphData.RasterData); } } }; function CBBox() { this.fMinX = 0; this.fMaxX = 0; this.fMinY = 0; this.fMaxY = 0; this.rasterDistances = null; } function CMetrics() { this.fWidth = 0; this.fHeight = 0; this.fHoriBearingX = 0; this.fHoriBearingY = 0; this.fHoriAdvance = 0; this.fVertBearingX = 0; this.fVertBearingY = 0; this.fVertAdvance = 0; } function CFontCacheSizes() { this.ushUnicode; // Значение символа в юникоде this.eState; // Есть ли символ в шрифте/стандартном шрифте this.nCMapIndex; // Номер таблицы 'cmap', в которой был найден данный символ this.ushGID; this.fAdvanceX; this.oBBox = new CBBox(); this.oMetrics = new CMetrics(); this.bBitmap = false; this.oBitmap = null; } function CCMapIndex() { this.index = 0; } function CGlyphVectorPainter() { // сдвиг this.X = 0; this.Y = 0; // scale this.KoefX = 25.4 / 72; this.KoefY = 25.4 / 72; this.NeedClosed = false; this.shift = 0; this.delta = 0; this.CurX = 0; this.CurY = 0; } CGlyphVectorPainter.prototype = { start: function () { }, _move_to: function(x, y, worker) { if (this.NeedClosed) { worker._z(); this.NeedClosed = false; } this.CurX = this.X + this.KoefX * (x / 64.0); this.CurY = this.Y - this.KoefY * (y / 64.0); worker._m(this.CurX, this.CurY); return 0; }, move_to: function (to, worker) { return this._move_to(to.x, to.y, worker); }, _line_to: function (x, y, worker) { this.CurX = this.X + this.KoefX * (x / 64.0); this.CurY = this.Y - this.KoefY * (y / 64.0); worker._l(this.CurX, this.CurY); this.NeedClosed = true; return 0; }, line_to: function (to, worker) { return this._line_to(to.x, to.y, worker); }, _conic_to: function (control_x, control_y, to_x, to_y, worker) { var dX0 = this.CurX; var dY0 = this.CurY; var dXc = this.X + this.KoefX * (control_x / 64.0); var dYc = this.Y - this.KoefY * (control_y / 64.0); var dX3 = this.X + this.KoefX * (to_x / 64.0); var dY3 = this.Y - this.KoefY * (to_y / 64.0); // Строим кривую Безье второго порядка, с помощью кривой Безье третего порядка. Если p0, pC, p3 - // начальная, контрольная и конечная точки, соответственно, для кривой Безье второго порядка. Тогда // для этой же кривой, рассматриваемой как кривая Безье третьего порядка, точки p0, p1, p2, p3 будут // начальной, две контрольные, конечная точки. Где p1 и p2 рассчитываются по следующим формулам: // p1 = (1/3) * (p0 + 2pС) // p2 = (1/3) * (2pС + p3) var dX1 = (1.0 / 3.0) * (dX0 + 2 * dXc); var dY1 = (1.0 / 3.0) * (dY0 + 2 * dYc); var dX2 = (1.0 / 3.0) * (2 * dXc + dX3); var dY2 = (1.0 / 3.0) * (2 * dYc + dY3); worker._c(dX1, dY1, dX2, dY2, dX3, dY3); this.CurX = dX3; this.CurY = dY3; this.NeedClosed = true; return 0; }, conic_to: function (control, to, worker) { return this._conic_to(control.x, control.y, to.x, to.y, worker); }, _cubic_to: function (control1_x, control1_y, control2_x, control2_y, to_x, to_y, worker) { this.CurX = this.X + this.KoefX * (to_x / 64.0); this.CurY = this.Y - this.KoefY * (to_y / 64.0); worker._c( this.X + this.KoefX * (control1_x / 64.0), this.Y - this.KoefY * (control1_y / 64.0), this.X + this.KoefX * (control2_x / 64.0), this.Y - this.KoefY * (control2_y / 64.0), this.CurX, this.CurY); this.NeedClosed = true; return 0; }, cubic_to: function (control1, control2, to, worker) { return this._cubic_to(control1.x, control1.y, control2.x, control2.y, to.x, to.y, worker); }, end: function (worker) { if (this.NeedClosed) { worker._z(); this.NeedClosed = false; } } }; function CFontFile() { this.m_arrdFontMatrix = ("undefined" == typeof Float64Array) ? new Array(6) : new Float64Array(6); this.m_arrdTextMatrix = ("undefined" == typeof Float64Array) ? new Array(6) : new Float64Array(6); this.m_bAntiAliasing = true; this.m_bUseKerning = false; this.m_fSize = 1.0; // Размер шрифта this.m_unHorDpi = 0; // Горизонтальное разрешение this.m_unVerDpi = 0; // Вертикальное разрешение this.m_bNeedDoItalic = false; this.m_bNeedDoBold = false; this.m_fCharSpacing = 0.0; this.m_oBox = new AscFonts.CGlyphBounds(); // Glyph box this.m_nError = 0; this.m_pFace = null; this.m_pFaceInfo = null; this.m_pHBFont = 0; this.m_dUnitsKoef = 1.0; this.m_nDefaultChar = -1; this.m_nSymbolic = -1; this.m_dTextScale = 0; this.m_bStringGID = false; this.m_nNum_charmaps = 0; this.m_lAscender = 0; this.m_lDescender = 0; this.m_lLineHeight = 0; this.m_lUnits_Per_Em = 0; this.m_arrCacheSizes = []; this.m_arrCacheSizesGid = []; this.m_oFontManager = null; this.HintsSupport = true; this.HintsSubpixelSupport = true; this.m_bIsTransform = true; // !IsIdentity matrix transform this.fixed_sizes = undefined; this.Picker = new CFontLoaderBySymbol(); this.FT_Load_Glyph_Wrapper = function(pFace, unGID, _LOAD_MODE) { var err = AscFonts.FT_Load_Glyph(pFace, unGID, _LOAD_MODE); if (0 != err && this.HintsSupport) { var err2 = AscFonts.FT_Load_Glyph(pFace, unGID, AscFonts.LOAD_MODE_DEFAULT); if (err2 != 0) return err; this.HintsSupport = false; return err2; } return err; }; this.ResetFontMatrix = function() { var m = this.m_arrdFontMatrix; if (this.m_bNeedDoItalic) { m[0] = 1; m[1] = 0; m[2] = FONT_ITALIC_ANGLE; m[3] = 1; m[4] = 0; m[5] = 0; } else { m[0] = 1; m[1] = 0; m[2] = 0; m[3] = 1; m[4] = 0; m[5] = 0; } this.UpdateMatrix(); }; this.ResetTextMatrix = function() { var m = this.m_arrdTextMatrix; m[0] = 1; m[1] = 0; m[2] = 0; m[3] = 1; m[4] = 0; m[5] = 0; this.CheckTextMatrix(); }; this.CheckTextMatrix = function() { this.m_bIsTransform = true; var m = this.m_arrdTextMatrix; if ((m[0] == 1) && (m[1] == 0) && (m[2] == 0) && (m[3] == 1)) { this.m_bIsTransform = false; this.UpdateMatrix(); } }; this.UpdateMatrix = function() { var m = this.m_arrdFontMatrix; var t = this.m_arrdTextMatrix; var xx = ((m[0] * t[0] + m[1] * t[2]) * 65536) >> 0; var yx = ((m[0] * t[1] + m[1] * t[3]) * 65536) >> 0; var xy = ((m[2] * t[0] + m[3] * t[2]) * 65536) >> 0; var yy = ((m[2] * t[1] + m[3] * t[3]) * 65536) >> 0; AscFonts.FT_Set_Transform(this.m_pFace, xx, yx, xy, yy); }; this.SetSizeAndDpi = function (dSize, unHorDpi, unVerDpi) { var dpiX = (unHorDpi + 0.5) >> 0; var dpiY = (unVerDpi + 0.5) >> 0; var dOldSize = this.m_fSize; var dNewSize = dSize; var dKoef = dNewSize / dOldSize; var isResize = (dKoef > 1.001 || dKoef < 0.999) ? true : false; if (isResize || dpiX != this.m_unHorDpi || dpiY != this.m_unVerDpi) { this.m_unHorDpi = dpiX; this.m_unVerDpi = dpiY; if (isResize) { this.m_fSize = dNewSize; this.UpdateMatrix(); } this.m_dUnitsKoef = this.m_unHorDpi / 72.0 * this.m_fSize; // Выставляем размер шрифта (dSize) и DPI this.m_nError = AscFonts.FT_Set_Char_Size(this.m_pFace, 0, (dNewSize * 64) >> 0, dpiX, dpiY); this.ClearCache(); } }; this.ClearCache = function() { this.Destroy(); this.ClearCacheNoAttack(); if (this.Picker) this.Picker.ClearCache(); }; this.ClearCacheNoAttack = function() { this.m_arrCacheSizes = []; this.m_arrCacheSizesGid = []; if (this.Picker) this.Picker.ClearCacheNoAttack(); }; this.Destroy = function() { if (this.m_oFontManager != null && this.m_oFontManager.RasterMemory != null) { var _arr = this.m_arrCacheSizes; for (var i in _arr) { if (_arr[i].oBitmap != null) _arr[i].oBitmap.Free(); } _arr = this.m_arrCacheSizesGid; for (var i in _arr) { if (_arr[i].oBitmap != null) _arr[i].oBitmap.Free(); } } }; this.SetTextMatrix = function(fA, fB, fC, fD, fE, fF) { var m = this.m_arrdTextMatrix; var b1 = (m[0] == fA && m[1] == -fB && m[2] == -fC && m[3] == fD); if (b1 && m[4] == fE && m[5] == fF) return false; m[0] = fA; m[1] = -fB; m[2] = -fC; m[3] = fD; m[4] = fE; m[5] = fF; if (!b1) { this.ClearCache(); } this.CheckTextMatrix(); return true; }; this.SetFontMatrix = function(fA, fB, fC, fD, fE, fF) { var m = this.m_arrdFontMatrix; if (this.m_bNeedDoItalic) { m[0] = fA; m[1] = fB; m[2] = fC + fA * FONT_ITALIC_ANGLE; m[3] = fD + fB * FONT_ITALIC_ANGLE; m[4] = fE; m[5] = fF; } else { m[0] = fA; m[1] = fB; m[2] = fC; m[3] = fD; m[4] = fE; m[5] = fF; } this.ClearCache(); }; this.GetGIDByUnicode = function(glyph) { var unGID = AscFonts.FT_SetCMapForCharCode(this.m_pFace, glyph); if (unGID > 0) return unGID; if (-1 != this.m_nSymbolic && glyph < 0xF000) unGID = AscFonts.FT_SetCMapForCharCode(this.m_pFace, glyph + 0xF000); return unGID; }; this.ShapeText = function(text, features, script, direction, language) { let buffer = []; for (var iter = text.getUnicodeIterator(); iter.check(); iter.next()) { buffer.push(iter.value()); } return this.ShapeCodePointsArray(buffer, features, script, direction, language); }; this.ShapeCodePointsArray = function(buffer, features, script, direction, language) { let segments = []; let curFont = this; let currentSegment = null; for (let nPos = 0, nCount = buffer.length; nPos < nCount; ++nPos) { let codePoint = buffer[nPos]; // TODO: Обработать ситуацию, когда сегмент начинается с AscFonts.HB_SCRIPT.HB_SCRIPT_INHERITED // в такой ситуации нужно ориентироваться на следующий юникод, у которого будет нормальный скрипт if (AscFonts.HB_SCRIPT.HB_SCRIPT_INHERITED !== AscFonts.hb_get_script_by_unicode(codePoint)) { let gid = this.GetGIDByUnicode(codePoint); if (gid <= 0) { curFont = this.Picker.GetFontBySymbolWithSize(this, codePoint); if (!curFont) curFont = this; } else { curFont = this; } } if (null == currentSegment || currentSegment.font !== curFont) { currentSegment = { font: curFont, text: AscCommon.encodeSurrogateChar(codePoint) }; segments.push(currentSegment); } else { currentSegment.text += AscCommon.encodeSurrogateChar(codePoint); } } for (let i = 0, len = segments.length; i < len; i++) { segments[i].glyphs = AscFonts.HB_Shape(segments[i].font, segments[i].text, features, script, direction, language); // change to id??? segments[i].font = segments[i].font.m_pFaceInfo.family_name; } return segments; }; this.CacheGlyph = function(glyph_index_or_unicode, isRaster, isRasterDistances, workerVector, workerVectorX, workerVectorY, isFromPicker) { var oSizes = new CFontCacheSizes(); oSizes.ushUnicode = glyph_index_or_unicode; var nUnicodeForHintTest = this.m_bStringGID ? 0 : glyph_index_or_unicode; var unGID = this.m_bStringGID ? glyph_index_or_unicode : AscFonts.FT_SetCMapForCharCode(this.m_pFace, glyph_index_or_unicode); if (unGID <= 0 && !this.m_bStringGID) { if (-1 != this.m_nSymbolic && glyph_index_or_unicode < 0xF000) unGID = AscFonts.FT_SetCMapForCharCode(this.m_pFace, glyph_index_or_unicode + 0xF000); } if (unGID <= 0) { if (isFromPicker === true) return null; if (!this.m_bStringGID && this.Picker) { // пробуем подобрать нужный шрифт var oSizesCheck = this.Picker.LoadSymbol(this, glyph_index_or_unicode, isRaster, isRasterDistances, workerVector, workerVectorX, workerVectorY); if (oSizesCheck) return oSizesCheck; } if (this.m_nDefaultChar >= 0) { unGID = this.m_nDefaultChar; oSizes.eState = EGlyphState.glyphstateDefault; } else { oSizes.eState = EGlyphState.glyphstateMiss; oSizes.ushGID = -1; oSizes.fAdvanceX = (AscFonts.FT_GetFaceMaxAdvanceX(this.m_pFace) >> 6) / 2.0; return oSizes; } } else { oSizes.eState = EGlyphState.glyphstateNormal; } oSizes.ushGID = unGID; oSizes.nCMapIndex = 0; // TODO:??? //var measure_time_start = performance.now(); var load_mode = this.GetCharLoadMode(nUnicodeForHintTest); if (!isRaster || this.m_bNeedDoBold || this.m_bNeedDoItalic) load_mode |= AscFonts.FT_Load_Mode.FT_LOAD_NO_BITMAP; else if (this.m_bStringGID) { if (!this.codePointsForGid || !this.codePointsForGid[0] || !AscFonts.isUseBitmapStrikes(this.codePointsForGid[0])) load_mode |= AscFonts.FT_Load_Mode.FT_LOAD_NO_BITMAP; } else if (!AscFonts.isUseBitmapStrikes(glyph_index_or_unicode)) load_mode |= AscFonts.FT_Load_Mode.FT_LOAD_NO_BITMAP; else { if (Math.abs(this.m_arrdTextMatrix[1]) > 0.001 || Math.abs(this.m_arrdTextMatrix[2]) > 0.001) load_mode |= AscFonts.FT_Load_Mode.FT_LOAD_NO_BITMAP; } if (this.FT_Load_Glyph_Wrapper(this.m_pFace, unGID, load_mode)) { oSizes.fAdvanceX = (AscFonts.FT_GetFaceMaxAdvanceX(this.m_pFace) >> 6) / 2.0; return oSizes; } var _painter = null; if (undefined !== workerVector) { _painter = new CGlyphVectorPainter(); _painter.KoefX = 25.4 / this.m_unHorDpi; _painter.KoefY = 25.4 / this.m_unVerDpi; if (workerVectorX !== undefined) _painter.X = workerVectorX; if (workerVectorY !== undefined) _painter.Y = workerVectorY; } var measureInfo = AscFonts.FT_Glyph_Get_Measure(this.m_pFace, workerVector, _painter); if ((null == measureInfo) || (undefined !== workerVector)) return oSizes; //window.measureTime += (performance.now() - measure_time_start); var isDisableNeedBold = ((this.m_pFaceInfo.os2_version != 0xFFFF) && (this.m_pFaceInfo.os2_usWeightClass >= 800)) ? true : false; oSizes.fAdvanceX = (measureInfo.linearHoriAdvance * this.m_dUnitsKoef / this.m_lUnits_Per_Em); if (this.m_bNeedDoBold && this.m_oFontManager.IsAdvanceNeedBoldFonts && !isDisableNeedBold) oSizes.fAdvanceX += 1; oSizes.oBBox.fMinX = (measureInfo.bbox_xMin >> 6); oSizes.oBBox.fMaxX = (measureInfo.bbox_xMax >> 6); oSizes.oBBox.fMinY = (measureInfo.bbox_yMin >> 6); oSizes.oBBox.fMaxY = (measureInfo.bbox_yMax >> 6); var dstM = oSizes.oMetrics; dstM.fWidth = (measureInfo.width >> 6); dstM.fHeight = (measureInfo.height >> 6); dstM.fHoriBearingX = (measureInfo.horiBearingX >> 6); dstM.fHoriBearingY = (measureInfo.horiBearingY >> 6); dstM.fHoriAdvance = (measureInfo.horiAdvance >> 6); dstM.fVertBearingX = (measureInfo.vertBearingX >> 6); dstM.fVertBearingY = (measureInfo.vertBearingY >> 6); dstM.fVertAdvance = (measureInfo.vertAdvance >> 6); if (isFromPicker && (0 == dstM.fHoriAdvance && 0 == measureInfo.width)) { if (this.m_bStringGID) return null; switch (glyph_index_or_unicode) { // список допустимых символов нулевой ширины case 0xFEFF: { return oSizes; } default: break; } return null; } if (!isRaster) { if (isRasterDistances) { var rasterInfo = AscFonts.FT_Glyph_Get_Raster(this.m_pFace, REND_MODE); if (rasterInfo) { var rasterBitmap = AscFonts.FT_Get_Glyph_Render_Buffer(this.m_pFace, rasterInfo, false); oSizes.oBBox.rasterDistances = get_raster_bounds_safe(rasterBitmap); } } return oSizes; } //measure_time_start = performance.now(); oSizes.bBitmap = true; var rasterInfo = AscFonts.FT_Glyph_Get_Raster(this.m_pFace, REND_MODE); if (!rasterInfo || rasterInfo.pitch == 0) return oSizes; //window.rasterTime += (performance.now() - measure_time_start); oSizes.oBitmap = new CGlyphBitmap(); oSizes.oBitmap.nX = rasterInfo.left; oSizes.oBitmap.nY = rasterInfo.top; oSizes.oBitmap.nWidth = rasterInfo.width; oSizes.oBitmap.nHeight = rasterInfo.rows; var rasterBitmap = AscFonts.FT_Get_Glyph_Render_Buffer(this.m_pFace, rasterInfo, true); if (this.m_bNeedDoBold && this.m_bAntiAliasing && !isDisableNeedBold) { var extraPixels = AscCommon.AscBrowser.retinaPixelRatio >> 0; if (extraPixels < 1) extraPixels = 1; oSizes.oBitmap.nWidth += extraPixels; var _width_im = oSizes.oBitmap.nWidth; var _height = oSizes.oBitmap.nHeight; var nY, nX; var pDstBuffer; var _input = raster_memory.m_oBuffer.data; while (extraPixels > 0) { --extraPixels; for (nY = 0, pDstBuffer = 0; nY < _height; ++nY, pDstBuffer += (raster_memory.pitch)) { var _pos_x = pDstBuffer + ((_width_im - extraPixels) << 2) - 1; // последние - просто копируем _input[_pos_x] = _input[_pos_x - 4]; _pos_x -= 4; for (nX = _width_im - extraPixels - 2; nX > 0; --nX, _pos_x -= 4) { // сдвигаем все вправо _input[_pos_x] = Math.min(255, _input[_pos_x - 4] + _input[_pos_x]); } } } } oSizes.oBitmap.fromAlphaMask(this.m_oFontManager); return oSizes; }; this.GetString = function(pString) { if (pString.GetLength() <= 0) return true; var unPrevGID = 0; var fPenX = 0, fPenY = 0; // Сначала мы все рассчитываем исходя только из матрицы шрифта FontMatrix if (this.m_bIsTransform) this.UpdateMatrix(); var _cache_array = (this.m_bStringGID === false) ? this.m_arrCacheSizes : this.m_arrCacheSizesGid; for (var nIndex = 0; nIndex < pString.GetLength(); ++nIndex) { var pCurGlyph = pString.GetAt(nIndex); var ushUnicode = pCurGlyph.lUnicode; var charSymbolObj = _cache_array[ushUnicode]; if (undefined == charSymbolObj) { _cache_array[ushUnicode] = this.CacheGlyph(ushUnicode, false); charSymbolObj = _cache_array[ushUnicode]; } var unGID = charSymbolObj.ushGID; var eState = charSymbolObj.eState; if (EGlyphState.glyphstateMiss == eState) { pString.SetStartPoint(nIndex, fPenX, fPenY); pString.SetBBox(nIndex, 0, 0, 0, 0); pString.SetState(nIndex, EGlyphState.glyphstateMiss); fPenX += charSymbolObj.fAdvanceX + this.m_fCharSpacing; unPrevGID = 0; continue; } else if (EGlyphState.glyphstateDefault == eState) { pString.SetState(nIndex, EGlyphState.glyphstateDefault); // kerning face!!! } else // if ( glyphstateNormal == eState ) { pString.SetState(nIndex, EGlyphState.glyphstateNormal); // kerning face!!! } /* if (0 != this.m_nNum_charmaps) { var nCharmap = pFace.charmap; var nCurCMapIndex = AscFonts.FT_Get_Charmap_Index(nCharmap); if (nCurCMapIndex != _cmap_index) { _cmap_index = Math.max(0, _cmap_index); nCharmap = pFace.charmaps[_cmap_index]; AscFonts.FT_Set_Charmap(pFace, nCharmap); } } */ if (this.m_bUseKerning && unPrevGID && (nIndex > 0 && pString.GetAt(nIndex).eState == pString.GetAt(nIndex - 1).eState)) { fPenX += this.GetKerning(unPrevGID, unGID); } var fX = pString.m_fX + fPenX; var fY = pString.m_fY + fPenY; // Начальную точку рассчитываем сразу исходя из глобальной матрицы var fXX = (pString.m_arrCTM[4] + fX * pString.m_arrCTM[0] + fY * pString.m_arrCTM[2] - pString.m_fX); var fYY = (pString.m_arrCTM[5] + fX * pString.m_arrCTM[1] + fY * pString.m_arrCTM[3] - pString.m_fY); pString.SetStartPoint(nIndex, fXX, fYY); var _metrics = charSymbolObj.oMetrics; pString.SetMetrics(nIndex, _metrics.fWidth, _metrics.fHeight, _metrics.fHoriAdvance, _metrics.fHoriBearingX, _metrics.fHoriBearingY, _metrics.fVertAdvance, _metrics.fVertBearingX, _metrics.fVertBearingY); pString.SetBBox(nIndex, charSymbolObj.oBBox.fMinX, charSymbolObj.oBBox.fMaxY, charSymbolObj.oBBox.fMaxX, charSymbolObj.oBBox.fMinY); fPenX += charSymbolObj.fAdvanceX + this.m_fCharSpacing; unPrevGID = unGID; } pString.m_fEndX = fPenX + pString.m_fX; pString.m_fEndY = fPenY + pString.m_fY; }; this.GetString2 = function(pString) { if (pString.GetLength() <= 0) return true; if (this.m_bIsTransform) this.UpdateMatrix(); var unPrevGID = 0; var fPenX = 0, fPenY = 0; var _cache_array = (this.m_bStringGID === false) ? this.m_arrCacheSizes : this.m_arrCacheSizesGid; for (var nIndex = 0; nIndex < pString.GetLength(); ++nIndex) { var pCurGlyph = pString.GetAt(nIndex); var ushUnicode = pCurGlyph.lUnicode; var charSymbolObj = _cache_array[ushUnicode]; if (undefined == charSymbolObj || null == charSymbolObj.oBitmap) { _cache_array[ushUnicode] = this.CacheGlyph(ushUnicode, true); charSymbolObj = _cache_array[ushUnicode]; } var nCMapIndex = charSymbolObj.nCMapIndex; var unGID = charSymbolObj.ushGID; var eState = charSymbolObj.eState; if (EGlyphState.glyphstateMiss == eState) { pString.SetStartPoint(nIndex, fPenX, fPenY); pString.SetBBox(nIndex, 0, 0, 0, 0); pString.SetState(nIndex, EGlyphState.glyphstateMiss); fPenX += charSymbolObj.fAdvanceX + this.m_fCharSpacing; unPrevGID = 0; continue; } else if (EGlyphState.glyphstateDefault == eState) { pString.SetState(nIndex, EGlyphState.glyphstateDefault); // kerning face!!! } else { pString.SetState(nIndex, EGlyphState.glyphstateNormal); // kerning face!!! } /* if (0 != this.m_nNum_charmaps) { var nCharmap = pFace.charmap; var nCurCMapIndex = AscFonts.FT_Get_Charmap_Index(nCharmap); if (nCurCMapIndex != nCMapIndex) { nCMapIndex = Math.max(0, nCMapIndex); nCharmap = this.m_pFace.charmaps[nCMapIndex]; AscFonts.FT_Set_Charmap(this.m_pFace, nCharmap); } } */ if (this.m_bUseKerning && unPrevGID && (nIndex > 0 && pString.GetAt(nIndex).eState == pString.GetAt(nIndex - 1).eState)) { fPenX += this.GetKerning(unPrevGID, unGID); } var fX = pString.m_fX + fPenX; var fY = pString.m_fY + fPenY; // Начальную точку рассчитываем сразу исходя из глобальной матрицы var fXX = (pString.m_arrCTM[4] + fX * pString.m_arrCTM[0] + fY * pString.m_arrCTM[2] - pString.m_fX); var fYY = (pString.m_arrCTM[5] + fX * pString.m_arrCTM[1] + fY * pString.m_arrCTM[3] - pString.m_fY); pString.SetStartPoint(nIndex, fXX, fYY); pCurGlyph.oMetrics = charSymbolObj.oMetrics; pString.SetBBox(nIndex, charSymbolObj.oBBox.fMinX, charSymbolObj.oBBox.fMaxY, charSymbolObj.oBBox.fMaxX, charSymbolObj.oBBox.fMinY); fPenX += charSymbolObj.fAdvanceX + this.m_fCharSpacing; pCurGlyph.bBitmap = charSymbolObj.bBitmap; pCurGlyph.oBitmap = charSymbolObj.oBitmap; unPrevGID = unGID; } pString.m_fEndX = fPenX + pString.m_fX; pString.m_fEndY = fPenY + pString.m_fY; }; this.GetString2C = function(pString) { // Сначала мы все рассчитываем исходя только из матрицы шрифта FontMatrix if (this.m_bIsTransform) this.UpdateMatrix(); var pCurGlyph = pString.m_pGlyphsBuffer[0]; var ushUnicode = pCurGlyph.lUnicode; var _cache_array = (this.m_bStringGID === false) ? this.m_arrCacheSizes : this.m_arrCacheSizesGid; var charSymbolObj = _cache_array[ushUnicode]; if (undefined == charSymbolObj || (null == charSymbolObj.oBitmap && charSymbolObj.bBitmap === false)) { _cache_array[ushUnicode] = this.CacheGlyph(ushUnicode, true); charSymbolObj = _cache_array[ushUnicode]; } if (!charSymbolObj) return; var eState = charSymbolObj.eState; pCurGlyph.eState = charSymbolObj.eState; if (EGlyphState.glyphstateMiss == eState) { pCurGlyph.fX = 0; pCurGlyph.fY = 0; pCurGlyph.fLeft = 0; pCurGlyph.fTop = 0; pCurGlyph.fRight = 0; pCurGlyph.fBottom = 0; return; } // кернинга нету пока. var fX = pString.m_fX; var fY = pString.m_fY; var _m = pString.m_arrCTM; // Начальную точку рассчитываем сразу исходя из глобальной матрицы pCurGlyph.fX = (_m[4] + fX * _m[0] + fY * _m[2] - pString.m_fX); pCurGlyph.fY = (_m[5] + fX * _m[1] + fY * _m[3] - pString.m_fY); //pString.SetMetrics(nIndex, charSymbolObj.oMetrics.fWidth, charSymbolObj.oMetrics.fHeight, charSymbolObj.oMetrics.fHoriAdvance, charSymbolObj.oMetrics.fHoriBearingX, charSymbolObj.oMetrics.fHoriBearingY, charSymbolObj.oMetrics.fVertAdvance, charSymbolObj.oMetrics.fVertBearingX, charSymbolObj.oMetrics.fVertBearingY); pCurGlyph.oMetrics = charSymbolObj.oMetrics; /* pCurGlyph.fLeft = charSymbolObj.oBBox.fMinX; pCurGlyph.fTop = charSymbolObj.oBBox.fMaxY; pCurGlyph.fRight = charSymbolObj.oBBox.fMaxX; pCurGlyph.fBottom = charSymbolObj.oBBox.fMinY; */ pCurGlyph.bBitmap = charSymbolObj.bBitmap; pCurGlyph.oBitmap = charSymbolObj.oBitmap; pString.m_fEndX = charSymbolObj.fAdvanceX + this.m_fCharSpacing + pString.m_fX; pString.m_fEndY = pString.m_fY; }; this.GetChar = function(lUnicode, is_raster_distances) { var Result = undefined; var ushUnicode = lUnicode; // Сначала мы все рассчитываем исходя только из матрицы шрифта FontMatrix if (this.m_bIsTransform) this.UpdateMatrix(); var _cache_array = (this.m_bStringGID === false) ? this.m_arrCacheSizes : this.m_arrCacheSizesGid; var charSymbolObj = _cache_array[ushUnicode]; if (undefined == charSymbolObj) { _cache_array[ushUnicode] = this.CacheGlyph(ushUnicode, false, is_raster_distances); charSymbolObj = _cache_array[ushUnicode]; } return charSymbolObj; }; this.GetCharPath = function(lUnicode, worker, x, y) { var pFace = this.m_pFace; var pCurentGliph = pFace.glyph; var Result; var ushUnicode = lUnicode; // Сначала мы все рассчитываем исходя только из матрицы шрифта FontMatrix if (this.m_bIsTransform) this.UpdateMatrix(); // no really cashe this.CacheGlyph(ushUnicode, false, false, worker, x, y); }; this.GetStringPath = function(string, worker) { var _len = string.GetLength(); if (_len <= 0) return true; for (var nIndex = 0; nIndex < _len; ++nIndex) { var _glyph = string.m_pGlyphsBuffer[nIndex]; var _x = string.m_fX + 25.4 * _glyph.fX / this.m_unHorDpi; var _y = string.m_fY + 25.4 * _glyph.fY / this.m_unVerDpi; worker._s(); this.GetCharPath(_glyph.lUnicode, worker, _x, _y); worker.df(); worker._e(); } }; this.GetCharLoadMode = function(code) { if (this.HintsSupport && this.HintsSubpixelSupport) { // ----------------------------------------------------------------- // заглушки if (code === 95 && this.m_pFaceInfo.family_name === "Wingdings 3") return AscFonts.LOAD_MODE_DEFAULT; // ----------------------------------------------------------------- return this.m_oFontManager.LOAD_MODE; } return AscFonts.LOAD_MODE_DEFAULT; }; this.GetKerning = function(unPrevGID, unGID) { var delta = AscFonts.FT_GetKerningX(this.m_pFace, unPrevGID, unGID); return (delta >> 6); }; this.SetStringGID = function(bGID) { if (this.m_bStringGID == bGID) return; this.m_bStringGID = bGID; }; this.GetStringGID = function() { return this.m_bStringGID; }; this.SetCharSpacing = function(fCharSpacing) { this.m_fCharSpacing = fCharSpacing; }; this.GetCharSpacing = function() { return this.m_fCharSpacing; }; this.GetStyleName = function() { return this.m_pFaceInfo.style_name; }; this.GetFamilyName = function() { return this.m_pFaceInfo.family_name; }; this.UpdateStyles = function(bBold, bItalic) { var sStyle = this.GetStyleName(); // Смотрим какой стиль у исходного шрифта var bSrcBold = (-1 != sStyle.indexOf("Bold")); var bSrcItalic = (-1 != sStyle.indexOf("Italic")); this.SetNeedBold(bBold && !bSrcBold); this.SetNeedItalic(bItalic && !bSrcItalic); }; this.IsBold = function() { if (this.m_bNeedDoBold) return true; return (-1 != this.m_pFaceInfo.style_name.indexOf("Bold")) ? true : false; }; this.IsItalic = function() { if (this.m_bNeedDoItalic) return true; return (-1 != this.m_pFaceInfo.style_name.indexOf("Italic")) ? true : false; }; this.SetNeedItalic = function(value) { if (this.m_bNeedDoItalic != value) { this.ClearCache(); this.m_bNeedDoItalic = value; this.ResetFontMatrix(); } }; this.SetNeedBold = function(value) { if (this.m_bNeedDoBold != value) { this.ClearCache(); this.m_bNeedDoBold = value; } }; this.IsSuccess = function() { return (0 == this.m_nError); }; this.GetAscender = function() { return this.m_lAscender; }; this.GetDescender = function() { return this.m_lDescender; }; this.GetHeight = function() { return this.m_lLineHeight; }; this.Units_Per_Em = function() { return this.m_lUnits_Per_Em; }; this.CheckHintsSupport = function() { this.HintsSupport = true; this.HintsSubpixelSupport = true; }; this.cellGetMetrics = function() { var face = this.m_pFaceInfo; var ret = []; ret.push(face.units_per_EM); if (face.os2_version != 0xFFFF) { ret.push(face.os2_usWinAscent); ret.push(-face.os2_usWinDescent); } else { ret.push(face.header_yMax); ret.push(face.header_yMin); } return ret; }; this.GetLimitsY = function() { return { min : this.m_pFaceInfo.header_yMin, max : this.m_pFaceInfo.header_yMax }; }; this.SetFace = function(face, fontManager) { this.m_pFace = face; this.m_pFaceInfo = new AscFonts.CFaceInfo(); this.m_pFaceInfo.load(this.m_pFace); this.m_lUnits_Per_Em = this.m_pFaceInfo.units_per_EM; this.m_lAscender = this.m_pFaceInfo.ascender; this.m_lDescender = this.m_pFaceInfo.descender; this.m_lLineHeight = this.m_pFaceInfo.height; if (fontManager.IsUseWinOS2Params && this.m_pFaceInfo.os2_version != 0xFFFF) { if (fontManager.Mode === AscFonts.TextMeasureMode.Cell) { /* // что-то типо этого в экселе... пока выключаем var _addidive = (0.15 * font.m_lLineHeight) >> 0; font.m_lAscender += ((_addidive + 1) >> 1); font.m_lDescender -= (_addidive >> 1); font.m_lLineHeight += _addidive; */ var _winAscent = this.m_pFaceInfo.os2_usWinAscent; var _winDescent = -this.m_pFaceInfo.os2_usWinDescent; // experimantal: for cjk fonts lineheight *= 1.3 if ((this.m_pFaceInfo.os2_ulUnicodeRange2 & 0x2DF00000) != 0) { var _addidive = (0.3 * (_winAscent - _winDescent)) >> 0; _winAscent += ((_addidive + 1) >> 1); _winDescent -= (_addidive >> 1); } // TODO: // https://www.microsoft.com/typography/otspec/recom.htm - hhea, not typo!!! if (this.m_pFaceInfo.height < (_winAscent - _winDescent)) { this.m_lAscender = _winAscent; this.m_lDescender = _winDescent; this.m_lLineHeight = _winAscent - _winDescent; } } else { // Здесь все подобрано после многочисленных тестов, если будет что-то меняться, то все шрифты // надо проверять заново let faceInfo = this.m_pFaceInfo; let nAscent = faceInfo.os2_usWinAscent; let nDescent = -faceInfo.os2_usWinDescent; let nLineGap = 0; if (faceInfo.os2_fsSelection & 128 || faceInfo.family_name === "Cambria Math") { nAscent = faceInfo.os2_sTypoAscender; nDescent = faceInfo.os2_sTypoDescender; nLineGap = faceInfo.os2_sTypoLineGap; } if (AscFonts.TextMeasureMode.Word === fontManager.Mode && faceInfo.os2_ulCodePageRange1 & 0x1e0000) { this.m_lLineHeight = ((nAscent - nDescent) * 1.3) | 0; this.m_lDescender = (nDescent - (nAscent - nDescent) * 0.15) | 0; this.m_lAscender = this.m_lLineHeight + this.m_lDescender; } else { this.m_lLineHeight = Math.max(this.m_lLineHeight, nAscent - nDescent + nLineGap); this.m_lAscender = nAscent; this.m_lDescender = nDescent; } } } this.m_nNum_charmaps = this.m_pFaceInfo.num_charmaps; this.m_nDefaultChar = this.m_pFaceInfo.os2_usDefaultChar; this.m_nSymbolic = this.m_pFaceInfo.os2_nSymbolic; this.m_nError = AscFonts.FT_Set_Char_Size(face, 0, this.m_fSize * 64, 0, 0); this.ResetTextMatrix(); this.ResetFontMatrix(); if (true === fontManager.m_bUseKerning) { this.m_bUseKerning = ((this.m_pFaceInfo.face_flags & 64) != 0 ? true : false); } if (0 != this.m_pFaceInfo.monochromeSizes.length) { this.fixed_sizes = []; for (var i = this.m_pFaceInfo.monochromeSizes.length - 1; i >= 0; i--) { this.fixed_sizes[this.m_pFaceInfo.monochromeSizes[i] >> 6] = true; } } } } CFontFile.prototype["GetFace"] = function() { return this.m_pFace; }; CFontFile.prototype["GetHBFont"] = function() { return this.m_pHBFont; }; CFontFile.prototype["SetHBFont"] = function(font) { this.m_pHBFont = font; }; function CFontLoaderBySymbol() { this.FontFiles = {}; this.GetFontBySymbol = function(pFontFile, symbol) { var fontManager = pFontFile.m_oFontManager; var name = AscFonts.FontPickerByCharacter.getFontBySymbol(symbol); if (undefined === name || name == "") return null; var _fontFilePick = this.FontFiles[name]; if (!_fontFilePick) { var _font_info = AscFonts.g_font_infos[AscFonts.g_map_font_index[name]]; var _style = AscFonts.FontStyle.FontStyleRegular; if (pFontFile.IsBold()) _style |= AscFonts.FontStyle.FontStyleBold; if (pFontFile.IsItalic()) _style |= AscFonts.FontStyle.FontStyleItalic; _fontFilePick = _font_info.LoadFont(AscCommon.g_font_loader, fontManager, pFontFile.m_fSize, _style, pFontFile.m_unHorDpi, pFontFile.m_unVerDpi, undefined, true); if (!_fontFilePick) return null; _fontFilePick.CheckHintsSupport(); this.FontFiles[name] = _fontFilePick; } return _fontFilePick; }; this.GetFontBySymbolWithSize = function(pFontFile, symbol) { var _fontFilePick = this.GetFontBySymbol(pFontFile, symbol); if (!_fontFilePick) return null; _fontFilePick.SetSizeAndDpi(pFontFile.m_fSize, pFontFile.m_unHorDpi, pFontFile.m_unVerDpi); var s = pFontFile.m_arrdTextMatrix; var d = _fontFilePick.m_arrdTextMatrix; d[0] = s[0]; d[1] = s[1]; d[2] = s[2]; d[3] = s[3]; d[4] = s[4]; d[5] = s[5]; _fontFilePick.UpdateMatrix(); return _fontFilePick; }; this.LoadSymbol = function(pFontFile, symbol, isRaster, isRasterDistances, workerVector, workerVectorX, workerVectorY, isFromPicker) { var _fontFilePick = this.GetFontBySymbolWithSize(pFontFile, symbol); if (!_fontFilePick) return null; return _fontFilePick.CacheGlyph(symbol, isRaster, isRasterDistances, workerVector, workerVectorX, workerVectorY, true); }; this.ClearCache = function() { if (this.FontClearCache_checker) return; this.FontClearCache_checker = true; for (var font in this.FontFiles) this.FontFiles[font].ClearCache(); delete this.FontClearCache_checker; }; this.ClearCacheNoAttack = function() { if (this.FontClearCacheNoAttack_checker) return; this.FontClearCacheNoAttack_checker = true; for (var font in this.FontFiles) this.FontFiles[font].ClearCacheNoAttack(); delete this.FontClearCacheNoAttack_checker; }; } window['AscFonts'].EGlyphState = EGlyphState; window['AscFonts'].CFontFile = CFontFile; })(window);