| Index: Source/core/platform/graphics/win/UniscribeHelper.cpp
|
| diff --git a/Source/core/platform/graphics/win/UniscribeHelper.cpp b/Source/core/platform/graphics/win/UniscribeHelper.cpp
|
| deleted file mode 100644
|
| index bd16612b1b2aeafecdabf3b6c42498a7ceeda948..0000000000000000000000000000000000000000
|
| --- a/Source/core/platform/graphics/win/UniscribeHelper.cpp
|
| +++ /dev/null
|
| @@ -1,1208 +0,0 @@
|
| -/*
|
| - * Copyright (c) 2006, 2007, 2008, 2009, 2012 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 "core/platform/graphics/win/UniscribeHelper.h"
|
| -
|
| -#include "core/platform/graphics/Font.h"
|
| -#include "core/platform/graphics/GraphicsContext.h"
|
| -#include "core/platform/graphics/skia/SkiaFontWin.h"
|
| -#include "platform/fonts/FontFallbackWin.h"
|
| -#include "platform/win/HWndDC.h"
|
| -#include "third_party/skia/include/core/SkPoint.h"
|
| -#include "wtf/Assertions.h"
|
| -
|
| -namespace WebCore {
|
| -
|
| -// The function types for ScriptItemizeOpenType() and ScriptShapeOpenType().
|
| -// We want to use these functions for OpenType feature support, but we can't
|
| -// call them directly because usp10.dll does not always have them.
|
| -// Instead, we use GetProcAddress() to check whether we can actually use these
|
| -// function. If we can't use these functions, we substitute ScriptItemze() and
|
| -// ScriptShape().
|
| -typedef HRESULT (WINAPI *ScriptItemizeOpenTypeFunc)(const WCHAR*, int, int,
|
| - const SCRIPT_CONTROL*,
|
| - const SCRIPT_STATE*,
|
| - SCRIPT_ITEM*,
|
| - OPENTYPE_TAG*, int*);
|
| -typedef HRESULT (WINAPI *ScriptShapeOpenTypeFunc)(HDC, SCRIPT_CACHE*,
|
| - SCRIPT_ANALYSIS*,
|
| - OPENTYPE_TAG, OPENTYPE_TAG,
|
| - int*, TEXTRANGE_PROPERTIES**,
|
| - int, const WCHAR*, int, int,
|
| - WORD*, SCRIPT_CHARPROP*,
|
| - WORD*, SCRIPT_GLYPHPROP*,
|
| - int*);
|
| -
|
| -static ScriptItemizeOpenTypeFunc gScriptItemizeOpenTypeFunc = 0;
|
| -static ScriptShapeOpenTypeFunc gScriptShapeOpenTypeFunc = 0;
|
| -static bool gOpenTypeFunctionsLoaded = false;
|
| -
|
| -static void loadOpenTypeFunctions()
|
| -{
|
| - HMODULE hModule = GetModuleHandle(L"usp10");
|
| - if (hModule) {
|
| - gScriptItemizeOpenTypeFunc = reinterpret_cast<ScriptItemizeOpenTypeFunc>(GetProcAddress(hModule, "ScriptItemizeOpenType"));
|
| - gScriptShapeOpenTypeFunc = reinterpret_cast<ScriptShapeOpenTypeFunc>(GetProcAddress(hModule, "ScriptShapeOpenType"));
|
| - }
|
| - if (!gScriptItemizeOpenTypeFunc || !gScriptShapeOpenTypeFunc) {
|
| - gScriptItemizeOpenTypeFunc = 0;
|
| - gScriptShapeOpenTypeFunc = 0;
|
| - }
|
| - gOpenTypeFunctionsLoaded = true;
|
| -}
|
| -
|
| -enum {
|
| - FontStyleNormal = 0,
|
| - FontStyleBold = 1,
|
| - FontStyleItalic = 2,
|
| - FontStyleUnderlined = 4
|
| -};
|
| -
|
| -int getStyleFromLogfont(const LOGFONT* logfont)
|
| -{
|
| - // FIXME: consider defining UNDEFINED or INVALID for style and
|
| - // returning it when logfont is 0
|
| - if (!logfont) {
|
| - ASSERT_NOT_REACHED();
|
| - return FontStyleNormal;
|
| - }
|
| - return (logfont->lfItalic ? FontStyleItalic : FontStyleNormal) |
|
| - (logfont->lfUnderline ? FontStyleUnderlined : FontStyleNormal) |
|
| - (logfont->lfWeight >= 700 ? FontStyleBold : FontStyleNormal);
|
| -}
|
| -
|
| -
|
| -// 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);
|
| -}
|
| -
|
| -// This memory DC will NOT be released but it's OK
|
| -// since we want to keep it for the whole life span of the process.
|
| -HDC UniscribeHelper::m_cachedDC = 0;
|
| -
|
| -static bool canUseGlyphIndex(const SCRIPT_ITEM& run)
|
| -{
|
| - // On early version of Uniscribe, ScriptShape() sets run.a.fNoGlyphIndex
|
| - // to TRUE when it can't shape the run with glyph indexes. This could
|
| - // occur when we use CFF webfonts(See http://crbug.com/39017).
|
| - // We don't use the font in that case and try to use fallback fonts.
|
| - return !run.a.fNoGlyphIndex;
|
| -}
|
| -
|
| -UniscribeHelper::UniscribeHelper(const UChar* input,
|
| - int inputLength,
|
| - bool isRtl,
|
| - HFONT hfont,
|
| - SCRIPT_CACHE* scriptCache,
|
| - SCRIPT_FONTPROPERTIES* fontProperties,
|
| - WORD spaceGlyph)
|
| - : m_input(input)
|
| - , m_inputLength(inputLength)
|
| - , m_isRtl(isRtl)
|
| - , m_hfont(hfont)
|
| - , m_scriptCache(scriptCache)
|
| - , m_fontProperties(fontProperties)
|
| - , m_spaceGlyph(spaceGlyph)
|
| - , 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;
|
| - if (!gOpenTypeFunctionsLoaded)
|
| - loadOpenTypeFunctions();
|
| -}
|
| -
|
| -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.
|
| - int minKashida;
|
| - // Disable kashida justification based on
|
| - // http://blogs.msdn.com/b/michkap/archive/2010/08/31/10056140.aspx.
|
| - for (int i = 0; i < totalGlyphs; ++i) {
|
| - if (visualAttributes[i].uJustification == SCRIPT_JUSTIFY_ARABIC_KASHIDA)
|
| - visualAttributes[i].uJustification = SCRIPT_JUSTIFY_NONE;
|
| - }
|
| - minKashida = 0;
|
| - 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,
|
| - const FontPlatformData& fontPlatformData, HDC dc, int x, int y,
|
| - const FloatRect& textRect, int from, int to)
|
| -{
|
| - HGDIOBJ oldFont = 0;
|
| - int curX = x;
|
| - bool firstRun = true;
|
| -
|
| - 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.
|
| - if (shaping.charLength() == 0 ||
|
| - fromChar >= shaping.charLength() || toChar <= 0) {
|
| - // No chars in this item to display.
|
| - curX += advanceForItem(itemIndex);
|
| - continue;
|
| - }
|
| -
|
| - // Compute the starting glyph within this span. |from| and |to| are
|
| - // global offsets that may intersect arbitrarily with our local run.
|
| - int fromGlyph, afterGlyph;
|
| - if (item.a.fRTL) {
|
| - // To compute the first glyph when going RTL, we use |to|.
|
| - if (toChar >= shaping.charLength())
|
| - // The end of the text is after (to the left) of us.
|
| - fromGlyph = 0;
|
| - else {
|
| - // Since |to| is exclusive, the first character we draw on the
|
| - // left is actually the one right before (to the right) of
|
| - // |to|.
|
| - fromGlyph = shaping.m_logs[toChar - 1];
|
| - }
|
| -
|
| - // The last glyph is actually the first character in the range.
|
| - if (fromChar <= 0) {
|
| - // The first character to draw is before (to the right) of this
|
| - // span, so draw all the way to the end.
|
| - afterGlyph = shaping.glyphLength();
|
| - } else {
|
| - // We want to draw everything up until the character to the
|
| - // right of |from|. To the right is - 1, so we look that up
|
| - // (remember our character could be more than one glyph, so we
|
| - // can't look up our glyph and add one).
|
| - afterGlyph = shaping.m_logs[fromChar - 1];
|
| - }
|
| - } else {
|
| - // Easy case, everybody agrees about directions. We only need to
|
| - // handle boundary conditions to get a range inclusive at the
|
| - // beginning, and exclusive at the ending. We have to do some
|
| - // computation to see the glyph one past the end.
|
| - fromGlyph = shaping.m_logs[fromChar < 0 ? 0 : fromChar];
|
| - if (toChar >= shaping.charLength())
|
| - afterGlyph = shaping.glyphLength();
|
| - else
|
| - afterGlyph = shaping.m_logs[toChar];
|
| - }
|
| -
|
| - // Account for the characters that were skipped in this run. When
|
| - // WebKit asks us to draw a subset of the run, it actually tells us
|
| - // to draw at the X offset of the beginning of the run, since it
|
| - // doesn't know the internal position of any of our characters.
|
| - const int* effectiveAdvances = shaping.effectiveAdvances();
|
| - int innerOffset = 0;
|
| - for (int i = 0; i < fromGlyph; i++)
|
| - innerOffset += effectiveAdvances[i];
|
| -
|
| - // Actually draw the glyphs we found.
|
| - int glyphCount = afterGlyph - fromGlyph;
|
| - if (fromGlyph >= 0 && glyphCount > 0) {
|
| - // Account for the preceding space we need to add to this run. We
|
| - // don't need to count for the following space because that will be
|
| - // counted in advanceForItem below when we move to the next run.
|
| - innerOffset += shaping.m_prePadding;
|
| -
|
| - // Pass 0 in when there is no justification.
|
| - const int* justify = shaping.m_justify.size() == 0 ? 0 : &shaping.m_justify[fromGlyph];
|
| -
|
| - const int* advances = shaping.m_justify.size() ?
|
| - &shaping.m_justify[fromGlyph]
|
| - : &shaping.m_advance[fromGlyph];
|
| -
|
| - // Fonts with different ascents can be used to render different
|
| - // runs. 'Across-runs' y-coordinate correction needs to be
|
| - // adjusted for each font.
|
| - bool textOutOk = false;
|
| - for (int executions = 0; executions < 2; ++executions) {
|
| - SkPoint origin;
|
| - origin.fX = curX + + innerOffset;
|
| - origin.fY = y + m_ascent;
|
| - paintSkiaText(graphicsContext,
|
| - fontPlatformData,
|
| - shaping.m_hfont,
|
| - glyphCount,
|
| - &shaping.m_glyphs[fromGlyph],
|
| - advances,
|
| - &shaping.m_offsets[fromGlyph],
|
| - origin,
|
| - textRect);
|
| - textOutOk = true;
|
| -
|
| - if (!textOutOk && 0 == executions) {
|
| - // If TextOut is called from the renderer it might fail
|
| - // because the sandbox is preventing it from opening the
|
| - // font files. If we are running in the renderer,
|
| - // TryToPreloadFont is overridden to ask the browser to
|
| - // preload the font for us so we can access it.
|
| - tryToPreloadFont(shaping.m_hfont);
|
| - continue;
|
| - }
|
| - break;
|
| - }
|
| - }
|
| -
|
| - curX += advanceForItem(itemIndex);
|
| - }
|
| -
|
| - if (oldFont)
|
| - SelectObject(dc, oldFont);
|
| -}
|
| -
|
| -WORD UniscribeHelper::firstGlyphForCharacter(int charOffset) const
|
| -{
|
| - // Find the run for the given character.
|
| - for (int i = 0; i < static_cast<int>(m_runs.size()); i++) {
|
| - int firstChar = m_runs[i].iCharPos;
|
| - const Shaping& shaping = m_shapes[i];
|
| - int localOffset = charOffset - firstChar;
|
| - if (localOffset >= 0 && localOffset < shaping.charLength()) {
|
| - // The character is in this run, return the first glyph for it
|
| - // (should generally be the only glyph). It seems Uniscribe gives
|
| - // glyph 0 for empty, which is what we want to return in the
|
| - // "missing" case.
|
| - size_t glyphIndex = shaping.m_logs[localOffset];
|
| - if (glyphIndex >= shaping.m_glyphs.size()) {
|
| - // The glyph should be in this run, but the run has too few
|
| - // actual characters. This can happen when shaping the run
|
| - // fails, in which case, we should have no data in the logs at
|
| - // all.
|
| - ASSERT(shaping.m_glyphs.size() == 0);
|
| - return 0;
|
| - }
|
| - return shaping.m_glyphs[glyphIndex];
|
| - }
|
| - }
|
| -
|
| - return 0;
|
| -}
|
| -
|
| -void UniscribeHelper::fillRuns()
|
| -{
|
| - HRESULT hr;
|
| - m_runs.resize(cUniscribeHelperStackRuns);
|
| - m_scriptTags.resize(cUniscribeHelperStackRuns);
|
| -
|
| - SCRIPT_STATE inputState;
|
| - inputState.uBidiLevel = m_isRtl;
|
| - inputState.fOverrideDirection = m_directionalOverride;
|
| - inputState.fInhibitSymSwap = false;
|
| - inputState.fCharShape = false; // Not implemented in Uniscribe
|
| - inputState.fDigitSubstitute = false; // Do we want this for Arabic?
|
| - inputState.fInhibitLigate = m_inhibitLigate;
|
| - inputState.fDisplayZWG = false; // Don't draw control characters.
|
| - inputState.fArabicNumContext = m_isRtl; // Do we want this for Arabic?
|
| - inputState.fGcpClusters = false;
|
| - inputState.fReserved = 0;
|
| - inputState.fEngineReserved = 0;
|
| - // The psControl argument to ScriptItemize should be non-0 for RTL text,
|
| - // per http://msdn.microsoft.com/en-us/library/ms776532.aspx . So use a
|
| - // SCRIPT_CONTROL that is set to all zeros. Zero as a locale ID means the
|
| - // neutral locale per http://msdn.microsoft.com/en-us/library/ms776294.aspx
|
| - static SCRIPT_CONTROL inputControl = {0, // uDefaultLanguage :16;
|
| - 0, // fContextDigits :1;
|
| - 0, // fInvertPreBoundDir :1;
|
| - 0, // fInvertPostBoundDir :1;
|
| - 0, // fLinkStringBefore :1;
|
| - 0, // fLinkStringAfter :1;
|
| - 0, // fNeutralOverride :1;
|
| - 0, // fNumericOverride :1;
|
| - 0, // fLegacyBidiClass :1;
|
| - 0, // fMergeNeutralItems :1;
|
| - 0};// fReserved :7;
|
| - // Calling ScriptApplyDigitSubstitution( 0, &inputControl, &inputState)
|
| - // here would be appropriate if we wanted to set the language ID, and get
|
| - // local digit substitution behavior. For now, don't do it.
|
| -
|
| - while (true) {
|
| - int numberOfItems = 0;
|
| -
|
| - // Ideally, we would have a way to know the runs before and after this
|
| - // one, and put them into the control parameter of ScriptItemize. This
|
| - // would allow us to shape characters properly that cross style
|
| - // boundaries (WebKit bug 6148).
|
| - //
|
| - // We tell ScriptItemize that the output list of items is one smaller
|
| - // than it actually is. According to Mozilla bug 366643, if there is
|
| - // not enough room in the array on pre-SP2 systems, ScriptItemize will
|
| - // write one past the end of the buffer.
|
| - //
|
| - // ScriptItemize is very strange. It will often require a much larger
|
| - // ITEM buffer internally than it will give us as output. For example,
|
| - // it will say a 16-item buffer is not big enough, and will write
|
| - // interesting numbers into all those items. But when we give it a 32
|
| - // item buffer and it succeeds, it only has one item output.
|
| - //
|
| - // It seems to be doing at least two passes, the first where it puts a
|
| - // lot of intermediate data into our items, and the second where it
|
| - // collates them.
|
| - if (gScriptItemizeOpenTypeFunc) {
|
| - hr = gScriptItemizeOpenTypeFunc(m_input, m_inputLength,
|
| - static_cast<int>(m_runs.size()) - 1,
|
| - &inputControl, &inputState,
|
| - &m_runs[0], &m_scriptTags[0],
|
| - &numberOfItems);
|
| -
|
| - if (SUCCEEDED(hr)) {
|
| - // Pack consecutive runs, the script tag of which are
|
| - // SCRIPT_TAG_UNKNOWN, to reduce the number of runs.
|
| - for (int i = 0; i < numberOfItems; ++i) {
|
| - // Do not pack with whitespace characters at the head.
|
| - // Otherwise whole the run is rendered as a whitespace.
|
| - WCHAR ch = m_input[m_runs[i].iCharPos];
|
| - if (m_scriptTags[i] == SCRIPT_TAG_UNKNOWN && !Font::treatAsSpace(ch) && !Font::treatAsZeroWidthSpace(ch)) {
|
| - int j = 1;
|
| - while (i + j < numberOfItems && m_scriptTags[i + j] == SCRIPT_TAG_UNKNOWN)
|
| - ++j;
|
| - if (--j) {
|
| - m_runs.remove(i + 1, j);
|
| - m_scriptTags.remove(i + 1, j);
|
| - numberOfItems -= j;
|
| - }
|
| - }
|
| - }
|
| - m_scriptTags.resize(numberOfItems);
|
| - }
|
| - } else {
|
| - hr = ScriptItemize(m_input, m_inputLength,
|
| - static_cast<int>(m_runs.size()) - 1,
|
| - &inputControl, &inputState, &m_runs[0],
|
| - &numberOfItems);
|
| - }
|
| - if (SUCCEEDED(hr)) {
|
| - m_runs.resize(numberOfItems);
|
| - break;
|
| - }
|
| - if (hr != E_OUTOFMEMORY) {
|
| - // Some kind of unexpected error.
|
| - m_runs.resize(0);
|
| - break;
|
| - }
|
| - // There was not enough items for it to write into, expand.
|
| - m_runs.resize(m_runs.size() * 2);
|
| - m_scriptTags.resize(m_runs.size());
|
| - }
|
| -}
|
| -
|
| -const int kUndefinedAscent = std::numeric_limits<int>::min();
|
| -
|
| -// Given an HFONT, return the ascent. If GetTextMetrics fails,
|
| -// kUndefinedAscent is returned, instead.
|
| -int getAscent(HFONT hfont)
|
| -{
|
| - HWndDC dc(0);
|
| - HGDIOBJ oldFont = SelectObject(dc, hfont);
|
| - TEXTMETRIC tm;
|
| - BOOL gotMetrics = GetTextMetrics(dc, &tm);
|
| - SelectObject(dc, oldFont);
|
| - return gotMetrics ? tm.tmAscent : kUndefinedAscent;
|
| -}
|
| -
|
| -const WORD kUnsupportedGlyph = 0xffff;
|
| -
|
| -WORD getSpaceGlyph(HFONT hfont)
|
| -{
|
| - HWndDC dc(0);
|
| - HGDIOBJ oldFont = SelectObject(dc, hfont);
|
| - WCHAR space = L' ';
|
| - WORD spaceGlyph = kUnsupportedGlyph;
|
| - GetGlyphIndices(dc, &space, 1, &spaceGlyph, GGI_MARK_NONEXISTING_GLYPHS);
|
| - SelectObject(dc, oldFont);
|
| - return spaceGlyph;
|
| -}
|
| -
|
| -struct ShaperFontData {
|
| - ShaperFontData()
|
| - : hfont(0)
|
| - , ascent(kUndefinedAscent)
|
| - , scriptCache(0)
|
| - , spaceGlyph(0)
|
| - {
|
| - }
|
| -
|
| - HFONT hfont;
|
| - int ascent;
|
| - mutable SCRIPT_CACHE scriptCache;
|
| - WORD spaceGlyph;
|
| -};
|
| -
|
| -// Again, using hash_map does not earn us much here. page_cycler_test intl2
|
| -// gave us a 'better' result with map than with hash_map even though they're
|
| -// well-within 1-sigma of each other so that the difference is not significant.
|
| -// On the other hand, some pages in intl2 seem to take longer to load with map
|
| -// in the 1st pass. Need to experiment further.
|
| -typedef HashMap<String, ShaperFontData> ShaperFontDataCache;
|
| -
|
| -// Derive a new HFONT by replacing lfFaceName of LOGFONT with |family|,
|
| -// calculate the ascent for the derived HFONT, and initialize SCRIPT_CACHE
|
| -// in ShaperFontData.
|
| -// |style| is only used for cache key generation. |style| is
|
| -// bit-wise OR of BOLD(1), UNDERLINED(2) and ITALIC(4) and
|
| -// should match what's contained in LOGFONT. It should be calculated
|
| -// by calling GetStyleFromLogFont.
|
| -// Returns false if the font is not accessible, in which case |ascent| field
|
| -// of |ShaperFontData| is set to kUndefinedAscent.
|
| -// Be aware that this is not thread-safe.
|
| -// FIXME: Instead of having three out params, we'd better have one
|
| -// (|*ShaperFontData|), but somehow it mysteriously messes up the layout for
|
| -// certain complex script pages (e.g. hi.wikipedia.org) and also crashes
|
| -// at the start-up if recently visited page list includes pages with complex
|
| -// scripts in their title. Moreover, somehow the very first-pass of
|
| -// intl2 page-cycler test is noticeably slower with one out param than
|
| -// the current version although the subsequent 9 passes take about the
|
| -// same time.
|
| -// Be aware that this is not thread-safe.
|
| -static bool getDerivedFontData(const UChar* family, int style, LOGFONT* logfont,
|
| - int* ascent, HFONT* hfont, SCRIPT_CACHE** scriptCache, WORD* spaceGlyph)
|
| -{
|
| - ASSERT(logfont);
|
| - ASSERT(family);
|
| - ASSERT(*family);
|
| -
|
| - // It does not matter that we leak font data when we exit.
|
| - static ShaperFontDataCache* gFontDataCache = 0;
|
| - if (!gFontDataCache)
|
| - gFontDataCache = new ShaperFontDataCache();
|
| -
|
| - // FIXME: This comes up pretty high in the profile so that
|
| - // we need to measure whether using SHA256 (after coercing all the
|
| - // fields to char*) is faster than String::format.
|
| - String fontKey = String::format("%1d:%d:%ls", style, logfont->lfHeight, family);
|
| - ShaperFontDataCache::iterator iter = gFontDataCache->find(fontKey);
|
| - ShaperFontData* derived;
|
| - if (iter == gFontDataCache->end()) {
|
| - ASSERT(wcslen(family) < LF_FACESIZE);
|
| - wcscpy_s(logfont->lfFaceName, LF_FACESIZE, family);
|
| - // FIXME: CreateFontIndirect always comes up with
|
| - // a font even if there's no font matching the name. Need to
|
| - // check it against what we actually want (as is done in
|
| - // FontCacheWin.cpp)
|
| - ShaperFontDataCache::AddResult entry = gFontDataCache->add(fontKey, ShaperFontData());
|
| - derived = &entry.iterator->value;
|
| - derived->hfont = CreateFontIndirect(logfont);
|
| - // GetAscent may return kUndefinedAscent, but we still want to
|
| - // cache it so that we won't have to call CreateFontIndirect once
|
| - // more for HFONT next time.
|
| - derived->ascent = getAscent(derived->hfont);
|
| - derived->spaceGlyph = getSpaceGlyph(derived->hfont);
|
| - } else {
|
| - derived = &iter->value;
|
| - // Last time, getAscent or getSpaceGlyph failed so that only HFONT was
|
| - // cached. Try once more assuming that TryPreloadFont
|
| - // was called by a caller between calls.
|
| - if (kUndefinedAscent == derived->ascent)
|
| - derived->ascent = getAscent(derived->hfont);
|
| - if (kUnsupportedGlyph == derived->spaceGlyph)
|
| - derived->spaceGlyph = getSpaceGlyph(derived->hfont);
|
| - }
|
| - *hfont = derived->hfont;
|
| - *ascent = derived->ascent;
|
| - *scriptCache = &(derived->scriptCache);
|
| - *spaceGlyph = derived->spaceGlyph;
|
| - return *ascent != kUndefinedAscent && *spaceGlyph != kUnsupportedGlyph;
|
| -}
|
| -
|
| -bool UniscribeHelper::shape(const UChar* input,
|
| - int itemLength,
|
| - int numGlyphs,
|
| - SCRIPT_ITEM& run,
|
| - OPENTYPE_TAG scriptTag,
|
| - Shaping& shaping)
|
| -{
|
| - HFONT hfont = m_hfont;
|
| - SCRIPT_CACHE* scriptCache = m_scriptCache;
|
| - SCRIPT_FONTPROPERTIES* fontProperties = m_fontProperties;
|
| - Vector<SCRIPT_CHARPROP, cUniscribeHelperStackChars> charProps;
|
| - Vector<SCRIPT_GLYPHPROP, cUniscribeHelperStackChars> glyphProps;
|
| - int ascent = m_ascent;
|
| - WORD spaceGlyph = m_spaceGlyph;
|
| - HRESULT hr;
|
| - // When used to fill up glyph pages for simple scripts in non-BMP,
|
| - // we don't want any font fallback in this class. The simple script
|
| - // font path can take care of font fallback.
|
| - bool lastFallbackTried = m_disableFontFallback;
|
| - bool result;
|
| -
|
| - int generatedGlyphs = 0;
|
| -
|
| - // In case HFONT passed in ctor cannot render this run, we have to scan
|
| - // other fonts from the beginning of the font list.
|
| - resetFontIndex();
|
| -
|
| - // Compute shapes.
|
| - while (true) {
|
| - shaping.m_logs.resize(itemLength);
|
| - shaping.m_glyphs.resize(numGlyphs);
|
| - shaping.m_visualAttributes.resize(numGlyphs);
|
| - charProps.resize(itemLength);
|
| - glyphProps.resize(numGlyphs);
|
| - run.a.fNoGlyphIndex = FALSE;
|
| -
|
| -#ifdef PURIFY
|
| - // http://code.google.com/p/chromium/issues/detail?id=5309
|
| - // Purify isn't able to track the assignments that ScriptShape makes to
|
| - // shaping.m_glyphs. Consequently, any bytes with value 0xCD that it
|
| - // writes, will be considered un-initialized data.
|
| - //
|
| - // This hack avoid the false-positive UMRs by marking the buffer as
|
| - // initialized.
|
| - //
|
| - // FIXME: A better solution would be to use Purify's API and mark only
|
| - // the populated range as initialized:
|
| - //
|
| - // PurifyMarkAsInitialized(
|
| - // &shaping.m_glyphs[0],
|
| - // sizeof(shaping.m_glyphs[0] * generatedGlyphs);
|
| -
|
| - ZeroMemory(&shaping.m_glyphs[0],
|
| - sizeof(shaping.m_glyphs[0]) * shaping.m_glyphs.size());
|
| -#endif
|
| - // If our DC is already created, select the font in it so we can use it now.
|
| - // Otherwise, we'll create it as needed afterward...
|
| - if (m_cachedDC)
|
| - SelectObject(m_cachedDC, hfont);
|
| -
|
| - // Firefox sets SCRIPT_ANALYSIS.SCRIPT_STATE.fDisplayZWG to true
|
| - // here. Is that what we want? It will display control characters.
|
| - if (gScriptShapeOpenTypeFunc) {
|
| - TEXTRANGE_PROPERTIES* rangeProps = m_featureRecords.size() ? &m_rangeProperties : 0;
|
| - hr = gScriptShapeOpenTypeFunc(m_cachedDC, scriptCache, &run.a,
|
| - scriptTag, 0, &itemLength,
|
| - &rangeProps, rangeProps ? 1 : 0,
|
| - input, itemLength, numGlyphs,
|
| - &shaping.m_logs[0], &charProps[0],
|
| - &shaping.m_glyphs[0], &glyphProps[0],
|
| - &generatedGlyphs);
|
| - if (SUCCEEDED(hr)) {
|
| - // If we use ScriptShapeOpenType(), visual attributes
|
| - // information for each characters are stored in
|
| - // |glyphProps[i].sva|.
|
| - for (int i = 0; i < generatedGlyphs; ++i)
|
| - memcpy(&shaping.m_visualAttributes[i], &glyphProps[i].sva, sizeof(SCRIPT_VISATTR));
|
| - }
|
| - } else {
|
| - hr = ScriptShape(m_cachedDC, scriptCache, input, itemLength,
|
| - numGlyphs, &run.a,
|
| - &shaping.m_glyphs[0], &shaping.m_logs[0],
|
| - &shaping.m_visualAttributes[0], &generatedGlyphs);
|
| - }
|
| - // We receive E_PENDING when we need to try again with a Drawing Context,
|
| - // but we don't want to retry again if we already tried with non-zero DC.
|
| - if (hr == E_PENDING && !m_cachedDC) {
|
| - EnsureCachedDCCreated();
|
| - continue;
|
| - }
|
| - if (hr == E_OUTOFMEMORY) {
|
| - numGlyphs *= 2;
|
| - continue;
|
| - }
|
| - if (SUCCEEDED(hr) && (lastFallbackTried || !containsMissingGlyphs(shaping, run, fontProperties) && canUseGlyphIndex(run)))
|
| - break;
|
| -
|
| - // The current font can't render this run, try next font.
|
| - if (!m_disableFontFallback &&
|
| - nextWinFontData(hfont, scriptCache, fontProperties, ascent, spaceGlyph)) {
|
| - // The primary font does not support this run. Try next font.
|
| - // In case of web page rendering, they come from fonts specified in
|
| - // CSS stylesheets.
|
| - continue;
|
| - } else if (!lastFallbackTried) {
|
| - lastFallbackTried = true;
|
| -
|
| - // Generate a last fallback font based on the script of
|
| - // a character to draw while inheriting size and styles
|
| - // from the primary font
|
| - if (!m_logfont.lfFaceName[0])
|
| - setLogFontAndStyle(m_hfont, &m_logfont, &m_style);
|
| -
|
| - // TODO(jungshik): generic type should come from webkit for
|
| - // UniscribeHelperTextRun (a derived class used in webkit).
|
| - const UChar *family = getFallbackFamilyForFirstNonCommonCharacter(input, itemLength,
|
| - FontDescription::StandardFamily);
|
| - bool fontOk = getDerivedFontData(family, m_style, &m_logfont,
|
| - &ascent, &hfont, &scriptCache,
|
| - &spaceGlyph);
|
| -
|
| -
|
| - if (!fontOk) {
|
| - // If this GetDerivedFontData is called from the renderer it
|
| - // might fail because the sandbox is preventing it from opening
|
| - // the font files. If we are running in the renderer,
|
| - // TryToPreloadFont is overridden to ask the browser to preload
|
| - // the font for us so we can access it.
|
| - tryToPreloadFont(hfont);
|
| -
|
| - // Try again.
|
| - fontOk = getDerivedFontData(family, m_style, &m_logfont,
|
| - &ascent, &hfont, &scriptCache,
|
| - &spaceGlyph);
|
| - ASSERT(fontOk);
|
| - }
|
| -
|
| - // TODO(jungshik) : Currently GetDerivedHFont always returns a
|
| - // a valid HFONT, but in the future, I may change it to return 0.
|
| - ASSERT(hfont);
|
| -
|
| - // We don't need a font_properties for the last resort fallback font
|
| - // because we don't have anything more to try and are forced to
|
| - // accept empty glyph boxes. If we tried a series of fonts as
|
| - // 'last-resort fallback', we'd need it, but currently, we don't.
|
| - continue;
|
| - } else if (hr == USP_E_SCRIPT_NOT_IN_FONT) {
|
| - run.a.eScript = SCRIPT_UNDEFINED;
|
| - continue;
|
| - } else if (FAILED(hr)) {
|
| - // Error shaping.
|
| - generatedGlyphs = 0;
|
| - result = false;
|
| - goto cleanup;
|
| - }
|
| - }
|
| -
|
| - // Sets Windows font data for this run to those corresponding to
|
| - // a font supporting this run. we don't need to store font_properties
|
| - // because it's not used elsewhere.
|
| - shaping.m_hfont = hfont;
|
| - shaping.m_scriptCache = scriptCache;
|
| - shaping.m_spaceGlyph = spaceGlyph;
|
| -
|
| - // The ascent of a font for this run can be different from
|
| - // that of the primary font so that we need to keep track of
|
| - // the difference per run and take that into account when calling
|
| - // ScriptTextOut in |draw|. Otherwise, different runs rendered by
|
| - // different fonts would not be aligned vertically.
|
| - shaping.m_ascentOffset = m_ascent ? ascent - m_ascent : 0;
|
| - result = true;
|
| -
|
| - cleanup:
|
| - shaping.m_glyphs.resize(generatedGlyphs);
|
| - shaping.m_visualAttributes.resize(generatedGlyphs);
|
| - shaping.m_advance.resize(generatedGlyphs);
|
| - shaping.m_offsets.resize(generatedGlyphs);
|
| -
|
| - // On failure, our logs don't mean anything, so zero those out.
|
| - if (!result)
|
| - shaping.m_logs.clear();
|
| -
|
| - return result;
|
| -}
|
| -
|
| -void UniscribeHelper::EnsureCachedDCCreated()
|
| -{
|
| - if (m_cachedDC)
|
| - return;
|
| - // Allocate a memory DC that is compatible with the Desktop DC since we don't have any window,
|
| - // and we don't want to use the Desktop DC directly since it can have nasty side effects
|
| - // as identified in Chrome Issue http://crbug.com/59315.
|
| - HWndDC screenDC(0);
|
| - m_cachedDC = ::CreateCompatibleDC(screenDC);
|
| - ASSERT(m_cachedDC);
|
| -}
|
| -
|
| -void UniscribeHelper::fillShapes()
|
| -{
|
| - m_shapes.resize(m_runs.size());
|
| - for (size_t i = 0; i < m_runs.size(); i++) {
|
| - int startItem = m_runs[i].iCharPos;
|
| - int itemLength = m_inputLength - startItem;
|
| - if (i < m_runs.size() - 1)
|
| - itemLength = m_runs[i + 1].iCharPos - startItem;
|
| -
|
| - int numGlyphs;
|
| - if (itemLength < cUniscribeHelperStackChars) {
|
| - // We'll start our buffer sizes with the current stack space
|
| - // available in our buffers if the current input fits. As long as
|
| - // it doesn't expand past that we'll save a lot of time mallocing.
|
| - numGlyphs = cUniscribeHelperStackChars;
|
| - } else {
|
| - // When the input doesn't fit, give up with the stack since it will
|
| - // almost surely not be enough room (unless the input actually
|
| - // shrinks, which is unlikely) and just start with the length
|
| - // recommended by the Uniscribe documentation as a "usually fits"
|
| - // size.
|
| - numGlyphs = itemLength * 3 / 2 + 16;
|
| - }
|
| -
|
| - // Convert a string to a glyph string trying the primary font, fonts in
|
| - // the fallback list and then script-specific last resort font.
|
| - Shaping& shaping = m_shapes[i];
|
| - if (!shape(&m_input[startItem], itemLength, numGlyphs, m_runs[i], m_scriptTags[i], shaping))
|
| - continue;
|
| -
|
| - // At the moment, the only time m_disableFontFallback is set is
|
| - // when we look up glyph indices for non-BMP code ranges. So,
|
| - // we can skip the glyph placement. When that becomes not the case
|
| - // any more, we have to add a new flag to control glyph placement.
|
| - if (m_disableFontFallback)
|
| - continue;
|
| -
|
| - // Compute placements. Note that offsets is documented incorrectly
|
| - // and is actually an array.
|
| - EnsureCachedDCCreated();
|
| - SelectObject(m_cachedDC, shaping.m_hfont);
|
| - shaping.m_prePadding = 0;
|
| - if (FAILED(ScriptPlace(m_cachedDC, shaping.m_scriptCache,
|
| - &shaping.m_glyphs[0],
|
| - static_cast<int>(shaping.m_glyphs.size()),
|
| - &shaping.m_visualAttributes[0], &m_runs[i].a,
|
| - &shaping.m_advance[0], &shaping.m_offsets[0],
|
| - &shaping.m_abc))) {
|
| - // Some error we don't know how to handle. Nuke all of our data
|
| - // since we can't deal with partially valid data later.
|
| - m_runs.clear();
|
| - m_scriptTags.clear();
|
| - m_shapes.clear();
|
| - m_screenOrder.clear();
|
| - }
|
| - }
|
| -
|
| - adjustSpaceAdvances();
|
| -
|
| - if (m_letterSpacing != 0 || m_wordSpacing != 0)
|
| - applySpacing();
|
| -}
|
| -
|
| -void UniscribeHelper::fillScreenOrder()
|
| -{
|
| - m_screenOrder.resize(m_runs.size());
|
| -
|
| - // We assume that the input has only one text direction in it.
|
| - // TODO(brettw) are we sure we want to keep this restriction?
|
| - if (m_isRtl) {
|
| - for (int i = 0; i < static_cast<int>(m_screenOrder.size()); i++)
|
| - m_screenOrder[static_cast<int>(m_screenOrder.size()) - i - 1] = i;
|
| - } else {
|
| - for (int i = 0; i < static_cast<int>(m_screenOrder.size()); i++)
|
| - m_screenOrder[i] = i;
|
| - }
|
| -}
|
| -
|
| -void UniscribeHelper::adjustSpaceAdvances()
|
| -{
|
| - if (m_spaceWidth == 0)
|
| - return;
|
| -
|
| - int spaceWidthWithoutLetterSpacing = m_spaceWidth - m_letterSpacing;
|
| -
|
| - // This mostly matches what WebKit's UniscribeController::shapeAndPlaceItem.
|
| - for (size_t run = 0; run < m_runs.size(); run++) {
|
| - Shaping& shaping = m_shapes[run];
|
| -
|
| - // FIXME: This loop is not UTF-16-safe. Unicode 6.0 has a couple
|
| - // of complex script blocks in Plane 1.
|
| - for (int i = 0; i < shaping.charLength(); i++) {
|
| - UChar c = m_input[m_runs[run].iCharPos + i];
|
| - bool treatAsSpace = Font::treatAsSpace(c);
|
| - if (!treatAsSpace && !Font::treatAsZeroWidthSpaceInComplexScript(c))
|
| - continue;
|
| -
|
| - int glyphIndex = shaping.m_logs[i];
|
| - int currentAdvance = shaping.m_advance[glyphIndex];
|
| -
|
| - shaping.m_glyphs[glyphIndex] = shaping.m_spaceGlyph;
|
| -
|
| - if (treatAsSpace) {
|
| - // currentAdvance does not include additional letter-spacing,
|
| - // but m_spaceWidth does. Here we find out how off we are from
|
| - // the correct width (spaceWidthWithoutLetterSpacing) and
|
| - // just subtract that diff.
|
| - int diff = currentAdvance - spaceWidthWithoutLetterSpacing;
|
| - // The shaping can consist of a run of text, so only subtract
|
| - // the difference in the width of the glyph.
|
| - shaping.m_advance[glyphIndex] -= diff;
|
| - shaping.m_abc.abcB -= diff;
|
| - continue;
|
| - }
|
| -
|
| - // For characters treated as zero-width space in complex
|
| - // scripts, set the advance width to zero, adjust
|
| - // |abcB| of the current run accordingly and set
|
| - // the glyph to m_spaceGlyph (invisible).
|
| - shaping.m_advance[glyphIndex] = 0;
|
| - shaping.m_abc.abcB -= currentAdvance;
|
| - shaping.m_offsets[glyphIndex].du = 0;
|
| - shaping.m_offsets[glyphIndex].dv = 0;
|
| - }
|
| - }
|
| -}
|
| -
|
| -void UniscribeHelper::applySpacing()
|
| -{
|
| - for (size_t run = 0; run < m_runs.size(); run++) {
|
| - Shaping& shaping = m_shapes[run];
|
| - bool isRtl = m_runs[run].a.fRTL;
|
| -
|
| - if (m_letterSpacing != 0) {
|
| - // RTL text gets padded to the left of each character. We increment
|
| - // the run's advance to make this happen. This will be balanced out
|
| - // by NOT adding additional advance to the last glyph in the run.
|
| - if (isRtl)
|
| - shaping.m_prePadding += m_letterSpacing;
|
| -
|
| - // Go through all the glyphs in this run and increase the "advance"
|
| - // to account for letter spacing. We adjust letter spacing only on
|
| - // cluster boundaries.
|
| - //
|
| - // This works for most scripts, but may have problems with some
|
| - // indic scripts. This behavior is better than Firefox or IE for
|
| - // Hebrew.
|
| - for (int i = 0; i < shaping.glyphLength(); i++) {
|
| - if (shaping.m_visualAttributes[i].fClusterStart) {
|
| - // Ick, we need to assign the extra space so that the glyph
|
| - // comes first, then is followed by the space. This is
|
| - // opposite for RTL.
|
| - if (isRtl) {
|
| - if (i != shaping.glyphLength() - 1) {
|
| - // All but the last character just get the spacing
|
| - // applied to their advance. The last character
|
| - // doesn't get anything,
|
| - shaping.m_advance[i] += m_letterSpacing;
|
| - shaping.m_abc.abcB += m_letterSpacing;
|
| - }
|
| - } else {
|
| - // LTR case is easier, we just add to the advance.
|
| - shaping.m_advance[i] += m_letterSpacing;
|
| - shaping.m_abc.abcB += m_letterSpacing;
|
| - }
|
| - }
|
| - }
|
| - }
|
| -
|
| - // Go through all the characters to find whitespace and insert the
|
| - // extra wordspacing amount for the glyphs they correspond to.
|
| - if (m_wordSpacing != 0) {
|
| - for (int i = 0; i < shaping.charLength(); i++) {
|
| - if (!Font::treatAsSpace(m_input[m_runs[run].iCharPos + i]))
|
| - continue;
|
| -
|
| - // The char in question is a word separator...
|
| - int glyphIndex = shaping.m_logs[i];
|
| -
|
| - // Spaces will not have a glyph in Uniscribe, it will just add
|
| - // additional advance to the character to the left of the
|
| - // space. The space's corresponding glyph will be the character
|
| - // following it in reading order.
|
| - if (isRtl) {
|
| - // In RTL, the glyph to the left of the space is the same
|
| - // as the first glyph of the following character, so we can
|
| - // just increment it.
|
| - shaping.m_advance[glyphIndex] += m_wordSpacing;
|
| - shaping.m_abc.abcB += m_wordSpacing;
|
| - } else {
|
| - // LTR is actually more complex here, we apply it to the
|
| - // previous character if there is one, otherwise we have to
|
| - // apply it to the leading space of the run.
|
| - if (glyphIndex == 0)
|
| - shaping.m_prePadding += m_wordSpacing;
|
| - else {
|
| - shaping.m_advance[glyphIndex - 1] += m_wordSpacing;
|
| - shaping.m_abc.abcB += m_wordSpacing;
|
| - }
|
| - }
|
| - }
|
| - } // m_wordSpacing != 0
|
| -
|
| - // Loop for next run...
|
| - }
|
| -}
|
| -
|
| -// The advance is the ABC width of the run
|
| -int UniscribeHelper::advanceForItem(int itemIndex) const
|
| -{
|
| - int accum = 0;
|
| - const Shaping& shaping = m_shapes[itemIndex];
|
| -
|
| - if (shaping.m_justify.size() == 0) {
|
| - // Easy case with no justification, the width is just the ABC width of
|
| - // the run. (The ABC width is the sum of the advances).
|
| - return shaping.m_abc.abcA + shaping.m_abc.abcB +
|
| - shaping.m_abc.abcC + shaping.m_prePadding;
|
| - }
|
| -
|
| - // With justification, we use the justified amounts instead. The
|
| - // justification array contains both the advance and the extra space
|
| - // added for justification, so is the width we want.
|
| - int justification = 0;
|
| - for (size_t i = 0; i < shaping.m_justify.size(); i++)
|
| - justification += shaping.m_justify[i];
|
| -
|
| - return shaping.m_prePadding + justification;
|
| -}
|
| -
|
| -// 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.
|
| -// However, we should not subject zero-width characters to this test.
|
| -
|
| -bool UniscribeHelper::containsMissingGlyphs(const Shaping& shaping,
|
| - const SCRIPT_ITEM& run,
|
| - const SCRIPT_FONTPROPERTIES* properties) const
|
| -{
|
| - for (int i = 0; i < shaping.charLength(); i++) {
|
| - UChar c = m_input[run.iCharPos + i];
|
| - // Skip zero-width space characters because they're not considered to
|
| - // be missing in a font.
|
| - if (Font::treatAsZeroWidthSpaceInComplexScript(c))
|
| - continue;
|
| - int glyphIndex = shaping.m_logs[i];
|
| - WORD glyph = shaping.m_glyphs[glyphIndex];
|
| - // Note on the thrid condition: Windows Vista sometimes returns glyphs
|
| - // equal to wgBlank (instead of wgDefault), with fZeroWidth set. Treat
|
| - // such cases as having missing glyphs if the corresponding character
|
| - // is not a zero width whitespace.
|
| - if (glyph == properties->wgDefault
|
| - || (glyph == properties->wgInvalid && glyph != properties->wgBlank)
|
| - || (glyph == properties->wgBlank && shaping.m_visualAttributes[glyphIndex].fZeroWidth && !Font::treatAsZeroWidthSpace(c)))
|
| - return true;
|
| - }
|
| - return false;
|
| -}
|
| -
|
| -static OPENTYPE_TAG convertFeatureTag(const String& tag)
|
| -{
|
| - return ((tag[0] & 0xFF) | ((tag[1] & 0xFF) << 8) | ((tag[2] & 0xFF) << 16) | ((tag[3] & 0xFF) << 24));
|
| -}
|
| -
|
| -void UniscribeHelper::setRangeProperties(const FontFeatureSettings* featureSettings)
|
| -{
|
| - if (!featureSettings || !featureSettings->size()) {
|
| - m_featureRecords.resize(0);
|
| - return;
|
| - }
|
| -
|
| - m_featureRecords.resize(featureSettings->size());
|
| - for (unsigned i = 0; i < featureSettings->size(); ++i) {
|
| - m_featureRecords[i].lParameter = featureSettings->at(i).value();
|
| - m_featureRecords[i].tagFeature = convertFeatureTag(featureSettings->at(i).tag());
|
| - }
|
| - m_rangeProperties.potfRecords = &m_featureRecords[0];
|
| - m_rangeProperties.cotfRecords = m_featureRecords.size();
|
| -}
|
| -
|
| -} // namespace WebCore
|
|
|