summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbrettw@google.com <brettw@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-11-17 17:30:19 +0000
committerbrettw@google.com <brettw@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-11-17 17:30:19 +0000
commit9b9877d2952882560e37205edf33ad460a7efa91 (patch)
tree452db327f3b534b65d8d8260213094c5d9e6d00d
parentb6f2b91367ba6ed9ebb305233d4e786c1c748e45 (diff)
downloadchromium_src-9b9877d2952882560e37205edf33ad460a7efa91.zip
chromium_src-9b9877d2952882560e37205edf33ad460a7efa91.tar.gz
chromium_src-9b9877d2952882560e37205edf33ad460a7efa91.tar.bz2
Debase our Uniscribe code. This moves FontUtils and all our Uniscribe code from
base/gfx to webkit/port/platform/graphics. I fixed the indenting and naming of the moved code. Review URL: http://codereview.chromium.org/10785 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@5561 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--base/build/base_gfx.vcproj16
-rw-r--r--base/build/base_unittests.vcproj4
-rw-r--r--base/gfx/base_gfx.scons4
-rw-r--r--base/gfx/font_utils.cc336
-rw-r--r--base/gfx/uniscribe.cc848
-rw-r--r--base/gfx/uniscribe.h366
-rw-r--r--base/gfx/uniscribe_unittest.cc140
-rw-r--r--webkit/SConscript.port3
-rw-r--r--webkit/build/port/port.vcproj40
-rw-r--r--webkit/port/platform/UniscribeStateTextRun.cpp128
-rw-r--r--webkit/port/platform/graphics/FontUtilsWin.cpp339
-rw-r--r--webkit/port/platform/graphics/FontUtilsWin.h (renamed from base/gfx/font_utils.h)50
-rw-r--r--webkit/port/platform/graphics/FontWin.cpp13
-rw-r--r--webkit/port/platform/graphics/GlyphPageTreeNodeWin.cpp12
-rw-r--r--webkit/port/platform/graphics/UniscribeHelper.cpp868
-rw-r--r--webkit/port/platform/graphics/UniscribeHelper.h380
-rw-r--r--webkit/port/platform/graphics/UniscribeHelperTextRun.cpp135
-rw-r--r--webkit/port/platform/graphics/UniscribeHelperTextRun.h (renamed from webkit/port/platform/UniscribeStateTextRun.h)73
-rw-r--r--webkit/port/platform/graphics/UniscribeHelper_unittest.cpp142
-rw-r--r--webkit/port/rendering/RenderThemeWin.cpp32
-rw-r--r--webkit/tools/test_shell/SConscript1
-rw-r--r--webkit/tools/test_shell/test_shell_tests.vcproj12
22 files changed, 1994 insertions, 1948 deletions
diff --git a/base/build/base_gfx.vcproj b/base/build/base_gfx.vcproj
index 79ce51d..22dc8b4 100644
--- a/base/build/base_gfx.vcproj
+++ b/base/build/base_gfx.vcproj
@@ -138,14 +138,6 @@
>
</File>
<File
- RelativePath="..\gfx\font_utils.cc"
- >
- </File>
- <File
- RelativePath="..\gfx\font_utils.h"
- >
- </File>
- <File
RelativePath="..\gfx\gdi_util.cc"
>
</File>
@@ -234,14 +226,6 @@
>
</File>
<File
- RelativePath="..\gfx\uniscribe.cc"
- >
- </File>
- <File
- RelativePath="..\gfx\uniscribe.h"
- >
- </File>
- <File
RelativePath="..\gfx\vector_canvas.cc"
>
</File>
diff --git a/base/build/base_unittests.vcproj b/base/build/base_unittests.vcproj
index 94dd0b5..df3444b 100644
--- a/base/build/base_unittests.vcproj
+++ b/base/build/base_unittests.vcproj
@@ -412,10 +412,6 @@
>
</File>
<File
- RelativePath="..\gfx\uniscribe_unittest.cc"
- >
- </File>
- <File
RelativePath="..\gfx\vector_canvas_unittest.cc"
>
</File>
diff --git a/base/gfx/base_gfx.scons b/base/gfx/base_gfx.scons
index 9892d66..748f13b 100644
--- a/base/gfx/base_gfx.scons
+++ b/base/gfx/base_gfx.scons
@@ -33,7 +33,6 @@ if env['PLATFORM'] == 'win32':
input_files = [
'convolver.cc',
- 'font_utils.cc',
'gdi_util.cc',
'image_operations.cc',
'native_theme.cc',
@@ -43,7 +42,6 @@ input_files = [
'rect.cc',
'size.cc',
'skia_utils.cc',
- 'uniscribe.cc',
'vector_canvas.cc',
'vector_device.cc',
]
@@ -52,11 +50,9 @@ if env['PLATFORM'] in ('posix', 'darwin'):
# Remove files that still need to be ported from the input_files list.
# TODO(port): delete files from this list as they get ported.
to_be_ported_files = [
- 'font_utils.cc',
'gdi_util.cc',
'native_theme.cc',
'skia_utils.cc',
- 'uniscribe.cc',
'vector_canvas.cc',
'vector_device.cc',
]
diff --git a/base/gfx/font_utils.cc b/base/gfx/font_utils.cc
deleted file mode 100644
index 23c7f3a..0000000
--- a/base/gfx/font_utils.cc
+++ /dev/null
@@ -1,336 +0,0 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/gfx/font_utils.h"
-
-#include <limits>
-#include <map>
-
-#include "base/gfx/uniscribe.h"
-#include "base/logging.h"
-#include "base/singleton.h"
-#include "base/string_util.h"
-#include "unicode/locid.h"
-#include "unicode/uchar.h"
-
-namespace gfx {
-
-namespace {
-
-// hash_map has extra cost with no sizable gain for a small number of integer
-// key items. When the map size becomes much bigger (which will be later as
-// more scripts are added) and this turns out to be prominent in the profile, we
-// may consider switching to hash_map (or just an array if we support all the
-// scripts)
-typedef std::map<UScriptCode, const wchar_t*> ScriptToFontMap;
-
-struct ScriptToFontMapSingletonTraits
- : public DefaultSingletonTraits<ScriptToFontMap> {
- static ScriptToFontMap* New() {
- struct FontMap {
- UScriptCode script;
- const wchar_t* family;
- };
-
- const static FontMap font_map[] = {
- {USCRIPT_LATIN, L"times new roman"},
- {USCRIPT_GREEK, L"times new roman"},
- {USCRIPT_CYRILLIC, L"times new roman"},
- {USCRIPT_SIMPLIFIED_HAN, L"simsun"},
- //{USCRIPT_TRADITIONAL_HAN, L"pmingliu"},
- {USCRIPT_HIRAGANA, L"ms pgothic"},
- {USCRIPT_KATAKANA, L"ms pgothic"},
- {USCRIPT_KATAKANA_OR_HIRAGANA, L"ms pgothic"},
- {USCRIPT_HANGUL, L"gulim"},
- {USCRIPT_THAI, L"tahoma"},
- {USCRIPT_HEBREW, L"david"},
- {USCRIPT_ARABIC, L"tahoma"},
- {USCRIPT_DEVANAGARI, L"mangal"},
- {USCRIPT_BENGALI, L"vrinda"},
- {USCRIPT_GURMUKHI, L"raavi"},
- {USCRIPT_GUJARATI, L"shruti"},
- {USCRIPT_ORIYA, L"kalinga"},
- {USCRIPT_TAMIL, L"latha"},
- {USCRIPT_TELUGU, L"gautami"},
- {USCRIPT_KANNADA, L"tunga"},
- {USCRIPT_MALAYALAM, L"kartika"},
- {USCRIPT_LAO, L"dokchampa"},
- {USCRIPT_TIBETAN, L"microsoft himalaya"},
- {USCRIPT_GEORGIAN, L"sylfaen"},
- {USCRIPT_ARMENIAN, L"sylfaen"},
- {USCRIPT_ETHIOPIC, L"nyala"},
- {USCRIPT_CANADIAN_ABORIGINAL, L"euphemia"},
- {USCRIPT_CHEROKEE, L"plantagenet cherokee"},
- {USCRIPT_YI, L"microsoft yi balti"},
- {USCRIPT_SINHALA, L"iskoola pota"},
- {USCRIPT_SYRIAC, L"estrangelo edessa"},
- {USCRIPT_KHMER, L"daunpenh"},
- {USCRIPT_THAANA, L"mv boli"},
- {USCRIPT_MONGOLIAN, L"mongolian balti"},
- {USCRIPT_MYANMAR, L"padauk"},
- // For USCRIPT_COMMON, we map blocks to scripts when
- // that makes sense.
- };
-
- ScriptToFontMap* new_instance = new ScriptToFontMap;
- // Cannot recover from OOM so that there's no need to check.
- for (int i = 0; i < arraysize(font_map); ++i)
- (*new_instance)[font_map[i].script] = font_map[i].family;
-
- // Initialize the locale-dependent mapping.
- // Since Chrome synchronizes the ICU default locale with its UI locale,
- // this ICU locale tells the current UI locale of Chrome.
- Locale locale = Locale::getDefault();
- ScriptToFontMap::const_iterator iter;
- if (locale == Locale::getJapanese()) {
- iter = new_instance->find(USCRIPT_HIRAGANA);
- } else if (locale == Locale::getKorean()) {
- iter = new_instance->find(USCRIPT_HANGUL);
- } else {
- // Use Simplified Chinese font for all other locales including
- // Traditional Chinese because Simsun (SC font) has a wider
- // coverage (covering both SC and TC) than PMingLiu (TC font).
- // This also speeds up the TC version of Chrome when rendering SC pages.
- iter = new_instance->find(USCRIPT_SIMPLIFIED_HAN);
- }
- if (iter != new_instance->end())
- (*new_instance)[USCRIPT_HAN] = iter->second;
-
- return new_instance;
- }
-};
-
-Singleton<ScriptToFontMap, ScriptToFontMapSingletonTraits> script_font_map;
-
-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) {
- HDC dc = GetDC(NULL);
- HGDIOBJ oldFont = SelectObject(dc, hfont);
- TEXTMETRIC tm;
- BOOL got_metrics = GetTextMetrics(dc, &tm);
- SelectObject(dc, oldFont);
- ReleaseDC(NULL, dc);
- return got_metrics ? tm.tmAscent : kUndefinedAscent;
-}
-
-struct FontData {
- FontData() : hfont(NULL), ascent(kUndefinedAscent), script_cache(NULL) {}
- HFONT hfont;
- int ascent;
- mutable SCRIPT_CACHE script_cache;
-};
-
-// 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 std::map<std::wstring, FontData*> FontDataCache;
-struct FontDataCacheSingletonTraits
- : public DefaultSingletonTraits<FontDataCache> {
- static void Delete(FontDataCache* cache) {
- FontDataCache::iterator iter = cache->begin();
- while (iter != cache->end()) {
- SCRIPT_CACHE script_cache = iter->second->script_cache;
- if (script_cache)
- ScriptFreeCache(&script_cache);
- delete iter->second;
- ++iter;
- }
- delete cache;
- }
-};
-
-} // namespace
-
-// TODO(jungshik) : this is font fallback code version 0.1
-// - Cover all the scripts
-// - Get the default font for each script/generic family from the
-// preference instead of hardcoding in the source.
-// (at least, read values from the registry for IE font settings).
-// - Support generic families (from FontDescription)
-// - If the default font for a script is not available,
-// try some more fonts known to support it. Finally, we can
-// use EnumFontFamilies or similar APIs to come up with a list of
-// fonts supporting the script and cache the result.
-// - Consider using UnicodeSet (or UnicodeMap) converted from
-// GLYPHSET (BMP) or directly read from truetype cmap tables to
-// keep track of which character is supported by which font
-// - Update script_font_cache in response to WM_FONTCHANGE
-
-const wchar_t* GetFontFamilyForScript(UScriptCode script,
- GenericFamilyType generic) {
- ScriptToFontMap::const_iterator iter = script_font_map->find(script);
- const wchar_t* family = NULL;
- if (iter != script_font_map->end()) {
- family = iter->second;
- }
- return family;
-}
-
-// TODO(jungshik)
-// - Handle 'Inherited', 'Common' and 'Unknown'
-// (see http://www.unicode.org/reports/tr24/#Usage_Model )
-// For 'Inherited' and 'Common', perhaps we need to
-// accept another parameter indicating the previous family
-// and just return it.
-// - All the characters (or characters up to the point a single
-// font can cover) need to be taken into account
-const wchar_t* GetFallbackFamily(const wchar_t *characters,
- int length,
- GenericFamilyType generic,
- UChar32 *char_checked,
- UScriptCode *script_checked) {
- DCHECK(characters && characters[0] && length > 0);
- UScriptCode script = USCRIPT_COMMON;
-
- // Sometimes characters common to script (e.g. space) is at
- // the beginning of a string so that we need to skip them
- // to get a font required to render the string.
- int i = 0;
- UChar32 ucs4 = 0;
- while (i < length && script == USCRIPT_COMMON ||
- script == USCRIPT_INVALID_CODE) {
- U16_NEXT(characters, i, length, ucs4);
- UErrorCode err = U_ZERO_ERROR;
- script = uscript_getScript(ucs4, &err);
- // silently ignore the error
- }
-
- // hack for full width ASCII. For the full-width ASCII, use the font
- // for Han (which is locale-dependent).
- if (0xFF00 < ucs4 && ucs4 < 0xFF5F)
- script = USCRIPT_HAN;
-
- // There are a lot of characters in USCRIPT_COMMON that can be covered
- // by fonts for scripts closely related to them.
- // See http://unicode.org/cldr/utility/list-unicodeset.jsp?a=[:Script=Common:]
- // TODO(jungshik): make this more efficient with a wider coverage
- if (script == USCRIPT_COMMON || script == USCRIPT_INHERITED) {
- UBlockCode block = ublock_getCode(ucs4);
- switch (block) {
- case UBLOCK_BASIC_LATIN:
- script = USCRIPT_LATIN;
- break;
- case UBLOCK_CJK_SYMBOLS_AND_PUNCTUATION:
- script = USCRIPT_HAN;
- break;
- case UBLOCK_HIRAGANA:
- case UBLOCK_KATAKANA:
- script = USCRIPT_HIRAGANA;
- break;
- case UBLOCK_ARABIC:
- script = USCRIPT_ARABIC;
- break;
- case UBLOCK_GREEK:
- script = USCRIPT_GREEK;
- break;
- case UBLOCK_DEVANAGARI:
- // For Danda and Double Danda (U+0964, U+0965), use a Devanagari
- // font for now although they're used by other scripts as well.
- // Without a context, we can't do any better.
- script = USCRIPT_DEVANAGARI;
- break;
- case UBLOCK_ARMENIAN:
- script = USCRIPT_ARMENIAN;
- break;
- case UBLOCK_GEORGIAN:
- script = USCRIPT_GEORGIAN;
- break;
- case UBLOCK_KANNADA:
- script = USCRIPT_KANNADA;
- break;
- }
- }
-
- // Another lame work-around to cover non-BMP characters.
- const wchar_t* family = GetFontFamilyForScript(script, generic);
- if (!family) {
- int plane = ucs4 >> 16;
- switch (plane) {
- case 1:
- family = L"code2001";
- break;
- case 2:
- family = L"simsun-extb";
- break;
- default:
- family = L"lucida sans unicode";
- }
- }
-
- if (char_checked) *char_checked = ucs4;
- if (script_checked) *script_checked = script;
- return family;
-}
-
-
-
-// Be aware that this is not thread-safe.
-bool GetDerivedFontData(const wchar_t *family,
- int style,
- LOGFONT *logfont,
- int *ascent,
- HFONT *hfont,
- SCRIPT_CACHE **script_cache) {
- DCHECK(logfont && family && *family);
- // Using |Singleton| here is not free, but the intl2 page cycler test
- // does not show any noticeable difference with and without it. Leaking
- // the contents of FontDataCache (especially SCRIPT_CACHE) at the end
- // of a renderer process may not be a good idea. We may use
- // atexit(). However, with no noticeable performance difference, |Singleton|
- // is cleaner, I believe.
- FontDataCache* font_data_cache =
- Singleton<FontDataCache, FontDataCacheSingletonTraits>::get();
- // TODO(jungshik) : 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 StringPrintf.
- std::wstring font_key = StringPrintf(L"%1d:%d:%ls", style, logfont->lfHeight,
- family);
- FontDataCache::const_iterator iter = font_data_cache->find(font_key);
- FontData *derived;
- if (iter == font_data_cache->end()) {
- DCHECK(wcslen(family) < LF_FACESIZE);
- wcscpy_s(logfont->lfFaceName, LF_FACESIZE, family);
- // TODO(jungshik): 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)
- derived = new FontData;
- 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);
- (*font_data_cache)[font_key] = derived;
- } else {
- derived = iter->second;
- // Last time, GetAscent 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);
- }
- *hfont = derived->hfont;
- *ascent = derived->ascent;
- *script_cache = &(derived->script_cache);
- return *ascent != kUndefinedAscent;
-}
-
-int GetStyleFromLogfont(const LOGFONT* logfont) {
- // TODO(jungshik) : consider defining UNDEFINED or INVALID for style and
- // returning it when logfont is NULL
- if (!logfont) {
- NOTREACHED();
- return FONT_STYLE_NORMAL;
- }
- return (logfont->lfItalic ? FONT_STYLE_ITALIC : FONT_STYLE_NORMAL) |
- (logfont->lfUnderline ? FONT_STYLE_UNDERLINED : FONT_STYLE_NORMAL) |
- (logfont->lfWeight >= 700 ? FONT_STYLE_BOLD : FONT_STYLE_NORMAL);
-}
-
-} // namespace gfx
-
diff --git a/base/gfx/uniscribe.cc b/base/gfx/uniscribe.cc
deleted file mode 100644
index bdf4154..0000000
--- a/base/gfx/uniscribe.cc
+++ /dev/null
@@ -1,848 +0,0 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <windows.h>
-
-#include "base/gfx/uniscribe.h"
-
-#include "base/gfx/font_utils.h"
-#include "base/logging.h"
-
-namespace gfx {
-
-// This function is used to see where word spacing should be applied inside
-// runs. Note that this must match Font::treatAsSpace so we all agree where
-// and how much space this is, so we don't want to do more general Unicode
-// "is this a word break" thing.
-static bool TreatAsSpace(wchar_t c) {
- return c == ' ' || c == '\t' || c == '\n' || c == 0x00A0;
-}
-
-// SCRIPT_FONTPROPERTIES contains glyph indices for default, invalid
-// and blank glyphs. Just because ScriptShape succeeds does not mean
-// that a text run is rendered correctly. Some characters may be rendered
-// with default/invalid/blank glyphs. Therefore, we need to check if the glyph
-// array returned by ScriptShape contains any of those glyphs to make
-// sure that the text run is rendered successfully.
-static bool ContainsMissingGlyphs(WORD *glyphs,
- int length,
- SCRIPT_FONTPROPERTIES* properties) {
- for (int i = 0; i < length; ++i) {
- if (glyphs[i] == properties->wgDefault ||
- (glyphs[i] == properties->wgInvalid && glyphs[i] != properties->wgBlank))
- return true;
- }
-
- return false;
-}
-
-// HFONT is the 'incarnation' of 'everything' about font, but it's an opaque
-// handle and we can't directly query it to make a new HFONT sharing
-// its characteristics (height, style, etc) except for family name.
-// This function uses GetObject to convert HFONT back to LOGFONT,
-// resets the fields of LOGFONT and calculates style to use later
-// for the creation of a font identical to HFONT other than family name.
-static void SetLogFontAndStyle(HFONT hfont, LOGFONT *logfont, int *style) {
- DCHECK(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 = gfx::GetStyleFromLogfont(logfont);
-}
-
-UniscribeState::UniscribeState(const wchar_t* input,
- int input_length,
- bool is_rtl,
- HFONT hfont,
- SCRIPT_CACHE* script_cache,
- SCRIPT_FONTPROPERTIES* font_properties)
- : input_(input),
- input_length_(input_length),
- is_rtl_(is_rtl),
- hfont_(hfont),
- script_cache_(script_cache),
- font_properties_(font_properties),
- directional_override_(false),
- inhibit_ligate_(false),
- letter_spacing_(0),
- space_width_(0),
- word_spacing_(0),
- ascent_(0) {
- logfont_.lfFaceName[0] = 0;
-}
-
-UniscribeState::~UniscribeState() {
-}
-
-void UniscribeState::InitWithOptionalLengthProtection(bool length_protection) {
- // 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 (input_length_ == 0 ||
- (length_protection && input_length_ > kMaxInputLength))
- return;
-
- FillRuns();
- FillShapes();
- FillScreenOrder();
-}
-
-int UniscribeState::Width() const {
- int width = 0;
- for (int item_index = 0; item_index < static_cast<int>(runs_->size());
- item_index++) {
- width += AdvanceForItem(item_index);
- }
- return width;
-}
-
-void UniscribeState::Justify(int additional_space) {
- // Count the total number of glyphs we have so we know how big to make the
- // buffers below.
- int total_glyphs = 0;
- for (size_t run = 0; run < runs_->size(); run++) {
- int run_idx = screen_order_[run];
- total_glyphs += static_cast<int>(shapes_[run_idx].glyph_length());
- }
- if (total_glyphs == 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.
- StackVector<SCRIPT_VISATTR, 64> visattr;
- visattr->resize(total_glyphs);
- StackVector<int, 64> advances;
- advances->resize(total_glyphs);
- StackVector<int, 64> justify;
- justify->resize(total_glyphs);
-
- // Build the packed input.
- int dest_index = 0;
- for (size_t run = 0; run < runs_->size(); run++) {
- int run_idx = screen_order_[run];
- const Shaping& shaping = shapes_[run_idx];
-
- for (int i = 0; i < shaping.glyph_length(); i++, dest_index++) {
- memcpy(&visattr[dest_index], &shaping.visattr[i], sizeof(SCRIPT_VISATTR));
- advances[dest_index] = shaping.advance[i];
- }
- }
-
- // The documentation for ScriptJustify is wrong, the parameter is the space
- // to add and not the width of the column you want.
- const int min_kashida = 1; // How do we decide what this should be?
- ScriptJustify(&visattr[0], &advances[0], total_glyphs, additional_space,
- min_kashida, &justify[0]);
-
- // Now we have to unpack the justification amounts back into the runs so
- // the glyph indices match.
- int global_glyph_index = 0;
- for (size_t run = 0; run < runs_->size(); run++) {
- int run_idx = screen_order_[run];
- Shaping& shaping = shapes_[run_idx];
-
- shaping.justify->resize(shaping.glyph_length());
- for (int i = 0; i < shaping.glyph_length(); i++, global_glyph_index++)
- shaping.justify[i] = justify[global_glyph_index];
- }
-}
-
-int UniscribeState::CharacterToX(int offset) const {
- HRESULT hr;
- DCHECK(offset <= input_length_);
-
- // 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 screen_idx = 0; screen_idx < runs_->size(); screen_idx++) {
- // Compute the length of this run.
- int item_idx = screen_order_[screen_idx];
- const SCRIPT_ITEM& item = runs_[item_idx];
- const Shaping& shaping = shapes_[item_idx];
- int item_length = shaping.char_length();
-
- if (offset >= item.iCharPos && offset <= item.iCharPos + item_length) {
- // Character offset is in this run.
- int char_len = offset - item.iCharPos;
-
- int cur_x = 0;
- hr = ScriptCPtoX(char_len, FALSE, item_length, shaping.glyph_length(),
- &shaping.logs[0], &shaping.visattr[0],
- shaping.effective_advances(), &item.a, &cur_x);
- if (FAILED(hr))
- return 0;
-
- width += cur_x + shaping.pre_padding;
- DCHECK(width >= 0);
- return width;
- }
-
- // Move to the next item.
- width += AdvanceForItem(item_idx);
- }
- DCHECK(width >= 0);
- return width;
-}
-
-int UniscribeState::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 screen_idx = 0; screen_idx < runs_->size(); screen_idx++) {
- int item_idx = screen_order_[screen_idx];
- int advance_for_item = AdvanceForItem(item_idx);
-
- // Note that the run may be empty if shaping failed, so we want to skip
- // over it.
- const Shaping& shaping = shapes_[item_idx];
- int item_length = shaping.char_length();
- if (x <= advance_for_item && item_length > 0) {
- // The requested offset is within this item.
- const SCRIPT_ITEM& item = runs_[item_idx];
-
- // Account for the leading space we've added to this run that Uniscribe
- // doesn't know about.
- x -= shaping.pre_padding;
-
- int char_x = 0;
- int trailing;
- hr = ScriptXtoCP(x, item_length, shaping.glyph_length(),
- &shaping.logs[0], &shaping.visattr[0],
- shaping.effective_advances(), &item.a, &char_x,
- &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 char_x + item.iCharPos;
- }
-
- // The offset is beyond this item, account for its length and move on.
- x -= advance_for_item;
- }
-
- // 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 UniscribeState::Draw(HDC dc, int x, int y, int from, int to) {
- HGDIOBJ old_font = 0;
- int cur_x = x;
- bool first_run = true;
-
- for (size_t screen_idx = 0; screen_idx < runs_->size(); screen_idx++) {
- int item_idx = screen_order_[screen_idx];
- const SCRIPT_ITEM& item = runs_[item_idx];
- const Shaping& shaping = shapes_[item_idx];
-
- // Character offsets within this run. THESE MAY NOT BE IN RANGE and may
- // be negative, etc. The code below handles this.
- int from_char = from - item.iCharPos;
- int to_char = to - item.iCharPos;
-
- // See if we need to draw any characters in this item.
- if (shaping.char_length() == 0 ||
- from_char >= shaping.char_length() || to_char <= 0) {
- // No chars in this item to display.
- cur_x += AdvanceForItem(item_idx);
- continue;
- }
-
- // Compute the starting glyph within this span. |from| and |to| are
- // global offsets that may intersect arbitrarily with our local run.
- int from_glyph, after_glyph;
- if (item.a.fRTL) {
- // To compute the first glyph when going RTL, we use |to|.
- if (to_char >= shaping.char_length()) {
- // The end of the text is after (to the left) of us.
- from_glyph = 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|.
- from_glyph = shaping.logs[to_char - 1];
- }
-
- // The last glyph is actually the first character in the range.
- if (from_char <= 0) {
- // The first character to draw is before (to the right) of this span,
- // so draw all the way to the end.
- after_glyph = shaping.glyph_length();
- } 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).
- after_glyph = shaping.logs[from_char - 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.
- from_glyph = shaping.logs[from_char < 0 ? 0 : from_char];
- if (to_char >= shaping.char_length())
- after_glyph = shaping.glyph_length();
- else
- after_glyph = shaping.logs[to_char];
- }
-
- // 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* effective_advances = shaping.effective_advances();
- int inner_offset = 0;
- for (int i = 0; i < from_glyph; i++)
- inner_offset += effective_advances[i];
-
- // Actually draw the glyphs we found.
- int glyph_count = after_glyph - from_glyph;
- if (from_glyph >= 0 && glyph_count > 0) {
- // Account for the preceeding 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.
- inner_offset += shaping.pre_padding;
-
- // Pass NULL in when there is no justification.
- const int* justify = shaping.justify->empty() ?
- NULL : &shaping.justify[from_glyph];
-
- if (first_run) {
- old_font = SelectObject(dc, shaping.hfont_);
- first_run = false;
- } else {
- SelectObject(dc, shaping.hfont_);
- }
-
- // TODO(brettw) bug 698452: if a half a character is selected,
- // we should set up a clip rect so we draw the half of the glyph
- // correctly.
- // Fonts with different ascents can be used to render different runs.
- // 'Across-runs' y-coordinate correction needs to be adjusted
- // for each font.
- HRESULT hr = S_FALSE;
- for (int executions = 0; executions < 2; ++executions) {
- hr = ScriptTextOut(dc, shaping.script_cache_, cur_x + inner_offset,
- y - shaping.ascent_offset_, 0, NULL, &item.a, NULL,
- 0, &shaping.glyphs[from_glyph],
- glyph_count, &shaping.advance[from_glyph],
- justify, &shaping.offsets[from_glyph]);
- if (S_OK != hr && 0 == executions) {
- // If this ScriptTextOut 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.hfont_);
- continue;
- }
- break;
- }
-
- DCHECK(S_OK == hr);
-
-
- }
-
- cur_x += AdvanceForItem(item_idx);
- }
-
- if (old_font)
- SelectObject(dc, old_font);
-}
-
-WORD UniscribeState::FirstGlyphForCharacter(int char_offset) const {
- // Find the run for the given character.
- for (int i = 0; i < static_cast<int>(runs_->size()); i++) {
- int first_char = runs_[i].iCharPos;
- const Shaping& shaping = shapes_[i];
- int local_offset = char_offset - first_char;
- if (local_offset >= 0 && local_offset < shaping.char_length()) {
- // 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 glyph_index = shaping.logs[local_offset];
- if (glyph_index >= shaping.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.
- DCHECK(shaping.glyphs->empty());
- return 0;
- }
- return shaping.glyphs[glyph_index];
- }
- }
- return 0;
-}
-
-void UniscribeState::FillRuns() {
- HRESULT hr;
- runs_->resize(UNISCRIBE_STATE_STACK_RUNS);
-
- SCRIPT_STATE input_state;
- input_state.uBidiLevel = is_rtl_;
- input_state.fOverrideDirection = directional_override_;
- input_state.fInhibitSymSwap = false;
- input_state.fCharShape = false; // Not implemented in Uniscribe
- input_state.fDigitSubstitute = false; // Do we want this for Arabic?
- input_state.fInhibitLigate = inhibit_ligate_;
- input_state.fDisplayZWG = false; // Don't draw control characters.
- input_state.fArabicNumContext = is_rtl_; // Do we want this for Arabic?
- input_state.fGcpClusters = false;
- input_state.fReserved = 0;
- input_state.fEngineReserved = 0;
- // The psControl argument to ScriptItemize should be non-NULL 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 input_control = {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( NULL, &input_control, &input_state)
- // 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 num_items = 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.
- hr = ScriptItemize(input_, input_length_,
- static_cast<int>(runs_->size()) - 1, &input_control, &input_state,
- &runs_[0], &num_items);
- if (SUCCEEDED(hr)) {
- runs_->resize(num_items);
- break;
- }
- if (hr != E_OUTOFMEMORY) {
- // Some kind of unexpected error.
- runs_->resize(0);
- break;
- }
- // There was not enough items for it to write into, expand.
- runs_->resize(runs_->size() * 2);
- }
-
- // Fix up the directions of the items so they're what WebKit thinks
- // they are. WebKit (and we assume any other caller) always knows what
- // direction it wants things to be in, and will only give us runs that are in
- // the same direction. Sometimes, Uniscibe disagrees, for example, if you
- // have embedded ASCII punctuation in an Arabic string, WebKit will
- // (correctly) know that is should still be rendered RTL, but Uniscibe might
- // think LTR is better.
- //
- // TODO(brettw) bug 747235:
- // This workaround fixes the bug but causes spacing problems in other cases.
- // WebKit sometimes gives us a big run that includes ASCII and Arabic, and
- // this forcing direction makes those cases incorrect. This seems to happen
- // during layout only, so it ends up that spacing is incorrect (because being
- // the wrong direction changes ligatures and stuff).
- //
- //for (size_t i = 0; i < runs_->size(); i++)
- // runs_[i].a.fRTL = is_rtl_;
-}
-
-
-bool UniscribeState::Shape(const wchar_t* input,
- int item_length,
- int num_glyphs,
- SCRIPT_ITEM& run,
- Shaping& shaping) {
- HFONT hfont = hfont_;
- SCRIPT_CACHE* script_cache = script_cache_;
- SCRIPT_FONTPROPERTIES* font_properties = font_properties_;
- int ascent = ascent_;
- HDC temp_dc = NULL;
- HGDIOBJ old_font = 0;
- HRESULT hr;
- bool lastFallbackTried = false;
- bool result;
-
- int generated_glyphs = 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.logs->resize(item_length);
- shaping.glyphs->resize(num_glyphs);
- shaping.visattr->resize(num_glyphs);
-
- // Firefox sets SCRIPT_ANALYSIS.SCRIPT_STATE.fDisplayZWG to true
- // here. Is that what we want? It will display control characters.
- hr = ScriptShape(temp_dc, script_cache, input, item_length,
- num_glyphs, &run.a,
- &shaping.glyphs[0], &shaping.logs[0],
- &shaping.visattr[0], &generated_glyphs);
- if (hr == E_PENDING) {
- // Allocate the DC.
- temp_dc = GetDC(NULL);
- old_font = SelectObject(temp_dc, hfont);
- continue;
- } else if (hr == E_OUTOFMEMORY) {
- num_glyphs *= 2;
- continue;
- } else if (SUCCEEDED(hr) &&
- (lastFallbackTried || !ContainsMissingGlyphs(&shaping.glyphs[0],
- generated_glyphs, font_properties))) {
- break;
- }
-
- // The current font can't render this run. clear DC and try
- // next font.
- if (temp_dc) {
- SelectObject(temp_dc, old_font);
- ReleaseDC(NULL, temp_dc);
- temp_dc = NULL;
- }
-
- if (NextWinFontData(&hfont, &script_cache, &font_properties, &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 (!logfont_.lfFaceName[0])
- SetLogFontAndStyle(hfont_, &logfont_, &style_);
-
- // TODO(jungshik): generic type should come from webkit for
- // UniscribeStateTextRun (a derived class used in webkit).
- const wchar_t *family = GetFallbackFamily(input, item_length,
- GENERIC_FAMILY_STANDARD, NULL, NULL);
- bool font_ok = GetDerivedFontData(family, style_, &logfont_, &ascent, &hfont, &script_cache);
-
- if (!font_ok) {
- // 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.
- font_ok = GetDerivedFontData(family, style_, &logfont_, &ascent, &hfont, &script_cache);
- DCHECK(font_ok);
- }
-
- // TODO(jungshik) : Currently GetDerivedHFont always returns a
- // a valid HFONT, but in the future, I may change it to return 0.
- DCHECK(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.
- generated_glyphs = 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.hfont_ = hfont;
- shaping.script_cache_ = script_cache;
-
- // 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.ascent_offset_ = ascent_ ? ascent - ascent_ : 0;
- result = true;
-
-cleanup:
- shaping.glyphs->resize(generated_glyphs);
- shaping.visattr->resize(generated_glyphs);
- shaping.advance->resize(generated_glyphs);
- shaping.offsets->resize(generated_glyphs);
- if (temp_dc) {
- SelectObject(temp_dc, old_font);
- ReleaseDC(NULL, temp_dc);
- }
- // On failure, our logs don't mean anything, so zero those out.
- if (!result)
- shaping.logs->clear();
-
- return result;
-}
-
-void UniscribeState::FillShapes() {
- shapes_->resize(runs_->size());
- for (size_t i = 0; i < runs_->size(); i++) {
- int start_item = runs_[i].iCharPos;
- int item_length = input_length_ - start_item;
- if (i < runs_->size() - 1)
- item_length = runs_[i + 1].iCharPos - start_item;
-
- int num_glyphs;
- if (item_length < UNISCRIBE_STATE_STACK_CHARS) {
- // 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.
- num_glyphs = UNISCRIBE_STATE_STACK_CHARS;
- } 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.
- num_glyphs = item_length * 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 = shapes_[i];
- if (!Shape(&input_[start_item], item_length, num_glyphs, runs_[i], shaping))
- continue;
-
- // Compute placements. Note that offsets is documented incorrectly
- // and is actually an array.
-
- // DC that we lazily create if Uniscribe commands us to.
- // (this does not happen often because script_cache is already
- // updated when calling ScriptShape).
- HDC temp_dc = NULL;
- HGDIOBJ old_font = NULL;
- HRESULT hr;
- while (true) {
- shaping.pre_padding = 0;
- hr = ScriptPlace(temp_dc, shaping.script_cache_, &shaping.glyphs[0],
- static_cast<int>(shaping.glyphs->size()),
- &shaping.visattr[0], &runs_[i].a,
- &shaping.advance[0], &shaping.offsets[0],
- &shaping.abc);
- if (hr != E_PENDING)
- break;
-
- // Allocate the DC and run the loop again.
- temp_dc = GetDC(NULL);
- old_font = SelectObject(temp_dc, shaping.hfont_);
- }
-
- if (FAILED(hr)) {
- // Some error we don't know how to handle. Nuke all of our data
- // since we can't deal with partially valid data later.
- runs_->clear();
- shapes_->clear();
- screen_order_->clear();
- }
-
- if (temp_dc) {
- SelectObject(temp_dc, old_font);
- ReleaseDC(NULL, temp_dc);
- }
- }
-
- AdjustSpaceAdvances();
-
- if (letter_spacing_ != 0 || word_spacing_ != 0)
- ApplySpacing();
-}
-
-void UniscribeState::FillScreenOrder() {
- screen_order_->resize(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 (is_rtl_) {
- for (int i = 0; i < static_cast<int>(screen_order_->size()); i++)
- screen_order_[static_cast<int>(screen_order_->size()) - i - 1] = i;
- } else {
- for (int i = 0; i < static_cast<int>(screen_order_->size()); i++)
- screen_order_[i] = i;
- }
-}
-
-void UniscribeState::AdjustSpaceAdvances() {
- if (space_width_ == 0)
- return;
-
- int space_width_without_letter_spacing = space_width_ - letter_spacing_;
-
- // This mostly matches what WebKit's UniscribeController::shapeAndPlaceItem.
- for (size_t run = 0; run < runs_->size(); run++) {
- Shaping& shaping = shapes_[run];
-
- for (int i = 0; i < shaping.char_length(); i++) {
- if (!TreatAsSpace(input_[runs_[run].iCharPos + i]))
- continue;
-
- int glyph_index = shaping.logs[i];
- int current_advance = shaping.advance[glyph_index];
- // Don't give zero-width spaces a width.
- if (!current_advance)
- continue;
-
- // current_advance does not include additional letter-spacing, but
- // space_width does. Here we find out how off we are from the correct
- // width for the space not including letter-spacing, then just subtract
- // that diff.
- int diff = current_advance - space_width_without_letter_spacing;
- // The shaping can consist of a run of text, so only subtract the
- // difference in the width of the glyph.
- shaping.advance[glyph_index] -= diff;
- shaping.abc.abcB -= diff;
- }
- }
-}
-
-void UniscribeState::ApplySpacing() {
- for (size_t run = 0; run < runs_->size(); run++) {
- Shaping& shaping = shapes_[run];
- bool is_rtl = runs_[run].a.fRTL;
-
- if (letter_spacing_ != 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 (is_rtl)
- shaping.pre_padding += letter_spacing_;
-
- // 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.glyph_length(); i++) {
- if (shaping.visattr[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 (is_rtl) {
- if (i != shaping.glyph_length() - 1) {
- // All but the last character just get the spacing applied to
- // their advance. The last character doesn't get anything,
- shaping.advance[i] += letter_spacing_;
- shaping.abc.abcB += letter_spacing_;
- }
- } else {
- // LTR case is easier, we just add to the advance.
- shaping.advance[i] += letter_spacing_;
- shaping.abc.abcB += letter_spacing_;
- }
- }
- }
- }
-
- // Go through all the characters to find whitespace and insert the extra
- // wordspacing amount for the glyphs they correspond to.
- if (word_spacing_ != 0) {
- for (int i = 0; i < shaping.char_length(); i++) {
- if (!TreatAsSpace(input_[runs_[run].iCharPos + i]))
- continue;
-
- // The char in question is a word separator...
- int glyph_index = shaping.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 (is_rtl) {
- // 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.advance[glyph_index] += word_spacing_;
- shaping.abc.abcB += word_spacing_;
- } 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 (glyph_index == 0) {
- shaping.pre_padding += word_spacing_;
- } else {
- shaping.advance[glyph_index - 1] += word_spacing_;
- shaping.abc.abcB += word_spacing_;
- }
- }
- }
- } // word_spacing_ != 0
-
- // Loop for next run...
- }
-}
-
-// The advance is the ABC width of the run
-int UniscribeState::AdvanceForItem(int item_index) const {
- int accum = 0;
- const Shaping& shaping = shapes_[item_index];
-
- if (shaping.justify->empty()) {
- // Easy case with no justification, the width is just the ABC width of t
- // the run. (The ABC width is the sum of the advances).
- return shaping.abc.abcA + shaping.abc.abcB + shaping.abc.abcC +
- shaping.pre_padding;
- }
-
- // 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.justify->size(); i++)
- justification += shaping.justify[i];
-
- return shaping.pre_padding + justification;
-}
-
-} // namespace gfx
-
diff --git a/base/gfx/uniscribe.h b/base/gfx/uniscribe.h
deleted file mode 100644
index 162e577..0000000
--- a/base/gfx/uniscribe.h
+++ /dev/null
@@ -1,366 +0,0 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-// A wrapper around Uniscribe that provides a reasonable API.
-
-#ifndef BASE_GFX_UNISCRIBE_H__
-#define BASE_GFX_UNISCRIBE_H__
-
-#include <windows.h>
-#include <usp10.h>
-#include <wchar.h>
-#include <map>
-#include <vector>
-
-#include "base/stack_container.h"
-#include "testing/gtest/include/gtest/gtest_prod.h"
-
-namespace gfx {
-
-#define UNISCRIBE_STATE_STACK_RUNS 8
-#define UNISCRIBE_STATE_STACK_CHARS 32
-
-// This object should be safe to create & destroy frequently, as long as the
-// caller preserves the script_cache when possible (this data may be slow to
-// compute).
-//
-// This object is "kind of large" (~1K) because it reserves a lot of space for
-// working with to avoid expensive heap operations. Therefore, not only should
-// you not worry about creating and destroying it, you should try to not keep
-// them around.
-class UniscribeState {
- public:
- // Initializes this Uniscribe run with the text pointed to by |run| with
- // |length|. The input is NOT null terminated.
- //
- // The is_rtl flag should be set if the input script is RTL. It is assumed
- // that the caller has already divided up the input text (using ICU, for
- // example) into runs of the same direction of script. This avoids
- // disagreements between the caller and Uniscribe later (see FillItems).
- //
- // A script cache should be provided by the caller that is initialized to
- // NULL. When the caller is done with the cache (it may be stored between
- // runs as long as it is used consistently with the same HFONT), it should
- // call ScriptFreeCache().
- UniscribeState(const wchar_t* input,
- int input_length,
- bool is_rtl,
- HFONT hfont,
- SCRIPT_CACHE* script_cache,
- SCRIPT_FONTPROPERTIES* font_properties);
-
- virtual ~UniscribeState();
-
- // Sets Uniscribe's directional override flag. False by default.
- bool directional_override() const {
- return directional_override_;
- }
- void set_directional_override(bool override) {
- directional_override_ = override;
- }
-
- // Set's Uniscribe's no-ligate override flag. False by default.
- bool inhibit_ligate() const {
- return inhibit_ligate_;
- }
- void set_inhibit_ligate(bool inhibit) {
- inhibit_ligate_ = inhibit;
- }
-
- // Set letter spacing. We will try to insert this much space between
- // graphemes (one or more glyphs perceived as a single unit by ordinary users
- // of a script). Positive values increase letter spacing, negative values
- // decrease it. 0 by default.
- int letter_spacing() const {
- return letter_spacing_;
- }
- void set_letter_spacing(int letter_spacing) {
- letter_spacing_ = letter_spacing;
- }
-
- // Set the width of a standard space character. We use this to normalize
- // space widths. Windows will make spaces after Hindi characters larger than
- // other spaces. A space_width of 0 means to use the default space width.
- //
- // Must be set before Init() is called.
- int space_width() const {
- return space_width_;
- }
- void set_space_width(int space_width) {
- space_width_ = space_width;
- }
-
- // Set word spacing. We will try to insert this much extra space between
- // each word in the input (beyond whatever whitespace character separates
- // words). Positive values lead to increased letter spacing, negative values
- // decrease it. 0 by default.
- //
- // Must be set before Init() is called.
- int word_spacing() const {
- return word_spacing_;
- }
- void set_word_spacing(int word_spacing) {
- word_spacing_ = word_spacing;
- }
- void set_ascent(int ascent) {
- ascent_ = ascent;
- }
-
- // You must call this after setting any options but before doing any
- // other calls like asking for widths or drawing.
- void Init() { InitWithOptionalLengthProtection(true); }
-
- // Returns the total width in pixels of the text run.
- int Width() const;
-
- // Call to justify the text, with the amount of space that should be ADDED to
- // get the desired width that the column should be justified to. Normally,
- // spaces are inserted, but for Arabic there will be kashidas (extra strokes)
- // inserted instead.
- //
- // This function MUST be called AFTER Init().
- void Justify(int additional_space);
-
- // Computes the given character offset into a pixel offset of the beginning
- // of that character.
- int CharacterToX(int offset) const;
-
- // Converts the given pixel X position into a logical character offset into
- // the run. For positions appearing before the first character, this will
- // return -1.
- int XToCharacter(int x) const;
-
- // Draws the given characters to (x, y) in the given DC. The font will be
- // handled by this function, but the font color and other attributes should
- // be pre-set.
- //
- // The y position is the upper left corner, NOT the baseline.
- void Draw(HDC dc, int x, int y, int from, int to);
-
- // Returns the first glyph assigned to the character at the given offset.
- // This function is used to retrieve glyph information when Uniscribe is
- // being used to generate glyphs for non-complex, non-BMP (above U+FFFF)
- // characters. These characters are not otherwise special and have no
- // complex shaping rules, so we don't otherwise need Uniscribe, except
- // Uniscribe is the only way to get glyphs for non-BMP characters.
- //
- // Returns 0 if there is no glyph for the given character.
- WORD FirstGlyphForCharacter(int char_offset) const;
-
- protected:
- // Backend for init. The flag allows the unit test to specify whether we
- // should fail early for very long strings like normal, or try to pass the
- // long string to Uniscribe. The latter provides a way to force failure of
- // shaping.
- void InitWithOptionalLengthProtection(bool length_protection);
-
- // Tries to preload the font when the it is not accessible.
- // This is the default implementation and it does not do anything.
- virtual void TryToPreloadFont(HFONT font) {}
-
- private:
- FRIEND_TEST(UniscribeTest, TooBig);
-
- // An array corresponding to each item in runs_ containing information
- // on each of the glyphs that were generated. Like runs_, this is in
- // reading order. However, for rtl text, the characters within each
- // item will be reversed.
- struct Shaping {
- Shaping()
- : pre_padding(0),
- hfont_(NULL),
- script_cache_(NULL),
- ascent_offset_(0) {
- abc.abcA = 0;
- abc.abcB = 0;
- abc.abcC = 0;
- }
-
- // Returns the number of glyphs (which will be drawn to the screen)
- // in this run.
- int glyph_length() const {
- return static_cast<int>(glyphs->size());
- }
-
- // Returns the number of characters (that we started with) in this run.
- int char_length() const {
- return static_cast<int>(logs->size());
- }
-
- // Returns the advance array that should be used when measuring glyphs.
- // The returned pointer will indicate an array with glyph_length() elements
- // and the advance that should be used for each one. This is either the
- // real advance, or the justified advances if there is one, and is the
- // array we want to use for measurement.
- const int* effective_advances() const {
- if (advance->empty())
- return 0;
- if (justify->empty())
- return &advance[0];
- return &justify[0];
- }
-
- // This is the advance amount of space that we have added to the beginning
- // of the run. It is like the ABC's |A| advance but one that we create and
- // must handle internally whenever computing with pixel offsets.
- int pre_padding;
-
- // Glyph indices in the font used to display this item. These indices
- // are in screen order.
- StackVector<WORD, UNISCRIBE_STATE_STACK_CHARS> glyphs;
-
- // For each input character, this tells us the first glyph index it
- // generated. This is the only array with size of the input chars.
- //
- // All offsets are from the beginning of this run. Multiple characters can
- // generate one glyph, in which case there will be adjacent duplicates in
- // this list. One character can also generate multiple glyphs, in which
- // case there will be skipped indices in this list.
- StackVector<WORD, UNISCRIBE_STATE_STACK_CHARS> logs;
-
- // Flags and such for each glyph.
- StackVector<SCRIPT_VISATTR, UNISCRIBE_STATE_STACK_CHARS> visattr;
-
- // Horizontal advances for each glyph listed above, this is basically
- // how wide each glyph is.
- StackVector<int, UNISCRIBE_STATE_STACK_CHARS> advance;
-
- // This contains glyph offsets, from the nominal position of a glyph. It
- // is used to adjust the positions of multiple combining characters
- // around/above/below base characters in a context-sensitive manner so
- // that they don't bump against each other and the base character.
- StackVector<GOFFSET, UNISCRIBE_STATE_STACK_CHARS> offsets;
-
- // Filled by a call to Justify, this is empty for nonjustified text.
- // If nonempty, this contains the array of justify characters for each
- // character as returned by ScriptJustify.
- //
- // This is the same as the advance array, but with extra space added for
- // some characters. The difference between a glyph's |justify| width and
- // it's |advance| width is the extra space added.
- StackVector<int, UNISCRIBE_STATE_STACK_CHARS> justify;
-
- // Sizing information for this run. This treats the entire run as a
- // character with a preceeding advance, width, and ending advance.
- // The B width is the sum of the |advance| array, and the A and C widths
- // are any extra spacing applied to each end.
- //
- // It is unclear from the documentation what this actually means. From
- // experimentation, it seems that the sum of the character advances is
- // always the sum of the ABC values, and I'm not sure what you're supposed
- // to do with the ABC values.
- ABC abc;
-
- // Pointers to windows font data used to render this run.
- HFONT hfont_;
- SCRIPT_CACHE* script_cache_;
-
- // Ascent offset between the ascent of the primary font
- // and that of the fallback font. The offset needs to be applied,
- // when drawing a string, to align multiple runs rendered with
- // different fonts.
- int ascent_offset_;
- };
-
- // Computes the runs_ array from the text run.
- void FillRuns();
-
- // Computes the shapes_ array given an runs_ array already filled in.
- void FillShapes();
-
- // Fills in the screen_order_ array (see below).
- void FillScreenOrder();
-
- // Called to update the glyph positions based on the current spacing options
- // that are set.
- void ApplySpacing();
-
- // Normalizes all advances for spaces to the same width. This keeps windows
- // from making spaces after Hindi characters larger, which is then
- // inconsistent with our meaure of the width since WebKit doesn't include
- // spaces in text-runs sent to uniscribe unless white-space:pre.
- void AdjustSpaceAdvances();
-
- // Returns the total width of a single item.
- int AdvanceForItem(int item_index) const;
-
- // Shapes a run (pointed to by |input|) using |hfont| first.
- // Tries a series of fonts specified retrieved with NextWinFontData
- // and finally a font covering characters in |*input|. A string pointed
- // by |input| comes from ScriptItemize and is supposed to contain
- // characters belonging to a single script aside from characters
- // common to all scripts (e.g. space).
- bool Shape(const wchar_t* input,
- int item_length,
- int num_glyphs,
- SCRIPT_ITEM& run,
- Shaping& shaping);
-
- // Gets Windows font data for the next best font to try in the list
- // of fonts. When there's no more font available, returns false
- // without touching any of out params. Need to call ResetFontIndex
- // to start scanning of the font list from the beginning.
- virtual bool NextWinFontData(HFONT* hfont,
- SCRIPT_CACHE** script_cache,
- SCRIPT_FONTPROPERTIES** font_properties,
- int* ascent) {
- return false;
- }
-
- // Resets the font index to the first in the list of fonts
- // to try after the primaryFont turns out not to work. With font_index
- // reset, NextWinFontData scans fallback fonts from the beginning.
- virtual void ResetFontIndex() {}
-
- // The input data for this run of Uniscribe. See the constructor.
- const wchar_t* input_;
- const int input_length_;
- const bool is_rtl_;
-
- // Windows font data for the primary font :
- // In a sense, logfont_ and style_ are redundant because
- // hfont_ contains all the information. However, invoking GetObject,
- // everytime we need the height and the style, is rather expensive so
- // that we cache them. Would it be better to add getter and (virtual)
- // setter for the height and the style of the primary font, instead of
- // logfont_? Then, a derived class ctor can set ascent_, height_ and style_
- // if they're known. Getters for them would have to 'infer' their values from
- // hfont_ ONLY when they're not set.
- HFONT hfont_;
- SCRIPT_CACHE* script_cache_;
- SCRIPT_FONTPROPERTIES* font_properties_;
- int ascent_;
- LOGFONT logfont_;
- int style_;
-
- // Options, see the getters/setters above.
- bool directional_override_;
- bool inhibit_ligate_;
- int letter_spacing_;
- int space_width_;
- int word_spacing_;
- int justification_width_;
-
- // Uniscribe breaks the text into Runs. These are one length of text that is
- // in one script and one direction. This array is in reading order.
- StackVector<SCRIPT_ITEM, UNISCRIBE_STATE_STACK_RUNS> runs_;
-
- StackVector<Shaping, UNISCRIBE_STATE_STACK_RUNS> shapes_;
-
- // This is a mapping between reading order and screen order for the items.
- // Uniscribe's items array are in reading order. For right-to-left text,
- // or mixed (although WebKit's |TextRun| should really be only one
- // direction), this makes it very difficult to compute character offsets
- // and positions. This list is in screen order from left to right, and
- // gives the index into the |runs_| and |shapes_| arrays of each
- // subsequent item.
- StackVector<int, UNISCRIBE_STATE_STACK_RUNS> screen_order_;
-
- DISALLOW_EVIL_CONSTRUCTORS(UniscribeState);
-};
-
-} // namespace gfx
-
-#endif // BASE_GFX_UNISCRIBE_H__
-
diff --git a/base/gfx/uniscribe_unittest.cc b/base/gfx/uniscribe_unittest.cc
deleted file mode 100644
index bbad411..0000000
--- a/base/gfx/uniscribe_unittest.cc
+++ /dev/null
@@ -1,140 +0,0 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/gfx/uniscribe.h"
-#include "base/win_util.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-// This must be in the gfx namespace for the friend statements in uniscribe.h
-// to work.
-namespace gfx {
-
-namespace {
-
-class UniscribeTest : public testing::Test {
- public:
- UniscribeTest() {
- }
-
- // Returns an HFONT with the given name. The caller does not have to free
- // this, it will be automatically freed at the end of the test. Returns NULL
- // on failure. On success, the
- HFONT MakeFont(const wchar_t* font_name, SCRIPT_CACHE** cache) {
- LOGFONT lf;
- memset(&lf, 0, sizeof(LOGFONT));
- lf.lfHeight = 20;
- wcscpy_s(lf.lfFaceName, font_name);
-
- HFONT hfont = CreateFontIndirect(&lf);
- if (!hfont)
- return NULL;
-
- *cache = new SCRIPT_CACHE;
- **cache = NULL;
- created_fonts_.push_back(std::make_pair(hfont, *cache));
- return hfont;
- }
-
- protected:
- // Default font properties structure for tests to use.
- SCRIPT_FONTPROPERTIES properties_;
-
- private:
- virtual void SetUp() {
- memset(&properties_, 0, sizeof(SCRIPT_FONTPROPERTIES));
- properties_.cBytes = sizeof(SCRIPT_FONTPROPERTIES);
- properties_.wgBlank = ' ';
- properties_.wgDefault = '?'; // Used when the character is not in the font.
- properties_.wgInvalid = '#'; // Used for invalid characters.
- }
-
- virtual void TearDown() {
- // Free any allocated fonts.
- for (size_t i = 0; i < created_fonts_.size(); i++) {
- DeleteObject(created_fonts_[i].first);
- ScriptFreeCache(created_fonts_[i].second);
- delete created_fonts_[i].second;
- }
- created_fonts_.clear();
- }
-
- // Tracks allocated fonts so we can delete them at the end of the test.
- // The script cache pointer is heap allocated and must be freed.
- std::vector< std::pair<HFONT, SCRIPT_CACHE*> > created_fonts_;
-
- DISALLOW_EVIL_CONSTRUCTORS(UniscribeTest);
-};
-
-} // namespace
-
-// This test tests giving Uniscribe a very large buffer, which will cause a
-// failure.
-TEST_F(UniscribeTest, TooBig) {
- // This test will only run on Windows XP. It seems Uniscribe does not have the
- // internal limit on Windows 2000 that we rely on to cause this failure.
- if (win_util::GetWinVersion() <= win_util::WINVERSION_2000)
- return;
-
- // Make a large string with an e with a zillion combining accents.
- std::wstring input(L"e");
- for (int i = 0; i < 100000; i++)
- input.push_back(0x301); // Combining acute accent.
-
- SCRIPT_CACHE* script_cache;
- HFONT hfont = MakeFont(L"Times New Roman", &script_cache);
- ASSERT_TRUE(hfont);
-
- // Test a long string without the normal length protection we have. This will
- // cause shaping to fail.
- {
- gfx::UniscribeState uniscribe(input.data(), static_cast<int>(input.size()),
- false, hfont, script_cache, &properties_);
- uniscribe.InitWithOptionalLengthProtection(false);
-
- // There should be one shaping entry, with nothing in it.
- ASSERT_EQ(1, uniscribe.shapes_->size());
- EXPECT_EQ(0, uniscribe.shapes_[0].glyphs->size());
- EXPECT_EQ(0, uniscribe.shapes_[0].logs->size());
- EXPECT_EQ(0, uniscribe.shapes_[0].visattr->size());
- EXPECT_EQ(0, uniscribe.shapes_[0].advance->size());
- EXPECT_EQ(0, uniscribe.shapes_[0].offsets->size());
- EXPECT_EQ(0, uniscribe.shapes_[0].justify->size());
- EXPECT_EQ(0, uniscribe.shapes_[0].abc.abcA);
- EXPECT_EQ(0, uniscribe.shapes_[0].abc.abcB);
- EXPECT_EQ(0, uniscribe.shapes_[0].abc.abcC);
-
- // The sizes of the other stuff should match the shaping entry.
- EXPECT_EQ(1, uniscribe.runs_->size());
- EXPECT_EQ(1, uniscribe.screen_order_->size());
-
- // Check that the various querying functions handle the empty case properly.
- EXPECT_EQ(0, uniscribe.Width());
- EXPECT_EQ(0, uniscribe.FirstGlyphForCharacter(0));
- EXPECT_EQ(0, uniscribe.FirstGlyphForCharacter(1000));
- EXPECT_EQ(0, uniscribe.XToCharacter(0));
- EXPECT_EQ(0, uniscribe.XToCharacter(1000));
- }
-
- // Now test the very large string and make sure it is handled properly by the
- // length protection.
- {
- gfx::UniscribeState uniscribe(input.data(), static_cast<int>(input.size()),
- false, hfont, script_cache, &properties_);
- uniscribe.InitWithOptionalLengthProtection(true);
-
- // There should be 0 runs and shapes.
- EXPECT_EQ(0, uniscribe.runs_->size());
- EXPECT_EQ(0, uniscribe.shapes_->size());
- EXPECT_EQ(0, uniscribe.screen_order_->size());
-
- EXPECT_EQ(0, uniscribe.Width());
- EXPECT_EQ(0, uniscribe.FirstGlyphForCharacter(0));
- EXPECT_EQ(0, uniscribe.FirstGlyphForCharacter(1000));
- EXPECT_EQ(0, uniscribe.XToCharacter(0));
- EXPECT_EQ(0, uniscribe.XToCharacter(1000));
- }
-}
-
-} // namespace gfx
-
diff --git a/webkit/SConscript.port b/webkit/SConscript.port
index 681461c..cbb08d4 100644
--- a/webkit/SConscript.port
+++ b/webkit/SConscript.port
@@ -159,11 +159,14 @@ if env['PLATFORM'] == 'win32':
'$PORT_DIR/platform/win/SoundWin.cpp',
'$PORT_DIR/platform/graphics/FontCacheWin.cpp',
'$PORT_DIR/platform/graphics/FontPlatformDataWin.cpp',
+ '$PORT_DIR/platform/graphics/FontUtilsWin.cpp',
'$PORT_DIR/platform/graphics/FontWin.cpp',
'$PORT_DIR/platform/graphics/GlyphPageTreeNodeWin.cpp',
'$PORT_DIR/platform/graphics/IconWin.cpp',
'$PORT_DIR/platform/graphics/SimpleFontDataWin.cpp',
'$PORT_DIR/platform/graphics/ThemeHelperWin.cpp',
+ '$PORT_DIR/platform/graphics/UniscribeHelper.cpp',
+ '$PORT_DIR/platform/graphics/UniscribeHelperTextRun.cpp',
'$PORT_DIR/rendering/RenderThemeWin.cpp',
])
diff --git a/webkit/build/port/port.vcproj b/webkit/build/port/port.vcproj
index e272395..d521dda 100644
--- a/webkit/build/port/port.vcproj
+++ b/webkit/build/port/port.vcproj
@@ -671,14 +671,6 @@
RelativePath="..\..\port\platform\StateTrackingString.h"
>
</File>
- <File
- RelativePath="..\..\port\platform\UniscribeStateTextRun.cpp"
- >
- </File>
- <File
- RelativePath="..\..\port\platform\UniscribeStateTextRun.h"
- >
- </File>
<Filter
Name="win"
>
@@ -723,19 +715,19 @@
>
</File>
<File
- RelativePath="..\..\port\platform\chromium\ContextMenuChromium.cpp"
+ RelativePath="..\..\port\platform\chromium\ClipboardUtilitiesChromium.cpp"
>
</File>
<File
- RelativePath="..\..\port\platform\chromium\ContextMenuItemChromium.cpp"
+ RelativePath="..\..\port\platform\chromium\ClipboardUtilitiesChromium.h"
>
</File>
<File
- RelativePath="..\..\port\platform\chromium\ClipboardUtilitiesChromium.cpp"
+ RelativePath="..\..\port\platform\chromium\ContextMenuChromium.cpp"
>
</File>
<File
- RelativePath="..\..\port\platform\chromium\ClipboardUtilitiesChromium.h"
+ RelativePath="..\..\port\platform\chromium\ContextMenuItemChromium.cpp"
>
</File>
<File
@@ -895,6 +887,14 @@
>
</File>
<File
+ RelativePath="..\..\port\platform\graphics\FontUtilsWin.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\port\platform\graphics\FontUtilsWin.h"
+ >
+ </File>
+ <File
RelativePath="..\..\port\platform\graphics\FontWin.cpp"
>
</File>
@@ -986,6 +986,22 @@
RelativePath="..\..\port\platform\graphics\ThemeHelperWin.h"
>
</File>
+ <File
+ RelativePath="..\..\port\platform\graphics\UniscribeHelper.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\port\platform\graphics\UniscribeHelper.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\port\platform\graphics\UniscribeHelperTextRun.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\port\platform\graphics\UniscribeHelperTextRun.h"
+ >
+ </File>
<Filter
Name="svg"
>
diff --git a/webkit/port/platform/UniscribeStateTextRun.cpp b/webkit/port/platform/UniscribeStateTextRun.cpp
deleted file mode 100644
index 25d79c1..0000000
--- a/webkit/port/platform/UniscribeStateTextRun.cpp
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2006, 2007 Apple 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:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. 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.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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 "UniscribeStateTextRun.h"
-
-#include "ChromiumBridge.h"
-#include "Font.h"
-#include "SimpleFontData.h"
-
-UniscribeStateTextRun::UniscribeStateTextRun(const WebCore::TextRun& run,
- const WebCore::Font& font)
- : UniscribeState(run.characters(), run.length(), run.rtl(),
- font.primaryFont()->platformData().hfont(),
- font.primaryFont()->platformData().scriptCache(),
- font.primaryFont()->platformData().scriptFontProperties()),
- font_(&font),
- font_index_(0) {
- set_directional_override(run.directionalOverride());
- set_letter_spacing(font.letterSpacing());
- set_space_width(font.spaceWidth());
- set_word_spacing(font.wordSpacing());
- set_ascent(font.primaryFont()->ascent());
-
- Init();
-
- // Padding is the amount to add to make justification happen. This
- // should be done after Init() so all the runs are already measured.
- if (run.padding() > 0)
- Justify(run.padding());
-}
-
-UniscribeStateTextRun::UniscribeStateTextRun(
- const wchar_t* input,
- int input_length,
- bool is_rtl,
- HFONT hfont,
- SCRIPT_CACHE* script_cache,
- SCRIPT_FONTPROPERTIES* font_properties)
- : UniscribeState(input, input_length, is_rtl, hfont,
- script_cache, font_properties),
- font_(NULL),
- font_index_(-1) {
-}
-
-void UniscribeStateTextRun::TryToPreloadFont(HFONT font) {
- // Ask the browser to get the font metrics for this font.
- // That will preload the font and it should now be accessible
- // from the renderer.
- WebCore::ChromiumBridge::ensureFontLoaded(font);
-}
-
-bool UniscribeStateTextRun::NextWinFontData(
- HFONT* hfont,
- SCRIPT_CACHE** script_cache,
- SCRIPT_FONTPROPERTIES** font_properties,
- int* ascent) {
- // This check is necessary because NextWinFontData can be
- // called again after we already ran out of fonts. fontDataAt
- // behaves in a strange manner when the difference between
- // param passed and # of fonts stored in WebKit::Font is
- // larger than one. We can avoid this check by setting
- // font_index_ to # of elements in hfonts_ when we run out
- // of font. In that case, we'd have to go through a couple of
- // more checks before returning false.
- if (font_index_ == -1 || !font_)
- return false;
-
- // If the font data for a fallback font requested is not
- // yet retrieved, add them to our vectors. Note that '>' rather
- // than '>=' is used to test that condition. primaryFont is not
- // stored in hfonts_, and friends so that indices for fontDataAt
- // and our vectors for font data are 1 off from each other.
- // That is, when fully populated, hfonts_ and friends have
- // one font fewer than what's contained in font_.
- if (static_cast<size_t>(++font_index_) > hfonts_->size()) {
- const WebCore::FontData *font_data;
- font_data = font_->fontDataAt(font_index_);
- if (!font_data) {
- // run out of fonts
- font_index_ = -1;
- return false;
- }
-
- // TODO(ericroman): this won't work for SegmentedFontData
- // http://b/issue?id=1007335
- const WebCore::SimpleFontData* simple_font_data =
- font_data->fontDataForCharacter(' ');
-
- hfonts_->push_back(simple_font_data->platformData().hfont());
- script_caches_->push_back(simple_font_data->platformData().scriptCache());
- font_properties_->push_back(simple_font_data->platformData().scriptFontProperties());
- ascents_->push_back(simple_font_data->ascent());
- }
-
- *hfont = hfonts_[font_index_ - 1];
- *script_cache = script_caches_[font_index_ - 1];
- *font_properties = font_properties_[font_index_ - 1];
- *ascent = ascents_[font_index_ - 1];
- return true;
-}
-
-void UniscribeStateTextRun::ResetFontIndex() {
- font_index_ = 0;
-}
diff --git a/webkit/port/platform/graphics/FontUtilsWin.cpp b/webkit/port/platform/graphics/FontUtilsWin.cpp
new file mode 100644
index 0000000..80d8ce0b
--- /dev/null
+++ b/webkit/port/platform/graphics/FontUtilsWin.cpp
@@ -0,0 +1,339 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+#include "FontUtilsWin.h"
+
+#include "UniscribeHelper.h"
+
+#include <limits>
+#include <map>
+
+
+#include "base/logging.h"
+#include "base/singleton.h"
+#include "base/string_util.h"
+#include "unicode/locid.h"
+#include "unicode/uchar.h"
+
+namespace WebCore {
+
+namespace {
+
+// hash_map has extra cost with no sizable gain for a small number of integer
+// key items. When the map size becomes much bigger (which will be later as
+// more scripts are added) and this turns out to be prominent in the profile, we
+// may consider switching to hash_map (or just an array if we support all the
+// scripts)
+typedef std::map<UScriptCode, const UChar*> ScriptToFontMap;
+
+struct ScriptToFontMapSingletonTraits
+ : public DefaultSingletonTraits<ScriptToFontMap> {
+ static ScriptToFontMap* New() {
+ struct FontMap {
+ UScriptCode script;
+ const UChar* family;
+ };
+
+ const static FontMap font_map[] = {
+ {USCRIPT_LATIN, L"times new roman"},
+ {USCRIPT_GREEK, L"times new roman"},
+ {USCRIPT_CYRILLIC, L"times new roman"},
+ {USCRIPT_SIMPLIFIED_HAN, L"simsun"},
+ //{USCRIPT_TRADITIONAL_HAN, L"pmingliu"},
+ {USCRIPT_HIRAGANA, L"ms pgothic"},
+ {USCRIPT_KATAKANA, L"ms pgothic"},
+ {USCRIPT_KATAKANA_OR_HIRAGANA, L"ms pgothic"},
+ {USCRIPT_HANGUL, L"gulim"},
+ {USCRIPT_THAI, L"tahoma"},
+ {USCRIPT_HEBREW, L"david"},
+ {USCRIPT_ARABIC, L"tahoma"},
+ {USCRIPT_DEVANAGARI, L"mangal"},
+ {USCRIPT_BENGALI, L"vrinda"},
+ {USCRIPT_GURMUKHI, L"raavi"},
+ {USCRIPT_GUJARATI, L"shruti"},
+ {USCRIPT_ORIYA, L"kalinga"},
+ {USCRIPT_TAMIL, L"latha"},
+ {USCRIPT_TELUGU, L"gautami"},
+ {USCRIPT_KANNADA, L"tunga"},
+ {USCRIPT_MALAYALAM, L"kartika"},
+ {USCRIPT_LAO, L"dokchampa"},
+ {USCRIPT_TIBETAN, L"microsoft himalaya"},
+ {USCRIPT_GEORGIAN, L"sylfaen"},
+ {USCRIPT_ARMENIAN, L"sylfaen"},
+ {USCRIPT_ETHIOPIC, L"nyala"},
+ {USCRIPT_CANADIAN_ABORIGINAL, L"euphemia"},
+ {USCRIPT_CHEROKEE, L"plantagenet cherokee"},
+ {USCRIPT_YI, L"microsoft yi balti"},
+ {USCRIPT_SINHALA, L"iskoola pota"},
+ {USCRIPT_SYRIAC, L"estrangelo edessa"},
+ {USCRIPT_KHMER, L"daunpenh"},
+ {USCRIPT_THAANA, L"mv boli"},
+ {USCRIPT_MONGOLIAN, L"mongolian balti"},
+ {USCRIPT_MYANMAR, L"padauk"},
+ // For USCRIPT_COMMON, we map blocks to scripts when
+ // that makes sense.
+ };
+
+ ScriptToFontMap* new_instance = new ScriptToFontMap;
+ // Cannot recover from OOM so that there's no need to check.
+ for (int i = 0; i < arraysize(font_map); ++i)
+ (*new_instance)[font_map[i].script] = font_map[i].family;
+
+ // Initialize the locale-dependent mapping.
+ // Since Chrome synchronizes the ICU default locale with its UI locale,
+ // this ICU locale tells the current UI locale of Chrome.
+ Locale locale = Locale::getDefault();
+ ScriptToFontMap::const_iterator iter;
+ if (locale == Locale::getJapanese()) {
+ iter = new_instance->find(USCRIPT_HIRAGANA);
+ } else if (locale == Locale::getKorean()) {
+ iter = new_instance->find(USCRIPT_HANGUL);
+ } else {
+ // Use Simplified Chinese font for all other locales including
+ // Traditional Chinese because Simsun (SC font) has a wider
+ // coverage (covering both SC and TC) than PMingLiu (TC font).
+ // This also speeds up the TC version of Chrome when rendering SC
+ // pages.
+ iter = new_instance->find(USCRIPT_SIMPLIFIED_HAN);
+ }
+ if (iter != new_instance->end())
+ (*new_instance)[USCRIPT_HAN] = iter->second;
+
+ return new_instance;
+ }
+};
+
+Singleton<ScriptToFontMap, ScriptToFontMapSingletonTraits> script_font_map;
+
+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) {
+ HDC dc = GetDC(NULL);
+ HGDIOBJ oldFont = SelectObject(dc, hfont);
+ TEXTMETRIC tm;
+ BOOL got_metrics = GetTextMetrics(dc, &tm);
+ SelectObject(dc, oldFont);
+ ReleaseDC(NULL, dc);
+ return got_metrics ? tm.tmAscent : kUndefinedAscent;
+}
+
+struct FontData {
+ FontData() : hfont(NULL), ascent(kUndefinedAscent), script_cache(NULL) {}
+ HFONT hfont;
+ int ascent;
+ mutable SCRIPT_CACHE script_cache;
+};
+
+// 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 std::map<std::wstring, FontData*> FontDataCache;
+struct FontDataCacheSingletonTraits
+ : public DefaultSingletonTraits<FontDataCache> {
+ static void Delete(FontDataCache* cache) {
+ FontDataCache::iterator iter = cache->begin();
+ while (iter != cache->end()) {
+ SCRIPT_CACHE script_cache = iter->second->script_cache;
+ if (script_cache)
+ ScriptFreeCache(&script_cache);
+ delete iter->second;
+ ++iter;
+ }
+ delete cache;
+ }
+};
+
+} // namespace
+
+// TODO(jungshik) : this is font fallback code version 0.1
+// - Cover all the scripts
+// - Get the default font for each script/generic family from the
+// preference instead of hardcoding in the source.
+// (at least, read values from the registry for IE font settings).
+// - Support generic families (from FontDescription)
+// - If the default font for a script is not available,
+// try some more fonts known to support it. Finally, we can
+// use EnumFontFamilies or similar APIs to come up with a list of
+// fonts supporting the script and cache the result.
+// - Consider using UnicodeSet (or UnicodeMap) converted from
+// GLYPHSET (BMP) or directly read from truetype cmap tables to
+// keep track of which character is supported by which font
+// - Update script_font_cache in response to WM_FONTCHANGE
+
+const UChar* GetFontFamilyForScript(UScriptCode script,
+ GenericFamilyType generic) {
+ ScriptToFontMap::const_iterator iter = script_font_map->find(script);
+ const UChar* family = NULL;
+ if (iter != script_font_map->end())
+ family = iter->second;
+ return family;
+}
+
+// TODO(jungshik)
+// - Handle 'Inherited', 'Common' and 'Unknown'
+// (see http://www.unicode.org/reports/tr24/#Usage_Model )
+// For 'Inherited' and 'Common', perhaps we need to
+// accept another parameter indicating the previous family
+// and just return it.
+// - All the characters (or characters up to the point a single
+// font can cover) need to be taken into account
+const UChar* GetFallbackFamily(const UChar *characters,
+ int length,
+ GenericFamilyType generic,
+ UChar32 *char_checked,
+ UScriptCode *script_checked) {
+ DCHECK(characters && characters[0] && length > 0);
+ UScriptCode script = USCRIPT_COMMON;
+
+ // Sometimes characters common to script (e.g. space) is at
+ // the beginning of a string so that we need to skip them
+ // to get a font required to render the string.
+ int i = 0;
+ UChar32 ucs4 = 0;
+ while (i < length && script == USCRIPT_COMMON ||
+ script == USCRIPT_INVALID_CODE) {
+ U16_NEXT(characters, i, length, ucs4);
+ UErrorCode err = U_ZERO_ERROR;
+ script = uscript_getScript(ucs4, &err);
+ // silently ignore the error
+ }
+
+ // hack for full width ASCII. For the full-width ASCII, use the font
+ // for Han (which is locale-dependent).
+ if (0xFF00 < ucs4 && ucs4 < 0xFF5F)
+ script = USCRIPT_HAN;
+
+ // There are a lot of characters in USCRIPT_COMMON that can be covered
+ // by fonts for scripts closely related to them. See
+ // http://unicode.org/cldr/utility/list-unicodeset.jsp?a=[:Script=Common:]
+ // TODO(jungshik): make this more efficient with a wider coverage
+ if (script == USCRIPT_COMMON || script == USCRIPT_INHERITED) {
+ UBlockCode block = ublock_getCode(ucs4);
+ switch (block) {
+ case UBLOCK_BASIC_LATIN:
+ script = USCRIPT_LATIN;
+ break;
+ case UBLOCK_CJK_SYMBOLS_AND_PUNCTUATION:
+ script = USCRIPT_HAN;
+ break;
+ case UBLOCK_HIRAGANA:
+ case UBLOCK_KATAKANA:
+ script = USCRIPT_HIRAGANA;
+ break;
+ case UBLOCK_ARABIC:
+ script = USCRIPT_ARABIC;
+ break;
+ case UBLOCK_GREEK:
+ script = USCRIPT_GREEK;
+ break;
+ case UBLOCK_DEVANAGARI:
+ // For Danda and Double Danda (U+0964, U+0965), use a Devanagari
+ // font for now although they're used by other scripts as well.
+ // Without a context, we can't do any better.
+ script = USCRIPT_DEVANAGARI;
+ break;
+ case UBLOCK_ARMENIAN:
+ script = USCRIPT_ARMENIAN;
+ break;
+ case UBLOCK_GEORGIAN:
+ script = USCRIPT_GEORGIAN;
+ break;
+ case UBLOCK_KANNADA:
+ script = USCRIPT_KANNADA;
+ break;
+ }
+ }
+
+ // Another lame work-around to cover non-BMP characters.
+ const UChar* family = GetFontFamilyForScript(script, generic);
+ if (!family) {
+ int plane = ucs4 >> 16;
+ switch (plane) {
+ case 1:
+ family = L"code2001";
+ break;
+ case 2:
+ family = L"simsun-extb";
+ break;
+ default:
+ family = L"lucida sans unicode";
+ }
+ }
+
+ if (char_checked) *char_checked = ucs4;
+ if (script_checked) *script_checked = script;
+ return family;
+}
+
+
+
+// Be aware that this is not thread-safe.
+bool GetDerivedFontData(const UChar *family,
+ int style,
+ LOGFONT *logfont,
+ int *ascent,
+ HFONT *hfont,
+ SCRIPT_CACHE **script_cache) {
+ DCHECK(logfont && family && *family);
+ // Using |Singleton| here is not free, but the intl2 page cycler test
+ // does not show any noticeable difference with and without it. Leaking
+ // the contents of FontDataCache (especially SCRIPT_CACHE) at the end
+ // of a renderer process may not be a good idea. We may use
+ // atexit(). However, with no noticeable performance difference, |Singleton|
+ // is cleaner, I believe.
+ FontDataCache* font_data_cache =
+ Singleton<FontDataCache, FontDataCacheSingletonTraits>::get();
+ // TODO(jungshik) : 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 StringPrintf.
+ std::wstring font_key = StringPrintf(L"%1d:%d:%ls", style,
+ logfont->lfHeight, family);
+ FontDataCache::const_iterator iter = font_data_cache->find(font_key);
+ FontData *derived;
+ if (iter == font_data_cache->end()) {
+ DCHECK(wcslen(family) < LF_FACESIZE);
+ wcscpy_s(logfont->lfFaceName, LF_FACESIZE, family);
+ // TODO(jungshik): 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)
+ derived = new FontData;
+ 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);
+ (*font_data_cache)[font_key] = derived;
+ } else {
+ derived = iter->second;
+ // Last time, GetAscent 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);
+ }
+ *hfont = derived->hfont;
+ *ascent = derived->ascent;
+ *script_cache = &(derived->script_cache);
+ return *ascent != kUndefinedAscent;
+}
+
+int GetStyleFromLogfont(const LOGFONT* logfont) {
+ // TODO(jungshik) : consider defining UNDEFINED or INVALID for style and
+ // returning it when logfont is NULL
+ if (!logfont) {
+ NOTREACHED();
+ return FONT_STYLE_NORMAL;
+ }
+ return (logfont->lfItalic ? FONT_STYLE_ITALIC : FONT_STYLE_NORMAL) |
+ (logfont->lfUnderline ? FONT_STYLE_UNDERLINED : FONT_STYLE_NORMAL) |
+ (logfont->lfWeight >= 700 ? FONT_STYLE_BOLD : FONT_STYLE_NORMAL);
+}
+
+} // namespace WebCore
diff --git a/base/gfx/font_utils.h b/webkit/port/platform/graphics/FontUtilsWin.h
index f00db46..1ec49eb 100644
--- a/base/gfx/font_utils.h
+++ b/webkit/port/platform/graphics/FontUtilsWin.h
@@ -4,8 +4,8 @@
//
// A collection of utilities for font handling.
-#ifndef BASE_GFX_FONT_UTILS_H__
-#define BASE_GFX_FONT_UTILS_H__
+#ifndef FontUtilsWin_h
+#define FontUtilsWin_h
#include <usp10.h>
#include <wchar.h>
@@ -13,26 +13,26 @@
#include <unicode/uscript.h>
-namespace gfx {
+namespace WebCore {
// The order of family types needs to be exactly the same as
// WebCore::FontDescription::GenericFamilyType. We may lift that restriction
// when we make webkit_glue::WebkitGenericToChromeGenericFamily more
// intelligent.
enum GenericFamilyType {
- GENERIC_FAMILY_NONE = 0,
- GENERIC_FAMILY_STANDARD,
- GENERIC_FAMILY_SERIF,
- GENERIC_FAMILY_SANSSERIF,
- GENERIC_FAMILY_MONOSPACE,
- GENERIC_FAMILY_CURSIVE,
- GENERIC_FAMILY_FANTASY
+ GENERIC_FAMILY_NONE = 0,
+ GENERIC_FAMILY_STANDARD,
+ GENERIC_FAMILY_SERIF,
+ GENERIC_FAMILY_SANSSERIF,
+ GENERIC_FAMILY_MONOSPACE,
+ GENERIC_FAMILY_CURSIVE,
+ GENERIC_FAMILY_FANTASY
};
// Return a font family that supports a script and belongs to |generic| font family.
// It can return NULL and a caller has to implement its own fallback.
-const wchar_t* GetFontFamilyForScript(UScriptCode script,
- GenericFamilyType generic);
+const UChar* GetFontFamilyForScript(UScriptCode script,
+ GenericFamilyType generic);
// Return a font family that can render |characters| based on
// what script characters belong to. When char_checked is non-NULL,
@@ -40,11 +40,12 @@ const wchar_t* GetFontFamilyForScript(UScriptCode script,
// When script_checked is non-NULL, the script used to determine
// the family is returned.
// TODO(jungshik) : This function needs a total overhaul.
-const wchar_t* GetFallbackFamily(const wchar_t* characters,
- int length,
- GenericFamilyType generic,
- UChar32 *char_checked,
- UScriptCode *script_checked);
+const UChar* GetFallbackFamily(const UChar* characters,
+ int length,
+ GenericFamilyType generic,
+ UChar32 *char_checked,
+ UScriptCode *script_checked);
+
// Derive a new HFONT by replacing lfFaceName of LOGFONT with |family|,
// calculate the ascent for the derived HFONT, and initialize SCRIPT_CACHE
// in FontData.
@@ -63,7 +64,7 @@ const wchar_t* GetFallbackFamily(const wchar_t* characters,
// 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.
-bool GetDerivedFontData(const wchar_t *family,
+bool GetDerivedFontData(const UChar *family,
int style,
LOGFONT *logfont,
int *ascent,
@@ -71,17 +72,16 @@ bool GetDerivedFontData(const wchar_t *family,
SCRIPT_CACHE **script_cache);
enum {
- FONT_STYLE_NORMAL = 0,
- FONT_STYLE_BOLD = 1,
- FONT_STYLE_ITALIC = 2,
- FONT_STYLE_UNDERLINED = 4
+ FONT_STYLE_NORMAL = 0,
+ FONT_STYLE_BOLD = 1,
+ FONT_STYLE_ITALIC = 2,
+ FONT_STYLE_UNDERLINED = 4
};
// Derive style (bit-wise OR of FONT_STYLE_BOLD, FONT_STYLE_UNDERLINED, and
// FONT_STYLE_ITALIC) from LOGFONT. Returns 0 if |*logfont| is NULL.
int GetStyleFromLogfont(const LOGFONT *logfont);
-} // namespace gfx
-
-#endif // BASE_GFX_FONT_UTILS_H__
+} // namespace WebCore
+#endif // FontUtilsWin_h
diff --git a/webkit/port/platform/graphics/FontWin.cpp b/webkit/port/platform/graphics/FontWin.cpp
index e881247..17c3668 100644
--- a/webkit/port/platform/graphics/FontWin.cpp
+++ b/webkit/port/platform/graphics/FontWin.cpp
@@ -32,7 +32,7 @@
#include "GlyphBuffer.h"
#include "PlatformContextSkia.h"
#include "SimpleFontData.h"
-#include "UniscribeStateTextRun.h"
+#include "UniscribeHelperTextRun.h"
#include "base/gfx/platform_canvas_win.h"
#include "base/gfx/skia_utils.h"
@@ -134,7 +134,7 @@ FloatRect Font::selectionRectForComplexText(const TextRun& run,
int from,
int to) const
{
- UniscribeStateTextRun state(run, *this);
+ UniscribeHelperTextRun state(run, *this);
float left = static_cast<float>(point.x() + state.CharacterToX(from));
float right = static_cast<float>(point.x() + state.CharacterToX(to));
@@ -154,7 +154,7 @@ void Font::drawComplexText(GraphicsContext* graphicsContext,
int to) const
{
PlatformGraphicsContext* context = graphicsContext->platformContext();
- UniscribeStateTextRun state(run, *this);
+ UniscribeHelperTextRun state(run, *this);
SkColor color = context->fillColor();
uint8 alpha = SkColorGetA(color);
@@ -184,15 +184,16 @@ void Font::drawComplexText(GraphicsContext* graphicsContext,
float Font::floatWidthForComplexText(const TextRun& run) const
{
- UniscribeStateTextRun state(run, *this);
+ UniscribeHelperTextRun state(run, *this);
return static_cast<float>(state.Width());
}
-int Font::offsetForPositionForComplexText(const TextRun& run, int x, bool includePartialGlyphs) const
+int Font::offsetForPositionForComplexText(const TextRun& run, int x,
+ bool includePartialGlyphs) const
{
// Mac code ignores includePartialGlyphs, and they don't know what it's
// supposed to do, so we just ignore it as well.
- UniscribeStateTextRun state(run, *this);
+ UniscribeHelperTextRun state(run, *this);
int char_index = state.XToCharacter(x);
// XToCharacter will return -1 if the position is before the first
diff --git a/webkit/port/platform/graphics/GlyphPageTreeNodeWin.cpp b/webkit/port/platform/graphics/GlyphPageTreeNodeWin.cpp
index 6e0ea3e..ef7c0d3 100644
--- a/webkit/port/platform/graphics/GlyphPageTreeNodeWin.cpp
+++ b/webkit/port/platform/graphics/GlyphPageTreeNodeWin.cpp
@@ -35,7 +35,7 @@
#include "Font.h"
#include "GlyphPageTreeNode.h"
#include "SimpleFontData.h"
-#include "UniscribeStateTextRun.h"
+#include "UniscribeHelperTextRun.h"
#include "base/win_util.h"
@@ -196,11 +196,11 @@ static bool FillNonBMPGlyphs(UChar* buffer,
{
bool have_glyphs = false;
- UniscribeStateTextRun state(buffer, GlyphPage::size * 2, false,
- fontData->m_font.hfont(),
- fontData->m_font.scriptCache(),
- fontData->m_font.scriptFontProperties());
- state.set_inhibit_ligate(true);
+ UniscribeHelperTextRun state(buffer, GlyphPage::size * 2, false,
+ fontData->m_font.hfont(),
+ fontData->m_font.scriptCache(),
+ fontData->m_font.scriptFontProperties());
+ state.setInhibitLigate(true);
state.Init();
for (unsigned i = 0; i < GlyphPage::size; i++) {
diff --git a/webkit/port/platform/graphics/UniscribeHelper.cpp b/webkit/port/platform/graphics/UniscribeHelper.cpp
new file mode 100644
index 0000000..e0ff01a
--- /dev/null
+++ b/webkit/port/platform/graphics/UniscribeHelper.cpp
@@ -0,0 +1,868 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+#include "UniscribeHelper.h"
+
+#include <windows.h>
+
+#include "FontUtilsWin.h"
+#include "wtf/Assertions.h"
+
+namespace WebCore {
+
+// This function is used to see where word spacing should be applied inside
+// runs. Note that this must match Font::treatAsSpace so we all agree where
+// and how much space this is, so we don't want to do more general Unicode
+// "is this a word break" thing.
+static bool TreatAsSpace(UChar c)
+{
+ return c == ' ' || c == '\t' || c == '\n' || c == 0x00A0;
+}
+
+// SCRIPT_FONTPROPERTIES contains glyph indices for default, invalid
+// and blank glyphs. Just because ScriptShape succeeds does not mean
+// that a text run is rendered correctly. Some characters may be rendered
+// with default/invalid/blank glyphs. Therefore, we need to check if the glyph
+// array returned by ScriptShape contains any of those glyphs to make
+// sure that the text run is rendered successfully.
+static bool ContainsMissingGlyphs(WORD *glyphs,
+ int length,
+ SCRIPT_FONTPROPERTIES* properties)
+{
+ for (int i = 0; i < length; ++i) {
+ if (glyphs[i] == properties->wgDefault ||
+ (glyphs[i] == properties->wgInvalid &&
+ glyphs[i] != properties->wgBlank))
+ return true;
+ }
+
+ return false;
+}
+
+// HFONT is the 'incarnation' of 'everything' about font, but it's an opaque
+// handle and we can't directly query it to make a new HFONT sharing
+// its characteristics (height, style, etc) except for family name.
+// This function uses GetObject to convert HFONT back to LOGFONT,
+// resets the fields of LOGFONT and calculates style to use later
+// for the creation of a font identical to HFONT other than family name.
+static void SetLogFontAndStyle(HFONT hfont, LOGFONT *logfont, int *style)
+{
+ ASSERT(hfont && logfont);
+ if (!hfont || !logfont)
+ return;
+
+ GetObject(hfont, sizeof(LOGFONT), logfont);
+ // We reset these fields to values appropriate for CreateFontIndirect.
+ // while keeping lfHeight, which is the most important value in creating
+ // a new font similar to hfont.
+ logfont->lfWidth = 0;
+ logfont->lfEscapement = 0;
+ logfont->lfOrientation = 0;
+ logfont->lfCharSet = DEFAULT_CHARSET;
+ logfont->lfOutPrecision = OUT_TT_ONLY_PRECIS;
+ logfont->lfQuality = DEFAULT_QUALITY; // Honor user's desktop settings.
+ logfont->lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
+ if (style)
+ *style = GetStyleFromLogfont(logfont);
+}
+
+UniscribeHelper::UniscribeHelper(const UChar* input,
+ int inputLength,
+ bool isRtl,
+ HFONT hfont,
+ SCRIPT_CACHE* scriptCache,
+ SCRIPT_FONTPROPERTIES* fontProperties)
+ : m_input(input)
+ , m_inputLength(inputLength)
+ , m_isRtl(isRtl)
+ , m_hfont(hfont)
+ , m_scriptCache(scriptCache)
+ , m_fontProperties(fontProperties)
+ , m_directionalOverride(false)
+ , m_inhibitLigate(false)
+ , m_letterSpacing(0)
+ , m_spaceWidth(0)
+ , m_wordSpacing(0)
+ , m_ascent(0)
+{
+ m_logfont.lfFaceName[0] = 0;
+}
+
+UniscribeHelper::~UniscribeHelper()
+{
+}
+
+void UniscribeHelper::InitWithOptionalLengthProtection(bool lengthProtection)
+{
+ // We cap the input length and just don't do anything. We'll allocate a lot
+ // of things of the size of the number of characters, so the allocated
+ // memory will be several times the input length. Plus shaping such a large
+ // buffer may be a form of denial of service. No legitimate text should be
+ // this long. It also appears that Uniscribe flatly rejects very long
+ // strings, so we don't lose anything by doing this.
+ //
+ // The input length protection may be disabled by the unit tests to cause
+ // an error condition.
+ static const int kMaxInputLength = 65535;
+ if (m_inputLength == 0 ||
+ (lengthProtection && m_inputLength > kMaxInputLength))
+ return;
+
+ FillRuns();
+ FillShapes();
+ FillScreenOrder();
+}
+
+int UniscribeHelper::Width() const
+{
+ int width = 0;
+ for (int item_index = 0; item_index < static_cast<int>(m_runs.size());
+ item_index++) {
+ width += AdvanceForItem(item_index);
+ }
+ 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 run_idx = m_screenOrder[run];
+ totalGlyphs += static_cast<int>(m_shapes[run_idx].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> visattr;
+ visattr.resize(totalGlyphs);
+ Vector<int, 64> advances;
+ advances.resize(totalGlyphs);
+ Vector<int, 64> justify;
+ justify.resize(totalGlyphs);
+
+ // Build the packed input.
+ int dest_index = 0;
+ for (size_t run = 0; run < m_runs.size(); run++) {
+ int run_idx = m_screenOrder[run];
+ const Shaping& shaping = m_shapes[run_idx];
+
+ for (int i = 0; i < shaping.glyphLength(); i++, dest_index++) {
+ memcpy(&visattr[dest_index], &shaping.m_visattr[i],
+ sizeof(SCRIPT_VISATTR));
+ advances[dest_index] = shaping.m_advance[i];
+ }
+ }
+
+ // The documentation for ScriptJustify is wrong, the parameter is the space
+ // to add and not the width of the column you want.
+ const int minKashida = 1; // How do we decide what this should be?
+ ScriptJustify(&visattr[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 run_idx = m_screenOrder[run];
+ Shaping& shaping = m_shapes[run_idx];
+
+ 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 screen_idx = 0; screen_idx < m_runs.size(); screen_idx++) {
+ // Compute the length of this run.
+ int itemIdx = m_screenOrder[screen_idx];
+ const SCRIPT_ITEM& item = m_runs[itemIdx];
+ const Shaping& shaping = m_shapes[itemIdx];
+ int itemLength = shaping.charLength();
+
+ if (offset >= item.iCharPos && offset <= item.iCharPos + itemLength) {
+ // Character offset is in this run.
+ int char_len = offset - item.iCharPos;
+
+ int curX = 0;
+ hr = ScriptCPtoX(char_len, FALSE, itemLength,
+ shaping.glyphLength(),
+ &shaping.m_logs[0], &shaping.m_visattr[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(itemIdx);
+ }
+ 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 screen_idx = 0; screen_idx < m_runs.size(); screen_idx++) {
+ int itemIdx = m_screenOrder[screen_idx];
+ int advance_for_item = AdvanceForItem(itemIdx);
+
+ // Note that the run may be empty if shaping failed, so we want to skip
+ // over it.
+ const Shaping& shaping = m_shapes[itemIdx];
+ int itemLength = shaping.charLength();
+ if (x <= advance_for_item && itemLength > 0) {
+ // The requested offset is within this item.
+ const SCRIPT_ITEM& item = m_runs[itemIdx];
+
+ // Account for the leading space we've added to this run that
+ // Uniscribe doesn't know about.
+ x -= shaping.m_prePadding;
+
+ int char_x = 0;
+ int trailing;
+ hr = ScriptXtoCP(x, itemLength, shaping.glyphLength(),
+ &shaping.m_logs[0], &shaping.m_visattr[0],
+ shaping.effectiveAdvances(), &item.a, &char_x,
+ &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 char_x + item.iCharPos;
+ }
+
+ // The offset is beyond this item, account for its length and move on.
+ x -= advance_for_item;
+ }
+
+ // 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(HDC dc, int x, int y, int from, int to)
+{
+ HGDIOBJ oldFont = 0;
+ int curX = x;
+ bool firstRun = true;
+
+ for (size_t screen_idx = 0; screen_idx < m_runs.size(); screen_idx++) {
+ int itemIdx = m_screenOrder[screen_idx];
+ const SCRIPT_ITEM& item = m_runs[itemIdx];
+ const Shaping& shaping = m_shapes[itemIdx];
+
+ // 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 to_char = to - item.iCharPos;
+
+ // See if we need to draw any characters in this item.
+ if (shaping.charLength() == 0 ||
+ fromChar >= shaping.charLength() || to_char <= 0) {
+ // No chars in this item to display.
+ curX += AdvanceForItem(itemIdx);
+ 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 (to_char >= 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[to_char - 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 (to_char >= shaping.charLength())
+ afterGlyph = shaping.glyphLength();
+ else
+ afterGlyph = shaping.m_logs[to_char];
+ }
+
+ // 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 preceeding 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 NULL in when there is no justification.
+ const int* justify = shaping.m_justify.size() == 0 ?
+ NULL : &shaping.m_justify[fromGlyph];
+
+ if (firstRun) {
+ oldFont = SelectObject(dc, shaping.m_hfont);
+ firstRun = false;
+ } else {
+ SelectObject(dc, shaping.m_hfont);
+ }
+
+ // TODO(brettw) bug 698452: if a half a character is selected,
+ // we should set up a clip rect so we draw the half of the glyph
+ // correctly.
+ // Fonts with different ascents can be used to render different
+ // runs. 'Across-runs' y-coordinate correction needs to be
+ // adjusted for each font.
+ HRESULT hr = S_FALSE;
+ for (int executions = 0; executions < 2; ++executions) {
+ hr = ScriptTextOut(dc, shaping.m_scriptCache,
+ curX + innerOffset,
+ y - shaping.m_ascentOffset,
+ 0, NULL, &item.a, NULL, 0,
+ &shaping.m_glyphs[fromGlyph],
+ glyphCount,
+ &shaping.m_advance[fromGlyph],
+ justify,
+ &shaping.m_offsets[fromGlyph]);
+ if (S_OK != hr && 0 == executions) {
+ // If this ScriptTextOut 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;
+ }
+
+ ASSERT(S_OK == hr);
+ }
+
+ curX += AdvanceForItem(itemIdx);
+ }
+
+ 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(UNISCRIBE_HELPER_STACK_RUNS);
+
+ 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-NULL 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( NULL, &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 num_items = 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.
+ hr = ScriptItemize(m_input, m_inputLength,
+ static_cast<int>(m_runs.size()) - 1, &inputControl,
+ &inputState,
+ &m_runs[0], &num_items);
+ if (SUCCEEDED(hr)) {
+ m_runs.resize(num_items);
+ 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);
+ }
+}
+
+bool UniscribeHelper::Shape(const UChar* input,
+ int itemLength,
+ int numGlyphs,
+ SCRIPT_ITEM& run,
+ Shaping& shaping)
+{
+ HFONT hfont = m_hfont;
+ SCRIPT_CACHE* scriptCache = m_scriptCache;
+ SCRIPT_FONTPROPERTIES* fontProperties = m_fontProperties;
+ int ascent = m_ascent;
+ HDC tempDC = NULL;
+ HGDIOBJ oldFont = 0;
+ HRESULT hr;
+ bool lastFallbackTried = false;
+ 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_visattr.resize(numGlyphs);
+
+ // Firefox sets SCRIPT_ANALYSIS.SCRIPT_STATE.fDisplayZWG to true
+ // here. Is that what we want? It will display control characters.
+ hr = ScriptShape(tempDC, scriptCache, input, itemLength,
+ numGlyphs, &run.a,
+ &shaping.m_glyphs[0], &shaping.m_logs[0],
+ &shaping.m_visattr[0], &generatedGlyphs);
+ if (hr == E_PENDING) {
+ // Allocate the DC.
+ tempDC = GetDC(NULL);
+ oldFont = SelectObject(tempDC, hfont);
+ continue;
+ } else if (hr == E_OUTOFMEMORY) {
+ numGlyphs *= 2;
+ continue;
+ } else if (SUCCEEDED(hr) &&
+ (lastFallbackTried ||
+ !ContainsMissingGlyphs(&shaping.m_glyphs[0],
+ generatedGlyphs, fontProperties))) {
+ break;
+ }
+
+ // The current font can't render this run. clear DC and try
+ // next font.
+ if (tempDC) {
+ SelectObject(tempDC, oldFont);
+ ReleaseDC(NULL, tempDC);
+ tempDC = NULL;
+ }
+
+ if (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,
+ GENERIC_FAMILY_STANDARD, NULL, NULL);
+ bool font_ok = GetDerivedFontData(family, m_style, &m_logfont,
+ &ascent, &hfont, &scriptCache);
+
+ if (!font_ok) {
+ // 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.
+ font_ok = GetDerivedFontData(family, m_style, &m_logfont,
+ &ascent, &hfont, &scriptCache);
+ ASSERT(font_ok);
+ }
+
+ // 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;
+
+ // 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_visattr.resize(generatedGlyphs);
+ shaping.m_advance.resize(generatedGlyphs);
+ shaping.m_offsets.resize(generatedGlyphs);
+ if (tempDC) {
+ SelectObject(tempDC, oldFont);
+ ReleaseDC(NULL, tempDC);
+ }
+ // On failure, our logs don't mean anything, so zero those out.
+ if (!result)
+ shaping.m_logs.clear();
+
+ return result;
+}
+
+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 < UNISCRIBE_HELPER_STACK_CHARS) {
+ // 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 = UNISCRIBE_HELPER_STACK_CHARS;
+ } 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],
+ shaping))
+ continue;
+
+ // Compute placements. Note that offsets is documented incorrectly
+ // and is actually an array.
+
+ // DC that we lazily create if Uniscribe commands us to.
+ // (this does not happen often because scriptCache is already
+ // updated when calling ScriptShape).
+ HDC tempDC = NULL;
+ HGDIOBJ oldFont = NULL;
+ HRESULT hr;
+ while (true) {
+ shaping.m_prePadding = 0;
+ hr = ScriptPlace(tempDC, shaping.m_scriptCache,
+ &shaping.m_glyphs[0],
+ static_cast<int>(shaping.m_glyphs.size()),
+ &shaping.m_visattr[0], &m_runs[i].a,
+ &shaping.m_advance[0], &shaping.m_offsets[0],
+ &shaping.m_abc);
+ if (hr != E_PENDING)
+ break;
+
+ // Allocate the DC and run the loop again.
+ tempDC = GetDC(NULL);
+ oldFont = SelectObject(tempDC, shaping.m_hfont);
+ }
+
+ if (FAILED(hr)) {
+ // 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_shapes.clear();
+ m_screenOrder.clear();
+ }
+
+ if (tempDC) {
+ SelectObject(tempDC, oldFont);
+ ReleaseDC(NULL, tempDC);
+ }
+ }
+
+ 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];
+
+ for (int i = 0; i < shaping.charLength(); i++) {
+ if (!TreatAsSpace(m_input[m_runs[run].iCharPos + i]))
+ continue;
+
+ int glyphIndex = shaping.m_logs[i];
+ int currentAdvance = shaping.m_advance[glyphIndex];
+ // Don't give zero-width spaces a width.
+ if (!currentAdvance)
+ continue;
+
+ // currentAdvance does not include additional letter-spacing, but
+ // space_width does. Here we find out how off we are from the
+ // correct width for the space not including letter-spacing, then
+ // 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;
+ }
+ }
+}
+
+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_visattr[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 (!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 item_index) const
+{
+ int accum = 0;
+ const Shaping& shaping = m_shapes[item_index];
+
+ 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;
+}
+
+} // namespace WebCore
diff --git a/webkit/port/platform/graphics/UniscribeHelper.h b/webkit/port/platform/graphics/UniscribeHelper.h
new file mode 100644
index 0000000..27d7961
--- /dev/null
+++ b/webkit/port/platform/graphics/UniscribeHelper.h
@@ -0,0 +1,380 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// A wrapper around Uniscribe that provides a reasonable API.
+
+#ifndef UniscribeHelper_h
+#define UniscribeHelper_h
+
+#include <windows.h>
+#include <usp10.h>
+#include <map>
+
+#include "wtf/Vector.h"
+
+#include "testing/gtest/include/gtest/gtest_prod.h"
+#include "unicode/uchar.h"
+
+namespace WebCore {
+
+#define UNISCRIBE_HELPER_STACK_RUNS 8
+#define UNISCRIBE_HELPER_STACK_CHARS 32
+
+// This object should be safe to create & destroy frequently, as long as the
+// caller preserves the script_cache when possible (this data may be slow to
+// compute).
+//
+// This object is "kind of large" (~1K) because it reserves a lot of space for
+// working with to avoid expensive heap operations. Therefore, not only should
+// you not worry about creating and destroying it, you should try to not keep
+// them around.
+class UniscribeHelper {
+public:
+ // Initializes this Uniscribe run with the text pointed to by |run| with
+ // |length|. The input is NOT null terminated.
+ //
+ // The is_rtl flag should be set if the input script is RTL. It is assumed
+ // that the caller has already divided up the input text (using ICU, for
+ // example) into runs of the same direction of script. This avoids
+ // disagreements between the caller and Uniscribe later (see FillItems).
+ //
+ // A script cache should be provided by the caller that is initialized to
+ // NULL. When the caller is done with the cache (it may be stored between
+ // runs as long as it is used consistently with the same HFONT), it should
+ // call ScriptFreeCache().
+ UniscribeHelper(const UChar* input,
+ int inputLength,
+ bool isRtl,
+ HFONT hfont,
+ SCRIPT_CACHE* scriptCache,
+ SCRIPT_FONTPROPERTIES* fontProperties);
+
+ virtual ~UniscribeHelper();
+
+ // Sets Uniscribe's directional override flag. False by default.
+ bool directionalOverride() const
+ {
+ return m_directionalOverride;
+ }
+ void setDirectionalOverride(bool override)
+ {
+ m_directionalOverride = override;
+ }
+
+ // Set's Uniscribe's no-ligate override flag. False by default.
+ bool inhibitLigate() const
+ {
+ return m_inhibitLigate;
+ }
+ void setInhibitLigate(bool inhibit)
+ {
+ m_inhibitLigate = inhibit;
+ }
+
+ // Set letter spacing. We will try to insert this much space between
+ // graphemes (one or more glyphs perceived as a single unit by ordinary
+ // users of a script). Positive values increase letter spacing, negative
+ // values decrease it. 0 by default.
+ int letterSpacing() const
+ {
+ return m_letterSpacing;
+ }
+ void setLetterSpacing(int letterSpacing)
+ {
+ m_letterSpacing = letterSpacing;
+ }
+
+ // Set the width of a standard space character. We use this to normalize
+ // space widths. Windows will make spaces after Hindi characters larger than
+ // other spaces. A space_width of 0 means to use the default space width.
+ //
+ // Must be set before Init() is called.
+ int spaceWidth() const
+ {
+ return m_spaceWidth;
+ }
+ void setSpaceWidth(int spaceWidth)
+ {
+ m_spaceWidth = spaceWidth;
+ }
+
+ // Set word spacing. We will try to insert this much extra space between
+ // each word in the input (beyond whatever whitespace character separates
+ // words). Positive values lead to increased letter spacing, negative values
+ // decrease it. 0 by default.
+ //
+ // Must be set before Init() is called.
+ int wordSpacing() const
+ {
+ return m_wordSpacing;
+ }
+ void setWordSpacing(int wordSpacing)
+ {
+ m_wordSpacing = wordSpacing;
+ }
+
+ void setAscent(int ascent)
+ {
+ m_ascent = ascent;
+ }
+
+ // You must call this after setting any options but before doing any
+ // other calls like asking for widths or drawing.
+ void Init()
+ {
+ InitWithOptionalLengthProtection(true);
+ }
+
+ // Returns the total width in pixels of the text run.
+ int Width() const;
+
+ // Call to justify the text, with the amount of space that should be ADDED
+ // to get the desired width that the column should be justified to.
+ // Normally, spaces are inserted, but for Arabic there will be kashidas
+ // (extra strokes) inserted instead.
+ //
+ // This function MUST be called AFTER Init().
+ void Justify(int additionalSpace);
+
+ // Computes the given character offset into a pixel offset of the beginning
+ // of that character.
+ int CharacterToX(int offset) const;
+
+ // Converts the given pixel X position into a logical character offset into
+ // the run. For positions appearing before the first character, this will
+ // return -1.
+ int XToCharacter(int x) const;
+
+ // Draws the given characters to (x, y) in the given DC. The font will be
+ // handled by this function, but the font color and other attributes should
+ // be pre-set.
+ //
+ // The y position is the upper left corner, NOT the baseline.
+ void Draw(HDC dc, int x, int y, int from, int to);
+
+ // Returns the first glyph assigned to the character at the given offset.
+ // This function is used to retrieve glyph information when Uniscribe is
+ // being used to generate glyphs for non-complex, non-BMP (above U+FFFF)
+ // characters. These characters are not otherwise special and have no
+ // complex shaping rules, so we don't otherwise need Uniscribe, except
+ // Uniscribe is the only way to get glyphs for non-BMP characters.
+ //
+ // Returns 0 if there is no glyph for the given character.
+ WORD FirstGlyphForCharacter(int charOffset) const;
+
+protected:
+ // Backend for init. The flag allows the unit test to specify whether we
+ // should fail early for very long strings like normal, or try to pass the
+ // long string to Uniscribe. The latter provides a way to force failure of
+ // shaping.
+ void InitWithOptionalLengthProtection(bool lengthProtection);
+
+ // Tries to preload the font when the it is not accessible.
+ // This is the default implementation and it does not do anything.
+ virtual void TryToPreloadFont(HFONT font) {}
+
+private:
+ FRIEND_TEST(UniscribeTest, TooBig);
+
+ // An array corresponding to each item in runs_ containing information
+ // on each of the glyphs that were generated. Like runs_, this is in
+ // reading order. However, for rtl text, the characters within each
+ // item will be reversed.
+ struct Shaping {
+ Shaping()
+ : m_prePadding(0)
+ , m_hfont(NULL)
+ , m_scriptCache(NULL)
+ , m_ascentOffset(0) {
+ m_abc.abcA = 0;
+ m_abc.abcB = 0;
+ m_abc.abcC = 0;
+ }
+
+ // Returns the number of glyphs (which will be drawn to the screen)
+ // in this run.
+ int glyphLength() const
+ {
+ return static_cast<int>(m_glyphs.size());
+ }
+
+ // Returns the number of characters (that we started with) in this run.
+ int charLength() const
+ {
+ return static_cast<int>(m_logs.size());
+ }
+
+ // Returns the advance array that should be used when measuring glyphs.
+ // The returned pointer will indicate an array with glyph_length()
+ // elements and the advance that should be used for each one. This is
+ // either the real advance, or the justified advances if there is one,
+ // and is the array we want to use for measurement.
+ const int* effectiveAdvances() const
+ {
+ if (m_advance.size() == 0)
+ return 0;
+ if (m_justify.size() == 0)
+ return &m_advance[0];
+ return &m_justify[0];
+ }
+
+ // This is the advance amount of space that we have added to the
+ // beginning of the run. It is like the ABC's |A| advance but one that
+ // we create and must handle internally whenever computing with pixel
+ // offsets.
+ int m_prePadding;
+
+ // Glyph indices in the font used to display this item. These indices
+ // are in screen order.
+ Vector<WORD, UNISCRIBE_HELPER_STACK_CHARS> m_glyphs;
+
+ // For each input character, this tells us the first glyph index it
+ // generated. This is the only array with size of the input chars.
+ //
+ // All offsets are from the beginning of this run. Multiple characters
+ // can generate one glyph, in which case there will be adjacent
+ // duplicates in this list. One character can also generate multiple
+ // glyphs, in which case there will be skipped indices in this list.
+ Vector<WORD, UNISCRIBE_HELPER_STACK_CHARS> m_logs;
+
+ // Flags and such for each glyph.
+ Vector<SCRIPT_VISATTR, UNISCRIBE_HELPER_STACK_CHARS> m_visattr;
+
+ // Horizontal advances for each glyph listed above, this is basically
+ // how wide each glyph is.
+ Vector<int, UNISCRIBE_HELPER_STACK_CHARS> m_advance;
+
+ // This contains glyph offsets, from the nominal position of a glyph.
+ // It is used to adjust the positions of multiple combining characters
+ // around/above/below base characters in a context-sensitive manner so
+ // that they don't bump against each other and the base character.
+ Vector<GOFFSET, UNISCRIBE_HELPER_STACK_CHARS> m_offsets;
+
+ // Filled by a call to Justify, this is empty for nonjustified text.
+ // If nonempty, this contains the array of justify characters for each
+ // character as returned by ScriptJustify.
+ //
+ // This is the same as the advance array, but with extra space added
+ // for some characters. The difference between a glyph's |justify|
+ // width and it's |advance| width is the extra space added.
+ Vector<int, UNISCRIBE_HELPER_STACK_CHARS> m_justify;
+
+ // Sizing information for this run. This treats the entire run as a
+ // character with a preceeding advance, width, and ending advance. The
+ // B width is the sum of the |advance| array, and the A and C widths
+ // are any extra spacing applied to each end.
+ //
+ // It is unclear from the documentation what this actually means. From
+ // experimentation, it seems that the sum of the character advances is
+ // always the sum of the ABC values, and I'm not sure what you're
+ // supposed to do with the ABC values.
+ ABC m_abc;
+
+ // Pointers to windows font data used to render this run.
+ HFONT m_hfont;
+ SCRIPT_CACHE* m_scriptCache;
+
+ // Ascent offset between the ascent of the primary font
+ // and that of the fallback font. The offset needs to be applied,
+ // when drawing a string, to align multiple runs rendered with
+ // different fonts.
+ int m_ascentOffset;
+ };
+
+ // Computes the runs_ array from the text run.
+ void FillRuns();
+
+ // Computes the shapes_ array given an runs_ array already filled in.
+ void FillShapes();
+
+ // Fills in the screen_order_ array (see below).
+ void FillScreenOrder();
+
+ // Called to update the glyph positions based on the current spacing
+ // options that are set.
+ void ApplySpacing();
+
+ // Normalizes all advances for spaces to the same width. This keeps windows
+ // from making spaces after Hindi characters larger, which is then
+ // inconsistent with our meaure of the width since WebKit doesn't include
+ // spaces in text-runs sent to uniscribe unless white-space:pre.
+ void AdjustSpaceAdvances();
+
+ // Returns the total width of a single item.
+ int AdvanceForItem(int item_index) const;
+
+ // Shapes a run (pointed to by |input|) using |hfont| first.
+ // Tries a series of fonts specified retrieved with NextWinFontData
+ // and finally a font covering characters in |*input|. A string pointed
+ // by |input| comes from ScriptItemize and is supposed to contain
+ // characters belonging to a single script aside from characters common to
+ // all scripts (e.g. space).
+ bool Shape(const UChar* input,
+ int item_length,
+ int num_glyphs,
+ SCRIPT_ITEM& run,
+ Shaping& shaping);
+
+ // Gets Windows font data for the next best font to try in the list
+ // of fonts. When there's no more font available, returns false
+ // without touching any of out params. Need to call ResetFontIndex
+ // to start scanning of the font list from the beginning.
+ virtual bool NextWinFontData(HFONT* hfont,
+ SCRIPT_CACHE** script_cache,
+ SCRIPT_FONTPROPERTIES** font_properties,
+ int* ascent) {
+ return false;
+ }
+
+ // Resets the font index to the first in the list of fonts to try after the
+ // primaryFont turns out not to work. With fontIndex reset,
+ // NextWinFontData scans fallback fonts from the beginning.
+ virtual void ResetFontIndex() {}
+
+ // The input data for this run of Uniscribe. See the constructor.
+ const UChar* m_input;
+ const int m_inputLength;
+ const bool m_isRtl;
+
+ // Windows font data for the primary font. In a sense, m_logfont and m_style
+ // are redundant because m_hfont contains all the information. However,
+ // invoking GetObject, everytime we need the height and the style, is rather
+ // expensive so that we cache them. Would it be better to add getter and
+ // (virtual) setter for the height and the style of the primary font,
+ // instead of m_logfont? Then, a derived class ctor can set m_ascent,
+ // m_height and m_style if they're known. Getters for them would have to
+ // 'infer' their values from m_hfont ONLY when they're not set.
+ HFONT m_hfont;
+ SCRIPT_CACHE* m_scriptCache;
+ SCRIPT_FONTPROPERTIES* m_fontProperties;
+ int m_ascent;
+ LOGFONT m_logfont;
+ int m_style;
+
+ // Options, see the getters/setters above.
+ bool m_directionalOverride;
+ bool m_inhibitLigate;
+ int m_letterSpacing;
+ int m_spaceWidth;
+ int m_wordSpacing;
+
+ // Uniscribe breaks the text into Runs. These are one length of text that is
+ // in one script and one direction. This array is in reading order.
+ Vector<SCRIPT_ITEM, UNISCRIBE_HELPER_STACK_RUNS> m_runs;
+
+ Vector<Shaping, UNISCRIBE_HELPER_STACK_RUNS> m_shapes;
+
+ // This is a mapping between reading order and screen order for the items.
+ // Uniscribe's items array are in reading order. For right-to-left text,
+ // or mixed (although WebKit's |TextRun| should really be only one
+ // direction), this makes it very difficult to compute character offsets
+ // and positions. This list is in screen order from left to right, and
+ // gives the index into the |m_runs| and |m_shapes| arrays of each
+ // subsequent item.
+ Vector<int, UNISCRIBE_HELPER_STACK_RUNS> m_screenOrder;
+};
+
+} // namespace WebCore
+
+#endif // UniscribeHelper_h
diff --git a/webkit/port/platform/graphics/UniscribeHelperTextRun.cpp b/webkit/port/platform/graphics/UniscribeHelperTextRun.cpp
new file mode 100644
index 0000000..93d23e9
--- /dev/null
+++ b/webkit/port/platform/graphics/UniscribeHelperTextRun.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2006, 2007 Apple 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:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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 "UniscribeHelperTextRun.h"
+
+#include "ChromiumBridge.h"
+#include "Font.h"
+#include "SimpleFontData.h"
+
+namespace WebCore {
+
+UniscribeHelperTextRun::UniscribeHelperTextRun(const WebCore::TextRun& run,
+ const WebCore::Font& font)
+ : UniscribeHelper(run.characters(), run.length(), run.rtl(),
+ font.primaryFont()->platformData().hfont(),
+ font.primaryFont()->platformData().scriptCache(),
+ font.primaryFont()->platformData().scriptFontProperties())
+ , m_font(&font)
+ , m_fontIndex(0)
+{
+ setDirectionalOverride(run.directionalOverride());
+ setLetterSpacing(font.letterSpacing());
+ setSpaceWidth(font.spaceWidth());
+ setWordSpacing(font.wordSpacing());
+ setAscent(font.primaryFont()->ascent());
+
+ Init();
+
+ // Padding is the amount to add to make justification happen. This
+ // should be done after Init() so all the runs are already measured.
+ if (run.padding() > 0)
+ Justify(run.padding());
+}
+
+UniscribeHelperTextRun::UniscribeHelperTextRun(
+ const wchar_t* input,
+ int inputLength,
+ bool isRtl,
+ HFONT hfont,
+ SCRIPT_CACHE* scriptCache,
+ SCRIPT_FONTPROPERTIES* fontProperties)
+ : UniscribeHelper(input, inputLength, isRtl, hfont,
+ scriptCache, fontProperties)
+ , m_font(NULL)
+ , m_fontIndex(-1)
+{
+}
+
+void UniscribeHelperTextRun::TryToPreloadFont(HFONT font)
+{
+ // Ask the browser to get the font metrics for this font.
+ // That will preload the font and it should now be accessible
+ // from the renderer.
+ WebCore::ChromiumBridge::ensureFontLoaded(font);
+}
+
+bool UniscribeHelperTextRun::NextWinFontData(
+ HFONT* hfont,
+ SCRIPT_CACHE** scriptCache,
+ SCRIPT_FONTPROPERTIES** fontProperties,
+ int* ascent)
+{
+ // This check is necessary because NextWinFontData can be called again
+ // after we already ran out of fonts. fontDataAt behaves in a strange
+ // manner when the difference between param passed and # of fonts stored in
+ // WebKit::Font is larger than one. We can avoid this check by setting
+ // font_index_ to # of elements in hfonts_ when we run out of font. In that
+ // case, we'd have to go through a couple of more checks before returning
+ // false.
+ if (m_fontIndex == -1 || !m_font)
+ return false;
+
+ // If the font data for a fallback font requested is not yet retrieved, add
+ // them to our vectors. Note that '>' rather than '>=' is used to test that
+ // condition. primaryFont is not stored in hfonts_, and friends so that
+ // indices for fontDataAt and our vectors for font data are 1 off from each
+ // other. That is, when fully populated, hfonts_ and friends have one font
+ // fewer than what's contained in font_.
+ if (static_cast<size_t>(++m_fontIndex) > m_hfonts.size()) {
+ const WebCore::FontData *fontData = m_font->fontDataAt(m_fontIndex);
+ if (!fontData) {
+ // Ran out of fonts.
+ m_fontIndex = -1;
+ return false;
+ }
+
+ // TODO(ericroman): this won't work for SegmentedFontData
+ // http://b/issue?id=1007335
+ const WebCore::SimpleFontData* simpleFontData =
+ fontData->fontDataForCharacter(' ');
+
+ m_hfonts.append(simpleFontData->platformData().hfont());
+ m_scriptCaches.append(
+ simpleFontData->platformData().scriptCache());
+ m_fontProperties.append(
+ simpleFontData->platformData().scriptFontProperties());
+ m_ascents.append(simpleFontData->ascent());
+ }
+
+ *hfont = m_hfonts[m_fontIndex - 1];
+ *scriptCache = m_scriptCaches[m_fontIndex - 1];
+ *fontProperties = m_fontProperties[m_fontIndex - 1];
+ *ascent = m_ascents[m_fontIndex - 1];
+ return true;
+}
+
+void UniscribeHelperTextRun::ResetFontIndex()
+{
+ m_fontIndex = 0;
+}
+
+} // namespace WebCore
diff --git a/webkit/port/platform/UniscribeStateTextRun.h b/webkit/port/platform/graphics/UniscribeHelperTextRun.h
index 7f6e43a..281e6d9 100644
--- a/webkit/port/platform/UniscribeStateTextRun.h
+++ b/webkit/port/platform/graphics/UniscribeHelperTextRun.h
@@ -27,76 +27,73 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef UniscribeStateTextRun_H
-#define UniscribeStateTextRun_H
+#ifndef UniscribeHelperTextRun_h
+#define UniscribeHelperTextRun_h
-#include "base/gfx/uniscribe.h"
+#include "UniscribeHelper.h"
namespace WebCore {
class Font;
class TextRun;
-}
-
-// Wrapper around the Uniscribe state that automatically sets it up with the
+// Wrapper around the Uniscribe helper that automatically sets it up with the
// WebKit types we supply.
-class UniscribeStateTextRun : public gfx::UniscribeState {
+class UniscribeHelperTextRun : public UniscribeHelper {
public:
// Regular constructor used for WebCore text run processing.
- UniscribeStateTextRun(const WebCore::TextRun& run,
- const WebCore::Font& font);
+ UniscribeHelperTextRun(const WebCore::TextRun& run,
+ const WebCore::Font& font);
// Constructor with the same interface as the gfx::UniscribeState. Using
// this constructor will not give you font fallback, but it will provide
// the ability to load fonts that may not be in the OS cache
// ("TryToPreloadFont") if the caller does not have a TextRun/Font.
- UniscribeStateTextRun(const wchar_t* input,
- int input_length,
- bool is_rtl,
- HFONT hfont,
- SCRIPT_CACHE* script_cache,
- SCRIPT_FONTPROPERTIES* font_properties);
+ UniscribeHelperTextRun(const wchar_t* input,
+ int inputLength,
+ bool isRtl,
+ HFONT hfont,
+ SCRIPT_CACHE* scriptCache,
+ SCRIPT_FONTPROPERTIES* fontProperties);
protected:
virtual void TryToPreloadFont(HFONT font);
private:
- // This function retrieves the Windows font data (HFONT, etc)
- // for the next WebKit font in the list. If the font data
- // corresponding to font_index_ has been obtained before,
- // returns the values stored in our internal vectors (hfonts_, etc).
- // Otherwise, it gets next SimpleFontData from WebKit and adds them to
- // in hfonts_ and friends so that font data can be returned
- // quickly next time they're requested.
+ // This function retrieves the Windows font data (HFONT, etc) for the next
+ // WebKit font in the list. If the font data corresponding to font_index_
+ // has been obtained before, returns the values stored in our internal
+ // vectors (hfonts_, etc). Otherwise, it gets next SimpleFontData from
+ // WebKit and adds them to in hfonts_ and friends so that font data can be
+ // returned quickly next time they're requested.
virtual bool NextWinFontData(HFONT* hfont,
- SCRIPT_CACHE** script_cache,
- SCRIPT_FONTPROPERTIES** font_properties,
+ SCRIPT_CACHE** scriptCache,
+ SCRIPT_FONTPROPERTIES** fontProperties,
int* ascent);
virtual void ResetFontIndex();
- // Reference to WebKit::Font that contains all the information
- // about fonts we can use to render this input run of text.
- // It is used in NextWinFontData to retrieve Windows font data
- // for a series of non-primary fonts.
+ // Reference to WebKit::Font that contains all the information about fonts
+ // we can use to render this input run of text. It is used in
+ // NextWinFontData to retrieve Windows font data for a series of
+ // non-primary fonts.
//
// This pointer can be NULL for no font fallback handling.
- const WebCore::Font* font_;
+ const Font* m_font;
// It's rare that many fonts are listed in stylesheets.
// Four would be large enough in most cases.
const static size_t kNumberOfFonts = 4;
- // These vectors are used to store Windows font data for
- // non-primary fonts.
- StackVector<HFONT, kNumberOfFonts> hfonts_;
- StackVector<SCRIPT_CACHE*, kNumberOfFonts> script_caches_;
- StackVector<SCRIPT_FONTPROPERTIES*, kNumberOfFonts> font_properties_;
- StackVector<int, kNumberOfFonts> ascents_;
+ // These vectors are used to store Windows font data for non-primary fonts.
+ Vector<HFONT, kNumberOfFonts> m_hfonts;
+ Vector<SCRIPT_CACHE*, kNumberOfFonts> m_scriptCaches;
+ Vector<SCRIPT_FONTPROPERTIES*, kNumberOfFonts> m_fontProperties;
+ Vector<int, kNumberOfFonts> m_ascents;
- //
- int font_index_;
+ // Index of the fallback font we're currently using for NextWinFontData.
+ int m_fontIndex;
};
-#endif // UniscribeStateTextRun_H
+} // namespace WebCore
+#endif // UniscribeHelperTextRun_h
diff --git a/webkit/port/platform/graphics/UniscribeHelper_unittest.cpp b/webkit/port/platform/graphics/UniscribeHelper_unittest.cpp
new file mode 100644
index 0000000..d3e8206
--- /dev/null
+++ b/webkit/port/platform/graphics/UniscribeHelper_unittest.cpp
@@ -0,0 +1,142 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+#include "UniscribeHelper.h"
+
+#include "PlatformString.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// This must be in the gfx namespace for the friend statements in uniscribe.h
+// to work.
+namespace WebCore {
+
+namespace {
+
+class UniscribeTest : public testing::Test {
+public:
+ UniscribeTest()
+ {
+ }
+
+ // Returns an HFONT with the given name. The caller does not have to free
+ // this, it will be automatically freed at the end of the test. Returns NULL
+ // on failure. On success, the
+ HFONT MakeFont(const wchar_t* fontName, SCRIPT_CACHE** cache)
+ {
+ LOGFONT lf;
+ memset(&lf, 0, sizeof(LOGFONT));
+ lf.lfHeight = 20;
+ wcscpy_s(lf.lfFaceName, fontName);
+
+ HFONT hfont = CreateFontIndirect(&lf);
+ if (!hfont)
+ return NULL;
+
+ *cache = new SCRIPT_CACHE;
+ **cache = NULL;
+ m_createdFonts.append(std::make_pair(hfont, *cache));
+ return hfont;
+ }
+
+protected:
+ // Default font properties structure for tests to use.
+ SCRIPT_FONTPROPERTIES m_properties;
+
+private:
+ virtual void SetUp()
+ {
+ memset(&m_properties, 0, sizeof(SCRIPT_FONTPROPERTIES));
+ m_properties.cBytes = sizeof(SCRIPT_FONTPROPERTIES);
+ m_properties.wgBlank = ' ';
+ m_properties.wgDefault = '?'; // Used when the char is not in the font.
+ m_properties.wgInvalid = '#'; // Used for invalid characters.
+ }
+
+ virtual void TearDown()
+ {
+ // Free any allocated fonts.
+ for (size_t i = 0; i < m_createdFonts.size(); i++) {
+ DeleteObject(m_createdFonts[i].first);
+ ScriptFreeCache(m_createdFonts[i].second);
+ delete m_createdFonts[i].second;
+ }
+ m_createdFonts.clear();
+ }
+
+ // Tracks allocated fonts so we can delete them at the end of the test.
+ // The script cache pointer is heap allocated and must be freed.
+ Vector< std::pair<HFONT, SCRIPT_CACHE*> > m_createdFonts;
+};
+
+} // namespace
+
+// This test tests giving Uniscribe a very large buffer, which will cause a
+// failure.
+TEST_F(UniscribeTest, TooBig)
+{
+ // Make a large string with an e with a zillion combining accents.
+ String input(L"e");
+ for (int i = 0; i < 100000; i++)
+ input.append(static_cast<UChar>(0x301)); // Combining acute accent.
+
+ SCRIPT_CACHE* scriptCache;
+ HFONT hfont = MakeFont(L"Times New Roman", &scriptCache);
+ ASSERT_TRUE(hfont);
+
+ // Test a long string without the normal length protection we have. This
+ // will cause shaping to fail.
+ {
+ UniscribeHelper uniscribe(
+ input.characters(), static_cast<int>(input.length()),
+ false, hfont, scriptCache, &m_properties);
+ uniscribe.InitWithOptionalLengthProtection(false);
+
+ // There should be one shaping entry, with nothing in it.
+ ASSERT_EQ(1, uniscribe.m_shapes.size());
+ EXPECT_EQ(0, uniscribe.m_shapes[0].m_glyphs.size());
+ EXPECT_EQ(0, uniscribe.m_shapes[0].m_logs.size());
+ EXPECT_EQ(0, uniscribe.m_shapes[0].m_visattr.size());
+ EXPECT_EQ(0, uniscribe.m_shapes[0].m_advance.size());
+ EXPECT_EQ(0, uniscribe.m_shapes[0].m_offsets.size());
+ EXPECT_EQ(0, uniscribe.m_shapes[0].m_justify.size());
+ EXPECT_EQ(0, uniscribe.m_shapes[0].m_abc.abcA);
+ EXPECT_EQ(0, uniscribe.m_shapes[0].m_abc.abcB);
+ EXPECT_EQ(0, uniscribe.m_shapes[0].m_abc.abcC);
+
+ // The sizes of the other stuff should match the shaping entry.
+ EXPECT_EQ(1, uniscribe.m_runs.size());
+ EXPECT_EQ(1, uniscribe.m_screenOrder.size());
+
+ // Check that the various querying functions handle the empty case
+ // properly.
+ EXPECT_EQ(0, uniscribe.Width());
+ EXPECT_EQ(0, uniscribe.FirstGlyphForCharacter(0));
+ EXPECT_EQ(0, uniscribe.FirstGlyphForCharacter(1000));
+ EXPECT_EQ(0, uniscribe.XToCharacter(0));
+ EXPECT_EQ(0, uniscribe.XToCharacter(1000));
+ }
+
+ // Now test the very large string and make sure it is handled properly by
+ // the length protection.
+ {
+ UniscribeHelper uniscribe(
+ input.characters(), static_cast<int>(input.length()),
+ false, hfont, scriptCache, &m_properties);
+ uniscribe.InitWithOptionalLengthProtection(true);
+
+ // There should be 0 runs and shapes.
+ EXPECT_EQ(0, uniscribe.m_runs.size());
+ EXPECT_EQ(0, uniscribe.m_shapes.size());
+ EXPECT_EQ(0, uniscribe.m_screenOrder.size());
+
+ EXPECT_EQ(0, uniscribe.Width());
+ EXPECT_EQ(0, uniscribe.FirstGlyphForCharacter(0));
+ EXPECT_EQ(0, uniscribe.FirstGlyphForCharacter(1000));
+ EXPECT_EQ(0, uniscribe.XToCharacter(0));
+ EXPECT_EQ(0, uniscribe.XToCharacter(1000));
+ }
+}
+
+} // namespace WebCore
diff --git a/webkit/port/rendering/RenderThemeWin.cpp b/webkit/port/rendering/RenderThemeWin.cpp
index 74a477b..591a965 100644
--- a/webkit/port/rendering/RenderThemeWin.cpp
+++ b/webkit/port/rendering/RenderThemeWin.cpp
@@ -31,13 +31,13 @@
#include "CSSValueKeywords.h"
#include "Document.h"
#include "FontSelector.h"
+#include "FontUtilsWin.h"
#include "GraphicsContext.h"
#include "ScrollbarTheme.h"
#include "SkiaUtils.h"
#include "ThemeHelperWin.h"
#include "base/gfx/native_theme.h"
-#include "base/gfx/font_utils.h"
#include "base/gfx/skia_utils.h"
#include "base/win_util.h"
@@ -243,20 +243,22 @@ static float systemFontSize(const LOGFONT& font)
// (e.g. 15px). So, for now we just use Arial.
static wchar_t* defaultGUIFont(Document* document)
{
- UScriptCode dominantScript = document->dominantScript();
- const wchar_t* family = NULL;
-
- // TODO(jungshik) : Special-casing of Latin/Greeek/Cyrillic should go away
- // once GetFontFamilyForScript is enhanced to support GenericFamilyType for real.
- // For now, we make sure that we use Arial to match IE for those scripts.
- if (dominantScript != USCRIPT_LATIN && dominantScript != USCRIPT_CYRILLIC &&
- dominantScript != USCRIPT_GREEK && dominantScript != USCRIPT_INVALID_CODE) {
- family = gfx::GetFontFamilyForScript(dominantScript,
- gfx::GENERIC_FAMILY_NONE);
- if (family)
- return const_cast<wchar_t*>(family);
- }
- return L"Arial";
+ UScriptCode dominantScript = document->dominantScript();
+ const wchar_t* family = NULL;
+
+ // TODO(jungshik) : Special-casing of Latin/Greeek/Cyrillic should go away
+ // once GetFontFamilyForScript is enhanced to support GenericFamilyType for
+ // real. For now, we make sure that we use Arial to match IE for those
+ // scripts.
+ if (dominantScript != USCRIPT_LATIN &&
+ dominantScript != USCRIPT_CYRILLIC &&
+ dominantScript != USCRIPT_GREEK &&
+ dominantScript != USCRIPT_INVALID_CODE) {
+ family = GetFontFamilyForScript(dominantScript, GENERIC_FAMILY_NONE);
+ if (family)
+ return const_cast<wchar_t*>(family);
+ }
+ return L"Arial";
}
// Converts |points| to pixels. One point is 1/72 of an inch.
diff --git a/webkit/tools/test_shell/SConscript b/webkit/tools/test_shell/SConscript
index c98ae98..fe4ab1c 100644
--- a/webkit/tools/test_shell/SConscript
+++ b/webkit/tools/test_shell/SConscript
@@ -176,6 +176,7 @@ test_files = [
'$WEBKIT_DIR/glue/resource_fetcher_unittest.cc',
'$WEBKIT_DIR/glue/webframe_unittest.cc',
'$WEBKIT_DIR/port/platform/GKURL_unittest.cpp',
+ '$WEBKIT_DIR/port/platform/graphics/UniscribeHelper_unittest.cpp',
'$WEBKIT_DIR/port/platform/image-decoders/bmp/BMPImageDecoder_unittest.cpp',
'$WEBKIT_DIR/port/platform/image-decoders/ico/ICOImageDecoder_unittest.cpp',
'$WEBKIT_DIR/port/platform/image-decoders/xbm/XBMImageDecoder_unittest.cpp',
diff --git a/webkit/tools/test_shell/test_shell_tests.vcproj b/webkit/tools/test_shell/test_shell_tests.vcproj
index a8f8d00..c21ea63 100644
--- a/webkit/tools/test_shell/test_shell_tests.vcproj
+++ b/webkit/tools/test_shell/test_shell_tests.vcproj
@@ -219,10 +219,6 @@
>
</File>
<File
- RelativePath=".\test_shell_win.cc"
- >
- </File>
- <File
RelativePath=".\resources\test_shell.ico"
>
</File>
@@ -255,6 +251,10 @@
>
</File>
<File
+ RelativePath=".\test_shell_win.cc"
+ >
+ </File>
+ <File
RelativePath=".\test_webview_delegate.cc"
>
</File>
@@ -383,6 +383,10 @@
>
</File>
<File
+ RelativePath="..\..\port\platform\graphics\UniscribeHelper_unittest.cpp"
+ >
+ </File>
+ <File
RelativePath="..\..\glue\webframe_unittest.cc"
>
</File>