📄 uniscribehelper.cpp
字号:
/* * Copyright (c) 2006, 2007, 2008, 2009, Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */#include "config.h"#include "UniscribeHelper.h"#include <windows.h>#include "FontUtilsChromiumWin.h"#include "PlatformContextSkia.h"#include "SkiaFontWin.h"#include "SkPoint.h"#include <wtf/Assertions.h>namespace WebCore {// This function is used to see where word spacing should be applied inside// runs. Note that this must match Font::treatAsSpace so we all agree where// and how much space this is, so we don't want to do more general Unicode// "is this a word break" thing.static bool treatAsSpace(UChar c){ return c == ' ' || c == '\t' || c == '\n' || c == 0x00A0;}// SCRIPT_FONTPROPERTIES contains glyph indices for default, invalid// and blank glyphs. Just because ScriptShape succeeds does not mean// that a text run is rendered correctly. Some characters may be rendered// with default/invalid/blank glyphs. Therefore, we need to check if the glyph// array returned by ScriptShape contains any of those glyphs to make// sure that the text run is rendered successfully.static bool containsMissingGlyphs(WORD *glyphs, int length, SCRIPT_FONTPROPERTIES* properties){ for (int i = 0; i < length; ++i) { if (glyphs[i] == properties->wgDefault || (glyphs[i] == properties->wgInvalid && glyphs[i] != properties->wgBlank)) return true; } return false;}// HFONT is the 'incarnation' of 'everything' about font, but it's an opaque// handle and we can't directly query it to make a new HFONT sharing// its characteristics (height, style, etc) except for family name.// This function uses GetObject to convert HFONT back to LOGFONT,// resets the fields of LOGFONT and calculates style to use later// for the creation of a font identical to HFONT other than family name.static void setLogFontAndStyle(HFONT hfont, LOGFONT *logfont, int *style){ ASSERT(hfont && logfont); if (!hfont || !logfont) return; GetObject(hfont, sizeof(LOGFONT), logfont); // We reset these fields to values appropriate for CreateFontIndirect. // while keeping lfHeight, which is the most important value in creating // a new font similar to hfont. logfont->lfWidth = 0; logfont->lfEscapement = 0; logfont->lfOrientation = 0; logfont->lfCharSet = DEFAULT_CHARSET; logfont->lfOutPrecision = OUT_TT_ONLY_PRECIS; logfont->lfQuality = DEFAULT_QUALITY; // Honor user's desktop settings. logfont->lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; if (style) *style = getStyleFromLogfont(logfont);}UniscribeHelper::UniscribeHelper(const UChar* input, int inputLength, bool isRtl, HFONT hfont, SCRIPT_CACHE* scriptCache, SCRIPT_FONTPROPERTIES* fontProperties) : m_input(input) , m_inputLength(inputLength) , m_isRtl(isRtl) , m_hfont(hfont) , m_scriptCache(scriptCache) , m_fontProperties(fontProperties) , m_directionalOverride(false) , m_inhibitLigate(false) , m_letterSpacing(0) , m_spaceWidth(0) , m_wordSpacing(0) , m_ascent(0) , m_disableFontFallback(false){ m_logfont.lfFaceName[0] = 0;}UniscribeHelper::~UniscribeHelper(){}void UniscribeHelper::initWithOptionalLengthProtection(bool lengthProtection){ // We cap the input length and just don't do anything. We'll allocate a lot // of things of the size of the number of characters, so the allocated // memory will be several times the input length. Plus shaping such a large // buffer may be a form of denial of service. No legitimate text should be // this long. It also appears that Uniscribe flatly rejects very long // strings, so we don't lose anything by doing this. // // The input length protection may be disabled by the unit tests to cause // an error condition. static const int kMaxInputLength = 65535; if (m_inputLength == 0 || (lengthProtection && m_inputLength > kMaxInputLength)) return; fillRuns(); fillShapes(); fillScreenOrder();}int UniscribeHelper::width() const{ int width = 0; for (int itemIndex = 0; itemIndex < static_cast<int>(m_runs.size()); itemIndex++) width += advanceForItem(itemIndex); return width;}void UniscribeHelper::justify(int additionalSpace){ // Count the total number of glyphs we have so we know how big to make the // buffers below. int totalGlyphs = 0; for (size_t run = 0; run < m_runs.size(); run++) { int runIndex = m_screenOrder[run]; totalGlyphs += static_cast<int>(m_shapes[runIndex].glyphLength()); } if (totalGlyphs == 0) return; // Nothing to do. // We make one big buffer in screen order of all the glyphs we are drawing // across runs so that the justification function will adjust evenly across // all glyphs. Vector<SCRIPT_VISATTR, 64> visualAttributes; visualAttributes.resize(totalGlyphs); Vector<int, 64> advances; advances.resize(totalGlyphs); Vector<int, 64> justify; justify.resize(totalGlyphs); // Build the packed input. int destIndex = 0; for (size_t run = 0; run < m_runs.size(); run++) { int runIndex = m_screenOrder[run]; const Shaping& shaping = m_shapes[runIndex]; for (int i = 0; i < shaping.glyphLength(); i++, destIndex++) { memcpy(&visualAttributes[destIndex], &shaping.m_visualAttributes[i], sizeof(SCRIPT_VISATTR)); advances[destIndex] = shaping.m_advance[i]; } } // The documentation for Scriptjustify is wrong, the parameter is the space // to add and not the width of the column you want. const int minKashida = 1; // How do we decide what this should be? ScriptJustify(&visualAttributes[0], &advances[0], totalGlyphs, additionalSpace, minKashida, &justify[0]); // Now we have to unpack the justification amounts back into the runs so // the glyph indices match. int globalGlyphIndex = 0; for (size_t run = 0; run < m_runs.size(); run++) { int runIndex = m_screenOrder[run]; Shaping& shaping = m_shapes[runIndex]; shaping.m_justify.resize(shaping.glyphLength()); for (int i = 0; i < shaping.glyphLength(); i++, globalGlyphIndex++) shaping.m_justify[i] = justify[globalGlyphIndex]; }}int UniscribeHelper::characterToX(int offset) const{ HRESULT hr; ASSERT(offset <= m_inputLength); // Our algorithm is to traverse the items in screen order from left to // right, adding in each item's screen width until we find the item with // the requested character in it. int width = 0; for (size_t screenIndex = 0; screenIndex < m_runs.size(); screenIndex++) { // Compute the length of this run. int itemIndex = m_screenOrder[screenIndex]; const SCRIPT_ITEM& item = m_runs[itemIndex]; const Shaping& shaping = m_shapes[itemIndex]; int itemLength = shaping.charLength(); if (offset >= item.iCharPos && offset <= item.iCharPos + itemLength) { // Character offset is in this run. int charLength = offset - item.iCharPos; int curX = 0; hr = ScriptCPtoX(charLength, FALSE, itemLength, shaping.glyphLength(), &shaping.m_logs[0], &shaping.m_visualAttributes[0], shaping.effectiveAdvances(), &item.a, &curX); if (FAILED(hr)) return 0; width += curX + shaping.m_prePadding; ASSERT(width >= 0); return width; } // Move to the next item. width += advanceForItem(itemIndex); } ASSERT(width >= 0); return width;}int UniscribeHelper::xToCharacter(int x) const{ // We iterate in screen order until we find the item with the given pixel // position in it. When we find that guy, we ask Uniscribe for the // character index. HRESULT hr; for (size_t screenIndex = 0; screenIndex < m_runs.size(); screenIndex++) { int itemIndex = m_screenOrder[screenIndex]; int itemAdvance = advanceForItem(itemIndex); // Note that the run may be empty if shaping failed, so we want to skip // over it. const Shaping& shaping = m_shapes[itemIndex]; int itemLength = shaping.charLength(); if (x <= itemAdvance && itemLength > 0) { // The requested offset is within this item. const SCRIPT_ITEM& item = m_runs[itemIndex]; // Account for the leading space we've added to this run that // Uniscribe doesn't know about. x -= shaping.m_prePadding; int charX = 0; int trailing; hr = ScriptXtoCP(x, itemLength, shaping.glyphLength(), &shaping.m_logs[0], &shaping.m_visualAttributes[0], shaping.effectiveAdvances(), &item.a, &charX, &trailing); // The character offset is within the item. We need to add the // item's offset to transform it into the space of the TextRun return charX + item.iCharPos; } // The offset is beyond this item, account for its length and move on. x -= itemAdvance; } // Error condition, we don't know what to do if we don't have that X // position in any of our items. return 0;}void UniscribeHelper::draw(GraphicsContext* graphicsContext, HDC dc, int x, int y, int from, int to){ HGDIOBJ oldFont = 0; int curX = x; bool firstRun = true; bool useWindowsDrawing = windowsCanHandleTextDrawing(graphicsContext); for (size_t screenIndex = 0; screenIndex < m_runs.size(); screenIndex++) { int itemIndex = m_screenOrder[screenIndex]; const SCRIPT_ITEM& item = m_runs[itemIndex]; const Shaping& shaping = m_shapes[itemIndex]; // Character offsets within this run. THESE MAY NOT BE IN RANGE and may // be negative, etc. The code below handles this. int fromChar = from - item.iCharPos; int toChar = to - item.iCharPos; // See if we need to draw any characters in this item.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -