| Index: webkit/port/platform/graphics/UniscribeHelper.cpp
|
| ===================================================================
|
| --- webkit/port/platform/graphics/UniscribeHelper.cpp (revision 5490)
|
| +++ webkit/port/platform/graphics/UniscribeHelper.cpp (working copy)
|
| @@ -2,21 +2,23 @@
|
| // 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 "base/gfx/uniscribe.h"
|
| +#include "FontUtilsWin.h"
|
| +#include "wtf/Assertions.h"
|
|
|
| -#include "base/gfx/font_utils.h"
|
| -#include "base/logging.h"
|
| +namespace WebCore {
|
|
|
| -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;
|
| +static bool TreatAsSpace(UChar c)
|
| +{
|
| + return c == ' ' || c == '\t' || c == '\n' || c == 0x00A0;
|
| }
|
|
|
| // SCRIPT_FONTPROPERTIES contains glyph indices for default, invalid
|
| @@ -27,14 +29,16 @@
|
| // 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;
|
| - }
|
| + 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;
|
| + return false;
|
| }
|
|
|
| // HFONT is the 'incarnation' of 'everything' about font, but it's an opaque
|
| @@ -43,806 +47,822 @@
|
| // 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;
|
| +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 = gfx::GetStyleFromLogfont(logfont);
|
| + 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);
|
| }
|
|
|
| -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;
|
| +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;
|
| }
|
|
|
| -UniscribeState::~UniscribeState() {
|
| +UniscribeHelper::~UniscribeHelper()
|
| +{
|
| }
|
|
|
| -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;
|
| +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();
|
| + 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;
|
| +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 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.
|
| +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.
|
| - 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);
|
| + // 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 < runs_->size(); run++) {
|
| - int run_idx = screen_order_[run];
|
| - const Shaping& shaping = shapes_[run_idx];
|
| + // 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.glyph_length(); i++, dest_index++) {
|
| - memcpy(&visattr[dest_index], &shaping.visattr[i], sizeof(SCRIPT_VISATTR));
|
| - advances[dest_index] = shaping.advance[i];
|
| + 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 min_kashida = 1; // How do we decide what this should be?
|
| - ScriptJustify(&visattr[0], &advances[0], total_glyphs, additional_space,
|
| - min_kashida, &justify[0]);
|
| + // 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 global_glyph_index = 0;
|
| - for (size_t run = 0; run < runs_->size(); run++) {
|
| - int run_idx = screen_order_[run];
|
| - Shaping& shaping = shapes_[run_idx];
|
| + // 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.justify->resize(shaping.glyph_length());
|
| - for (int i = 0; i < shaping.glyph_length(); i++, global_glyph_index++)
|
| - shaping.justify[i] = justify[global_glyph_index];
|
| - }
|
| + shaping.m_justify.resize(shaping.glyphLength());
|
| + for (int i = 0; i < shaping.glyphLength(); i++, globalGlyphIndex++)
|
| + shaping.m_justify[i] = justify[globalGlyphIndex];
|
| + }
|
| }
|
|
|
| -int UniscribeState::CharacterToX(int offset) const {
|
| - HRESULT hr;
|
| - DCHECK(offset <= input_length_);
|
| +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 < 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();
|
| + // 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 + item_length) {
|
| - // Character offset is in this run.
|
| - int char_len = offset - item.iCharPos;
|
| + if (offset >= item.iCharPos && offset <= item.iCharPos + itemLength) {
|
| + // 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;
|
| + 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 += cur_x + shaping.pre_padding;
|
| - DCHECK(width >= 0);
|
| - return width;
|
| + width += curX + shaping.m_prePadding;
|
| + ASSERT(width >= 0);
|
| + return width;
|
| + }
|
| +
|
| + // Move to the next item.
|
| + width += AdvanceForItem(itemIdx);
|
| }
|
| -
|
| - // Move to the next item.
|
| - width += AdvanceForItem(item_idx);
|
| - }
|
| - DCHECK(width >= 0);
|
| - return width;
|
| + ASSERT(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);
|
| +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 = 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];
|
| + // 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.pre_padding;
|
| + // 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, item_length, shaping.glyph_length(),
|
| - &shaping.logs[0], &shaping.visattr[0],
|
| - shaping.effective_advances(), &item.a, &char_x,
|
| - &trailing);
|
| + 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 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;
|
| }
|
|
|
| - // 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;
|
| + // 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;
|
| +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 < runs_->size(); screen_idx++) {
|
| - int item_idx = screen_order_[screen_idx];
|
| - const SCRIPT_ITEM& item = runs_[item_idx];
|
| - const Shaping& shaping = shapes_[item_idx];
|
| + 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 from_char = from - item.iCharPos;
|
| - int to_char = to - item.iCharPos;
|
| + // 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.char_length() == 0 ||
|
| - from_char >= shaping.char_length() || to_char <= 0) {
|
| - // No chars in this item to display.
|
| - cur_x += AdvanceForItem(item_idx);
|
| - continue;
|
| - }
|
| + // 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 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];
|
| - }
|
| + // 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 (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];
|
| - }
|
| + // 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* effective_advances = shaping.effective_advances();
|
| - int inner_offset = 0;
|
| - for (int i = 0; i < from_glyph; i++)
|
| - inner_offset += effective_advances[i];
|
| + // 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 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;
|
| + // 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.justify->empty() ?
|
| - NULL : &shaping.justify[from_glyph];
|
| + // Pass NULL in when there is no justification.
|
| + const int* justify = shaping.m_justify.size() == 0 ?
|
| + NULL : &shaping.m_justify[fromGlyph];
|
|
|
| - if (first_run) {
|
| - old_font = SelectObject(dc, shaping.hfont_);
|
| - first_run = false;
|
| - } else {
|
| - SelectObject(dc, shaping.hfont_);
|
| - }
|
| + 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.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;
|
| + // 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);
|
| }
|
| - break;
|
| - }
|
|
|
| - DCHECK(S_OK == hr);
|
| -
|
| -
|
| + curX += AdvanceForItem(itemIdx);
|
| }
|
|
|
| - cur_x += AdvanceForItem(item_idx);
|
| - }
|
| -
|
| - if (old_font)
|
| - SelectObject(dc, old_font);
|
| + if (oldFont)
|
| + SelectObject(dc, oldFont);
|
| }
|
|
|
| -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];
|
| +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;
|
| + return 0;
|
| }
|
|
|
| -void UniscribeState::FillRuns() {
|
| - HRESULT hr;
|
| - runs_->resize(UNISCRIBE_STATE_STACK_RUNS);
|
| +void UniscribeHelper::FillRuns()
|
| +{
|
| + HRESULT hr;
|
| + m_runs.resize(UNISCRIBE_HELPER_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.
|
| + 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;
|
| + 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;
|
| + // 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);
|
| }
|
| - 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 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;
|
|
|
| -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 generatedGlyphs = 0;
|
|
|
| - 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();
|
|
|
| - // 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);
|
|
|
| - // 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(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;
|
| + }
|
|
|
| - // 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 (tempDC) {
|
| + SelectObject(tempDC, oldFont);
|
| + ReleaseDC(NULL, tempDC);
|
| + tempDC = NULL;
|
| + }
|
|
|
| - // 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, &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;
|
|
|
| - 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 (!m_logfont.lfFaceName[0])
|
| + SetLogFontAndStyle(m_hfont, &m_logfont, &m_style);
|
|
|
| - // 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
|
| + // 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);
|
|
|
| - // 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);
|
|
|
| - 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);
|
| + }
|
|
|
| - // 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.
|
| + ASSERT(hfont);
|
|
|
| - // 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;
|
| + // 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.hfont_ = hfont;
|
| - shaping.script_cache_ = script_cache;
|
| + // 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.ascent_offset_ = ascent_ ? ascent - ascent_ : 0;
|
| - result = true;
|
| + // 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.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();
|
| + 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;
|
| + 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;
|
| +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 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;
|
| - }
|
| + 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 = shapes_[i];
|
| - if (!Shape(&input_[start_item], item_length, num_glyphs, runs_[i], shaping))
|
| - continue;
|
| + // 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.
|
| + // 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;
|
| + // 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.
|
| - temp_dc = GetDC(NULL);
|
| - old_font = SelectObject(temp_dc, shaping.hfont_);
|
| - }
|
| + // 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.
|
| - runs_->clear();
|
| - shapes_->clear();
|
| - screen_order_->clear();
|
| - }
|
| + 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 (temp_dc) {
|
| - SelectObject(temp_dc, old_font);
|
| - ReleaseDC(NULL, temp_dc);
|
| + if (tempDC) {
|
| + SelectObject(tempDC, oldFont);
|
| + ReleaseDC(NULL, tempDC);
|
| + }
|
| }
|
| - }
|
|
|
| - AdjustSpaceAdvances();
|
| + AdjustSpaceAdvances();
|
|
|
| - if (letter_spacing_ != 0 || word_spacing_ != 0)
|
| - ApplySpacing();
|
| + if (m_letterSpacing != 0 || m_wordSpacing != 0)
|
| + ApplySpacing();
|
| }
|
|
|
| -void UniscribeState::FillScreenOrder() {
|
| - screen_order_->resize(runs_->size());
|
| +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 (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;
|
| - }
|
| + // 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 UniscribeState::AdjustSpaceAdvances() {
|
| - if (space_width_ == 0)
|
| - return;
|
| +void UniscribeHelper::AdjustSpaceAdvances()
|
| +{
|
| + if (m_spaceWidth == 0)
|
| + return;
|
|
|
| - int space_width_without_letter_spacing = space_width_ - letter_spacing_;
|
| + int spaceWidthWithoutLetterSpacing = m_spaceWidth - m_letterSpacing;
|
|
|
| - // This mostly matches what WebKit's UniscribeController::shapeAndPlaceItem.
|
| - for (size_t run = 0; run < runs_->size(); run++) {
|
| - Shaping& shaping = shapes_[run];
|
| + // 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.char_length(); i++) {
|
| - if (!TreatAsSpace(input_[runs_[run].iCharPos + i]))
|
| - continue;
|
| + for (int i = 0; i < shaping.charLength(); i++) {
|
| + if (!TreatAsSpace(m_input[m_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;
|
| + int glyphIndex = shaping.m_logs[i];
|
| + int currentAdvance = shaping.m_advance[glyphIndex];
|
| + // Don't give zero-width spaces a width.
|
| + if (!currentAdvance)
|
| + 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;
|
| + // 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 UniscribeState::ApplySpacing() {
|
| - for (size_t run = 0; run < runs_->size(); run++) {
|
| - Shaping& shaping = shapes_[run];
|
| - bool is_rtl = runs_[run].a.fRTL;
|
| +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 (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_;
|
| + 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.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_;
|
| + // 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;
|
| + }
|
| + }
|
| }
|
| - } 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;
|
| + // 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 glyph_index = shaping.logs[i];
|
| + // 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 (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
|
| + // 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...
|
| - }
|
| + // 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];
|
| +int UniscribeHelper::AdvanceForItem(int item_index) const
|
| +{
|
| + int accum = 0;
|
| + const Shaping& shaping = m_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;
|
| - }
|
| + 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.justify->size(); i++)
|
| - justification += shaping.justify[i];
|
| + // 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.pre_padding + justification;
|
| + return shaping.m_prePadding + justification;
|
| }
|
|
|
| -} // namespace gfx
|
| -
|
| +} // namespace WebCore
|
|
|
| Property changes on: webkit\port\platform\graphics\UniscribeHelper.cpp
|
| ___________________________________________________________________
|
| Added: svn:mergeinfo
|
| Merged /branches/chrome_webkit_merge_branch/base/gfx/uniscribe.cc:r69-2775
|
|
|
|
|