diff options
Diffstat (limited to 'Source/WebCore/platform/graphics/chromium/UniscribeHelper.cpp')
-rw-r--r-- | Source/WebCore/platform/graphics/chromium/UniscribeHelper.cpp | 1101 |
1 files changed, 0 insertions, 1101 deletions
diff --git a/Source/WebCore/platform/graphics/chromium/UniscribeHelper.cpp b/Source/WebCore/platform/graphics/chromium/UniscribeHelper.cpp deleted file mode 100644 index fa8ec9467..000000000 --- a/Source/WebCore/platform/graphics/chromium/UniscribeHelper.cpp +++ /dev/null @@ -1,1101 +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 "UniscribeHelper.h" - -#include "Font.h" -#include "FontUtilsChromiumWin.h" -#include "HWndDC.h" -#include "PlatformContextSkia.h" -#include "SkiaFontWin.h" -#include "SkPoint.h" -#include <windows.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; -} - -// 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; -#if USE(SKIA_TEXT) - // 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; -#else - minKashida = 1; // How do we decide what this should be? -#endif - 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; -#if !USE(SKIA_TEXT) - bool useWindowsDrawing = windowsCanHandleTextDrawing(graphicsContext); -#endif - - 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]; - -#if USE(SKIA_TEXT) - const int* advances = shaping.m_justify.size() ? - &shaping.m_justify[fromGlyph] - : &shaping.m_advance[fromGlyph]; -#else - if (useWindowsDrawing) { - if (firstRun) { - oldFont = SelectObject(dc, shaping.m_hfont); - firstRun = false; - } else - SelectObject(dc, shaping.m_hfont); - } - -#endif - // 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) { -#if USE(SKIA_TEXT) - SkPoint origin; - origin.fX = curX + + innerOffset; - origin.fY = y + m_ascent; - paintSkiaText(graphicsContext, - shaping.m_hfont, - glyphCount, - &shaping.m_glyphs[fromGlyph], - advances, - &shaping.m_offsets[fromGlyph], - &origin); - textOutOk = true; -#else - if (useWindowsDrawing) { - HRESULT hr = ScriptTextOut(dc, shaping.m_scriptCache, - curX + innerOffset, - y - shaping.m_ascentOffset, - 0, 0, &item.a, 0, 0, - &shaping.m_glyphs[fromGlyph], - glyphCount, - &shaping.m_advance[fromGlyph], - justify, - &shaping.m_offsets[fromGlyph]); - textOutOk = (hr == S_OK); - } else { - SkPoint origin; - origin.fX = curX + + innerOffset; - origin.fY = y + m_ascent; - paintSkiaText(graphicsContext, - shaping.m_hfont, - glyphCount, - &shaping.m_glyphs[fromGlyph], - &shaping.m_advance[fromGlyph], - &shaping.m_offsets[fromGlyph], - &origin); - textOutOk = true; - } -#endif - - 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) { - if (m_scriptTags[i] == SCRIPT_TAG_UNKNOWN) { - 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()); - } -} - -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); - } 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)) { - // 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 = getFallbackFamily(input, itemLength, - FontDescription::StandardFamily, 0, 0); - 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); - // If we use ScriptShapeOpenType(), visual attributes information for each - // characters are stored in |glyphProps[i].sva|. - if (gScriptShapeOpenTypeFunc) { - for (int i = 0; i < generatedGlyphs; ++i) - memcpy(&shaping.m_visualAttributes[i], &glyphProps[i].sva, sizeof(SCRIPT_VISATTR)); - } - 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]; - if (glyph == properties->wgDefault - || (glyph == properties->wgInvalid && glyph != properties->wgBlank)) - 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 |