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 |