Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(604)

Side by Side Diff: webkit/port/platform/graphics/UniscribeHelper.cpp

Issue 10785: Debase our Uniscribe code. This moves FontUtils and all our Uniscribe code fr... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 12 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
Property Changes:
Added: svn:mergeinfo
Merged /branches/chrome_webkit_merge_branch/base/gfx/uniscribe.cc:r69-2775
OLDNEW
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "config.h"
6 #include "UniscribeHelper.h"
7
5 #include <windows.h> 8 #include <windows.h>
6 9
7 #include "base/gfx/uniscribe.h" 10 #include "FontUtilsWin.h"
11 #include "wtf/Assertions.h"
8 12
9 #include "base/gfx/font_utils.h" 13 namespace WebCore {
10 #include "base/logging.h"
11
12 namespace gfx {
13 14
14 // This function is used to see where word spacing should be applied inside 15 // This function is used to see where word spacing should be applied inside
15 // runs. Note that this must match Font::treatAsSpace so we all agree where 16 // runs. Note that this must match Font::treatAsSpace so we all agree where
16 // and how much space this is, so we don't want to do more general Unicode 17 // and how much space this is, so we don't want to do more general Unicode
17 // "is this a word break" thing. 18 // "is this a word break" thing.
18 static bool TreatAsSpace(wchar_t c) { 19 static bool TreatAsSpace(UChar c)
19 return c == ' ' || c == '\t' || c == '\n' || c == 0x00A0; 20 {
21 return c == ' ' || c == '\t' || c == '\n' || c == 0x00A0;
20 } 22 }
21 23
22 // SCRIPT_FONTPROPERTIES contains glyph indices for default, invalid 24 // SCRIPT_FONTPROPERTIES contains glyph indices for default, invalid
23 // and blank glyphs. Just because ScriptShape succeeds does not mean 25 // and blank glyphs. Just because ScriptShape succeeds does not mean
24 // that a text run is rendered correctly. Some characters may be rendered 26 // that a text run is rendered correctly. Some characters may be rendered
25 // with default/invalid/blank glyphs. Therefore, we need to check if the glyph 27 // with default/invalid/blank glyphs. Therefore, we need to check if the glyph
26 // array returned by ScriptShape contains any of those glyphs to make 28 // array returned by ScriptShape contains any of those glyphs to make
27 // sure that the text run is rendered successfully. 29 // sure that the text run is rendered successfully.
28 static bool ContainsMissingGlyphs(WORD *glyphs, 30 static bool ContainsMissingGlyphs(WORD *glyphs,
29 int length, 31 int length,
30 SCRIPT_FONTPROPERTIES* properties) { 32 SCRIPT_FONTPROPERTIES* properties)
31 for (int i = 0; i < length; ++i) { 33 {
32 if (glyphs[i] == properties->wgDefault || 34 for (int i = 0; i < length; ++i) {
33 (glyphs[i] == properties->wgInvalid && glyphs[i] != properties->wgBlank) ) 35 if (glyphs[i] == properties->wgDefault ||
34 return true; 36 (glyphs[i] == properties->wgInvalid &&
35 } 37 glyphs[i] != properties->wgBlank))
38 return true;
39 }
36 40
37 return false; 41 return false;
38 } 42 }
39 43
40 // HFONT is the 'incarnation' of 'everything' about font, but it's an opaque 44 // HFONT is the 'incarnation' of 'everything' about font, but it's an opaque
41 // handle and we can't directly query it to make a new HFONT sharing 45 // handle and we can't directly query it to make a new HFONT sharing
42 // its characteristics (height, style, etc) except for family name. 46 // its characteristics (height, style, etc) except for family name.
43 // This function uses GetObject to convert HFONT back to LOGFONT, 47 // This function uses GetObject to convert HFONT back to LOGFONT,
44 // resets the fields of LOGFONT and calculates style to use later 48 // resets the fields of LOGFONT and calculates style to use later
45 // for the creation of a font identical to HFONT other than family name. 49 // for the creation of a font identical to HFONT other than family name.
46 static void SetLogFontAndStyle(HFONT hfont, LOGFONT *logfont, int *style) { 50 static void SetLogFontAndStyle(HFONT hfont, LOGFONT *logfont, int *style)
47 DCHECK(hfont && logfont); 51 {
48 if (!hfont || !logfont) 52 ASSERT(hfont && logfont);
49 return; 53 if (!hfont || !logfont)
50 54 return;
51 GetObject(hfont, sizeof(LOGFONT), logfont); 55
52 // We reset these fields to values appropriate for CreateFontIndirect. 56 GetObject(hfont, sizeof(LOGFONT), logfont);
53 // while keeping lfHeight, which is the most important value in creating 57 // We reset these fields to values appropriate for CreateFontIndirect.
54 // a new font similar to hfont. 58 // while keeping lfHeight, which is the most important value in creating
55 logfont->lfWidth = 0; 59 // a new font similar to hfont.
56 logfont->lfEscapement = 0; 60 logfont->lfWidth = 0;
57 logfont->lfOrientation = 0; 61 logfont->lfEscapement = 0;
58 logfont->lfCharSet = DEFAULT_CHARSET; 62 logfont->lfOrientation = 0;
59 logfont->lfOutPrecision = OUT_TT_ONLY_PRECIS; 63 logfont->lfCharSet = DEFAULT_CHARSET;
60 logfont->lfQuality = DEFAULT_QUALITY; // Honor user's desktop settings. 64 logfont->lfOutPrecision = OUT_TT_ONLY_PRECIS;
61 logfont->lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; 65 logfont->lfQuality = DEFAULT_QUALITY; // Honor user's desktop settings.
62 if (style) 66 logfont->lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
63 *style = gfx::GetStyleFromLogfont(logfont); 67 if (style)
64 } 68 *style = GetStyleFromLogfont(logfont);
65 69 }
66 UniscribeState::UniscribeState(const wchar_t* input, 70
67 int input_length, 71 UniscribeHelper::UniscribeHelper(const UChar* input,
68 bool is_rtl, 72 int inputLength,
69 HFONT hfont, 73 bool isRtl,
70 SCRIPT_CACHE* script_cache, 74 HFONT hfont,
71 SCRIPT_FONTPROPERTIES* font_properties) 75 SCRIPT_CACHE* scriptCache,
72 : input_(input), 76 SCRIPT_FONTPROPERTIES* fontProperties)
73 input_length_(input_length), 77 : m_input(input)
74 is_rtl_(is_rtl), 78 , m_inputLength(inputLength)
75 hfont_(hfont), 79 , m_isRtl(isRtl)
76 script_cache_(script_cache), 80 , m_hfont(hfont)
77 font_properties_(font_properties), 81 , m_scriptCache(scriptCache)
78 directional_override_(false), 82 , m_fontProperties(fontProperties)
79 inhibit_ligate_(false), 83 , m_directionalOverride(false)
80 letter_spacing_(0), 84 , m_inhibitLigate(false)
81 space_width_(0), 85 , m_letterSpacing(0)
82 word_spacing_(0), 86 , m_spaceWidth(0)
83 ascent_(0) { 87 , m_wordSpacing(0)
84 logfont_.lfFaceName[0] = 0; 88 , m_ascent(0)
85 } 89 {
86 90 m_logfont.lfFaceName[0] = 0;
87 UniscribeState::~UniscribeState() { 91 }
88 } 92
89 93 UniscribeHelper::~UniscribeHelper()
90 void UniscribeState::InitWithOptionalLengthProtection(bool length_protection) { 94 {
91 // We cap the input length and just don't do anything. We'll allocate a lot 95 }
92 // of things of the size of the number of characters, so the allocated memory 96
93 // will be several times the input length. Plus shaping such a large buffer 97 void UniscribeHelper::InitWithOptionalLengthProtection(bool lengthProtection)
94 // may be a form of denial of service. No legitimate text should be this long. 98 {
95 // It also appears that Uniscribe flatly rejects very long strings, so we 99 // We cap the input length and just don't do anything. We'll allocate a lot
96 // don't lose anything by doing this. 100 // of things of the size of the number of characters, so the allocated
97 // 101 // memory will be several times the input length. Plus shaping such a large
98 // The input length protection may be disabled by the unit tests to cause 102 // buffer may be a form of denial of service. No legitimate text should be
99 // an error condition. 103 // this long. It also appears that Uniscribe flatly rejects very long
100 static const int kMaxInputLength = 65535; 104 // strings, so we don't lose anything by doing this.
101 if (input_length_ == 0 || 105 //
102 (length_protection && input_length_ > kMaxInputLength)) 106 // The input length protection may be disabled by the unit tests to cause
103 return; 107 // an error condition.
104 108 static const int kMaxInputLength = 65535;
105 FillRuns(); 109 if (m_inputLength == 0 ||
106 FillShapes(); 110 (lengthProtection && m_inputLength > kMaxInputLength))
107 FillScreenOrder(); 111 return;
108 } 112
109 113 FillRuns();
110 int UniscribeState::Width() const { 114 FillShapes();
111 int width = 0; 115 FillScreenOrder();
112 for (int item_index = 0; item_index < static_cast<int>(runs_->size()); 116 }
113 item_index++) { 117
114 width += AdvanceForItem(item_index); 118 int UniscribeHelper::Width() const
115 } 119 {
116 return width; 120 int width = 0;
117 } 121 for (int item_index = 0; item_index < static_cast<int>(m_runs.size());
118 122 item_index++) {
119 void UniscribeState::Justify(int additional_space) { 123 width += AdvanceForItem(item_index);
120 // Count the total number of glyphs we have so we know how big to make the 124 }
121 // buffers below. 125 return width;
122 int total_glyphs = 0; 126 }
123 for (size_t run = 0; run < runs_->size(); run++) { 127
124 int run_idx = screen_order_[run]; 128 void UniscribeHelper::Justify(int additionalSpace)
125 total_glyphs += static_cast<int>(shapes_[run_idx].glyph_length()); 129 {
126 } 130 // Count the total number of glyphs we have so we know how big to make the
127 if (total_glyphs == 0) 131 // buffers below.
128 return; // Nothing to do. 132 int totalGlyphs = 0;
129 133 for (size_t run = 0; run < m_runs.size(); run++) {
130 // We make one big buffer in screen order of all the glyphs we are drawing 134 int run_idx = m_screenOrder[run];
131 // across runs so that the justification function will adjust evenly across 135 totalGlyphs += static_cast<int>(m_shapes[run_idx].glyphLength());
132 // all glyphs. 136 }
133 StackVector<SCRIPT_VISATTR, 64> visattr; 137 if (totalGlyphs == 0)
134 visattr->resize(total_glyphs); 138 return; // Nothing to do.
135 StackVector<int, 64> advances; 139
136 advances->resize(total_glyphs); 140 // We make one big buffer in screen order of all the glyphs we are drawing
137 StackVector<int, 64> justify; 141 // across runs so that the justification function will adjust evenly across
138 justify->resize(total_glyphs); 142 // all glyphs.
139 143 Vector<SCRIPT_VISATTR, 64> visattr;
140 // Build the packed input. 144 visattr.resize(totalGlyphs);
141 int dest_index = 0; 145 Vector<int, 64> advances;
142 for (size_t run = 0; run < runs_->size(); run++) { 146 advances.resize(totalGlyphs);
143 int run_idx = screen_order_[run]; 147 Vector<int, 64> justify;
144 const Shaping& shaping = shapes_[run_idx]; 148 justify.resize(totalGlyphs);
145 149
146 for (int i = 0; i < shaping.glyph_length(); i++, dest_index++) { 150 // Build the packed input.
147 memcpy(&visattr[dest_index], &shaping.visattr[i], sizeof(SCRIPT_VISATTR)); 151 int dest_index = 0;
148 advances[dest_index] = shaping.advance[i]; 152 for (size_t run = 0; run < m_runs.size(); run++) {
149 } 153 int run_idx = m_screenOrder[run];
150 } 154 const Shaping& shaping = m_shapes[run_idx];
151 155
152 // The documentation for ScriptJustify is wrong, the parameter is the space 156 for (int i = 0; i < shaping.glyphLength(); i++, dest_index++) {
153 // to add and not the width of the column you want. 157 memcpy(&visattr[dest_index], &shaping.m_visattr[i],
154 const int min_kashida = 1; // How do we decide what this should be? 158 sizeof(SCRIPT_VISATTR));
155 ScriptJustify(&visattr[0], &advances[0], total_glyphs, additional_space, 159 advances[dest_index] = shaping.m_advance[i];
156 min_kashida, &justify[0]); 160 }
157 161 }
158 // Now we have to unpack the justification amounts back into the runs so 162
159 // the glyph indices match. 163 // The documentation for ScriptJustify is wrong, the parameter is the space
160 int global_glyph_index = 0; 164 // to add and not the width of the column you want.
161 for (size_t run = 0; run < runs_->size(); run++) { 165 const int minKashida = 1; // How do we decide what this should be?
162 int run_idx = screen_order_[run]; 166 ScriptJustify(&visattr[0], &advances[0], totalGlyphs, additionalSpace,
163 Shaping& shaping = shapes_[run_idx]; 167 minKashida, &justify[0]);
164 168
165 shaping.justify->resize(shaping.glyph_length()); 169 // Now we have to unpack the justification amounts back into the runs so
166 for (int i = 0; i < shaping.glyph_length(); i++, global_glyph_index++) 170 // the glyph indices match.
167 shaping.justify[i] = justify[global_glyph_index]; 171 int globalGlyphIndex = 0;
168 } 172 for (size_t run = 0; run < m_runs.size(); run++) {
169 } 173 int run_idx = m_screenOrder[run];
170 174 Shaping& shaping = m_shapes[run_idx];
171 int UniscribeState::CharacterToX(int offset) const { 175
172 HRESULT hr; 176 shaping.m_justify.resize(shaping.glyphLength());
173 DCHECK(offset <= input_length_); 177 for (int i = 0; i < shaping.glyphLength(); i++, globalGlyphIndex++)
174 178 shaping.m_justify[i] = justify[globalGlyphIndex];
175 // Our algorithm is to traverse the items in screen order from left to 179 }
176 // right, adding in each item's screen width until we find the item with 180 }
177 // the requested character in it. 181
178 int width = 0; 182 int UniscribeHelper::CharacterToX(int offset) const
179 for (size_t screen_idx = 0; screen_idx < runs_->size(); screen_idx++) { 183 {
180 // Compute the length of this run. 184 HRESULT hr;
181 int item_idx = screen_order_[screen_idx]; 185 ASSERT(offset <= m_inputLength);
182 const SCRIPT_ITEM& item = runs_[item_idx]; 186
183 const Shaping& shaping = shapes_[item_idx]; 187 // Our algorithm is to traverse the items in screen order from left to
184 int item_length = shaping.char_length(); 188 // right, adding in each item's screen width until we find the item with
185 189 // the requested character in it.
186 if (offset >= item.iCharPos && offset <= item.iCharPos + item_length) { 190 int width = 0;
187 // Character offset is in this run. 191 for (size_t screen_idx = 0; screen_idx < m_runs.size(); screen_idx++) {
188 int char_len = offset - item.iCharPos; 192 // Compute the length of this run.
189 193 int itemIdx = m_screenOrder[screen_idx];
190 int cur_x = 0; 194 const SCRIPT_ITEM& item = m_runs[itemIdx];
191 hr = ScriptCPtoX(char_len, FALSE, item_length, shaping.glyph_length(), 195 const Shaping& shaping = m_shapes[itemIdx];
192 &shaping.logs[0], &shaping.visattr[0], 196 int itemLength = shaping.charLength();
193 shaping.effective_advances(), &item.a, &cur_x); 197
194 if (FAILED(hr)) 198 if (offset >= item.iCharPos && offset <= item.iCharPos + itemLength) {
195 return 0; 199 // Character offset is in this run.
196 200 int char_len = offset - item.iCharPos;
197 width += cur_x + shaping.pre_padding; 201
198 DCHECK(width >= 0); 202 int curX = 0;
199 return width; 203 hr = ScriptCPtoX(char_len, FALSE, itemLength,
200 } 204 shaping.glyphLength(),
201 205 &shaping.m_logs[0], &shaping.m_visattr[0],
202 // Move to the next item. 206 shaping.effectiveAdvances(), &item.a, &curX);
203 width += AdvanceForItem(item_idx); 207 if (FAILED(hr))
204 } 208 return 0;
205 DCHECK(width >= 0); 209
206 return width; 210 width += curX + shaping.m_prePadding;
207 } 211 ASSERT(width >= 0);
208 212 return width;
209 int UniscribeState::XToCharacter(int x) const { 213 }
210 // We iterate in screen order until we find the item with the given pixel 214
211 // position in it. When we find that guy, we ask Uniscribe for the 215 // Move to the next item.
212 // character index. 216 width += AdvanceForItem(itemIdx);
213 HRESULT hr; 217 }
214 for (size_t screen_idx = 0; screen_idx < runs_->size(); screen_idx++) { 218 ASSERT(width >= 0);
215 int item_idx = screen_order_[screen_idx]; 219 return width;
216 int advance_for_item = AdvanceForItem(item_idx); 220 }
217 221
218 // Note that the run may be empty if shaping failed, so we want to skip 222 int UniscribeHelper::XToCharacter(int x) const
219 // over it. 223 {
220 const Shaping& shaping = shapes_[item_idx]; 224 // We iterate in screen order until we find the item with the given pixel
221 int item_length = shaping.char_length(); 225 // position in it. When we find that guy, we ask Uniscribe for the
222 if (x <= advance_for_item && item_length > 0) { 226 // character index.
223 // The requested offset is within this item. 227 HRESULT hr;
224 const SCRIPT_ITEM& item = runs_[item_idx]; 228 for (size_t screen_idx = 0; screen_idx < m_runs.size(); screen_idx++) {
225 229 int itemIdx = m_screenOrder[screen_idx];
226 // Account for the leading space we've added to this run that Uniscribe 230 int advance_for_item = AdvanceForItem(itemIdx);
227 // doesn't know about. 231
228 x -= shaping.pre_padding; 232 // Note that the run may be empty if shaping failed, so we want to skip
229 233 // over it.
230 int char_x = 0; 234 const Shaping& shaping = m_shapes[itemIdx];
231 int trailing; 235 int itemLength = shaping.charLength();
232 hr = ScriptXtoCP(x, item_length, shaping.glyph_length(), 236 if (x <= advance_for_item && itemLength > 0) {
233 &shaping.logs[0], &shaping.visattr[0], 237 // The requested offset is within this item.
234 shaping.effective_advances(), &item.a, &char_x, 238 const SCRIPT_ITEM& item = m_runs[itemIdx];
235 &trailing); 239
236 240 // Account for the leading space we've added to this run that
237 // The character offset is within the item. We need to add the item's 241 // Uniscribe doesn't know about.
238 // offset to transform it into the space of the TextRun 242 x -= shaping.m_prePadding;
239 return char_x + item.iCharPos; 243
240 } 244 int char_x = 0;
241 245 int trailing;
242 // The offset is beyond this item, account for its length and move on. 246 hr = ScriptXtoCP(x, itemLength, shaping.glyphLength(),
243 x -= advance_for_item; 247 &shaping.m_logs[0], &shaping.m_visattr[0],
244 } 248 shaping.effectiveAdvances(), &item.a, &char_x,
245 249 &trailing);
246 // Error condition, we don't know what to do if we don't have that X 250
247 // position in any of our items. 251 // The character offset is within the item. We need to add the
248 return 0; 252 // item's offset to transform it into the space of the TextRun
249 } 253 return char_x + item.iCharPos;
250 254 }
251 void UniscribeState::Draw(HDC dc, int x, int y, int from, int to) { 255
252 HGDIOBJ old_font = 0; 256 // The offset is beyond this item, account for its length and move on.
253 int cur_x = x; 257 x -= advance_for_item;
254 bool first_run = true; 258 }
255 259
256 for (size_t screen_idx = 0; screen_idx < runs_->size(); screen_idx++) { 260 // Error condition, we don't know what to do if we don't have that X
257 int item_idx = screen_order_[screen_idx]; 261 // position in any of our items.
258 const SCRIPT_ITEM& item = runs_[item_idx]; 262 return 0;
259 const Shaping& shaping = shapes_[item_idx]; 263 }
260 264
261 // Character offsets within this run. THESE MAY NOT BE IN RANGE and may 265 void UniscribeHelper::Draw(HDC dc, int x, int y, int from, int to)
262 // be negative, etc. The code below handles this. 266 {
263 int from_char = from - item.iCharPos; 267 HGDIOBJ oldFont = 0;
264 int to_char = to - item.iCharPos; 268 int curX = x;
265 269 bool firstRun = true;
266 // See if we need to draw any characters in this item. 270
267 if (shaping.char_length() == 0 || 271 for (size_t screen_idx = 0; screen_idx < m_runs.size(); screen_idx++) {
268 from_char >= shaping.char_length() || to_char <= 0) { 272 int itemIdx = m_screenOrder[screen_idx];
269 // No chars in this item to display. 273 const SCRIPT_ITEM& item = m_runs[itemIdx];
270 cur_x += AdvanceForItem(item_idx); 274 const Shaping& shaping = m_shapes[itemIdx];
271 continue; 275
272 } 276 // Character offsets within this run. THESE MAY NOT BE IN RANGE and may
273 277 // be negative, etc. The code below handles this.
274 // Compute the starting glyph within this span. |from| and |to| are 278 int fromChar = from - item.iCharPos;
275 // global offsets that may intersect arbitrarily with our local run. 279 int to_char = to - item.iCharPos;
276 int from_glyph, after_glyph; 280
277 if (item.a.fRTL) { 281 // See if we need to draw any characters in this item.
278 // To compute the first glyph when going RTL, we use |to|. 282 if (shaping.charLength() == 0 ||
279 if (to_char >= shaping.char_length()) { 283 fromChar >= shaping.charLength() || to_char <= 0) {
280 // The end of the text is after (to the left) of us. 284 // No chars in this item to display.
281 from_glyph = 0; 285 curX += AdvanceForItem(itemIdx);
282 } else { 286 continue;
283 // Since |to| is exclusive, the first character we draw on the left 287 }
284 // is actually the one right before (to the right) of |to|. 288
285 from_glyph = shaping.logs[to_char - 1]; 289 // Compute the starting glyph within this span. |from| and |to| are
286 } 290 // global offsets that may intersect arbitrarily with our local run.
287 291 int fromGlyph, afterGlyph;
288 // The last glyph is actually the first character in the range. 292 if (item.a.fRTL) {
289 if (from_char <= 0) { 293 // To compute the first glyph when going RTL, we use |to|.
290 // The first character to draw is before (to the right) of this span, 294 if (to_char >= shaping.charLength()) {
291 // so draw all the way to the end. 295 // The end of the text is after (to the left) of us.
292 after_glyph = shaping.glyph_length(); 296 fromGlyph = 0;
293 } else { 297 } else {
294 // We want to draw everything up until the character to the right of 298 // Since |to| is exclusive, the first character we draw on the
295 // |from|. To the right is - 1, so we look that up (remember our 299 // left is actually the one right before (to the right) of
296 // character could be more than one glyph, so we can't look up our 300 // |to|.
297 // glyph and add one). 301 fromGlyph = shaping.m_logs[to_char - 1];
298 after_glyph = shaping.logs[from_char - 1]; 302 }
299 } 303
304 // The last glyph is actually the first character in the range.
305 if (fromChar <= 0) {
306 // The first character to draw is before (to the right) of this
307 // span, so draw all the way to the end.
308 afterGlyph = shaping.glyphLength();
309 } else {
310 // We want to draw everything up until the character to the
311 // right of |from|. To the right is - 1, so we look that up
312 // (remember our character could be more than one glyph, so we
313 // can't look up our glyph and add one).
314 afterGlyph = shaping.m_logs[fromChar - 1];
315 }
316 } else {
317 // Easy case, everybody agrees about directions. We only need to
318 // handle boundary conditions to get a range inclusive at the
319 // beginning, and exclusive at the ending. We have to do some
320 // computation to see the glyph one past the end.
321 fromGlyph = shaping.m_logs[fromChar < 0 ? 0 : fromChar];
322 if (to_char >= shaping.charLength())
323 afterGlyph = shaping.glyphLength();
324 else
325 afterGlyph = shaping.m_logs[to_char];
326 }
327
328 // Account for the characters that were skipped in this run. When
329 // WebKit asks us to draw a subset of the run, it actually tells us
330 // to draw at the X offset of the beginning of the run, since it
331 // doesn't know the internal position of any of our characters.
332 const int* effectiveAdvances = shaping.effectiveAdvances();
333 int innerOffset = 0;
334 for (int i = 0; i < fromGlyph; i++)
335 innerOffset += effectiveAdvances[i];
336
337 // Actually draw the glyphs we found.
338 int glyphCount = afterGlyph - fromGlyph;
339 if (fromGlyph >= 0 && glyphCount > 0) {
340 // Account for the preceeding space we need to add to this run. We
341 // don't need to count for the following space because that will be
342 // counted in AdvanceForItem below when we move to the next run.
343 innerOffset += shaping.m_prePadding;
344
345 // Pass NULL in when there is no justification.
346 const int* justify = shaping.m_justify.size() == 0 ?
347 NULL : &shaping.m_justify[fromGlyph];
348
349 if (firstRun) {
350 oldFont = SelectObject(dc, shaping.m_hfont);
351 firstRun = false;
352 } else {
353 SelectObject(dc, shaping.m_hfont);
354 }
355
356 // TODO(brettw) bug 698452: if a half a character is selected,
357 // we should set up a clip rect so we draw the half of the glyph
358 // correctly.
359 // Fonts with different ascents can be used to render different
360 // runs. 'Across-runs' y-coordinate correction needs to be
361 // adjusted for each font.
362 HRESULT hr = S_FALSE;
363 for (int executions = 0; executions < 2; ++executions) {
364 hr = ScriptTextOut(dc, shaping.m_scriptCache,
365 curX + innerOffset,
366 y - shaping.m_ascentOffset,
367 0, NULL, &item.a, NULL, 0,
368 &shaping.m_glyphs[fromGlyph],
369 glyphCount,
370 &shaping.m_advance[fromGlyph],
371 justify,
372 &shaping.m_offsets[fromGlyph]);
373 if (S_OK != hr && 0 == executions) {
374 // If this ScriptTextOut is called from the renderer it
375 // might fail because the sandbox is preventing it from
376 // opening the font files. If we are running in the
377 // renderer, TryToPreloadFont is overridden to ask the
378 // browser to preload the font for us so we can access it.
379 TryToPreloadFont(shaping.m_hfont);
380 continue;
381 }
382 break;
383 }
384
385 ASSERT(S_OK == hr);
386 }
387
388 curX += AdvanceForItem(itemIdx);
389 }
390
391 if (oldFont)
392 SelectObject(dc, oldFont);
393 }
394
395 WORD UniscribeHelper::FirstGlyphForCharacter(int charOffset) const
396 {
397 // Find the run for the given character.
398 for (int i = 0; i < static_cast<int>(m_runs.size()); i++) {
399 int firstChar = m_runs[i].iCharPos;
400 const Shaping& shaping = m_shapes[i];
401 int localOffset = charOffset - firstChar;
402 if (localOffset >= 0 && localOffset < shaping.charLength()) {
403 // The character is in this run, return the first glyph for it
404 // (should generally be the only glyph). It seems Uniscribe gives
405 // glyph 0 for empty, which is what we want to return in the
406 // "missing" case.
407 size_t glyphIndex = shaping.m_logs[localOffset];
408 if (glyphIndex >= shaping.m_glyphs.size()) {
409 // The glyph should be in this run, but the run has too few
410 // actual characters. This can happen when shaping the run
411 // fails, in which case, we should have no data in the logs at
412 // all.
413 ASSERT(shaping.m_glyphs.size() == 0);
414 return 0;
415 }
416 return shaping.m_glyphs[glyphIndex];
417 }
418 }
419 return 0;
420 }
421
422 void UniscribeHelper::FillRuns()
423 {
424 HRESULT hr;
425 m_runs.resize(UNISCRIBE_HELPER_STACK_RUNS);
426
427 SCRIPT_STATE inputState;
428 inputState.uBidiLevel = m_isRtl;
429 inputState.fOverrideDirection = m_directionalOverride;
430 inputState.fInhibitSymSwap = false;
431 inputState.fCharShape = false; // Not implemented in Uniscribe
432 inputState.fDigitSubstitute = false; // Do we want this for Arabic?
433 inputState.fInhibitLigate = m_inhibitLigate;
434 inputState.fDisplayZWG = false; // Don't draw control characters.
435 inputState.fArabicNumContext = m_isRtl; // Do we want this for Arabic?
436 inputState.fGcpClusters = false;
437 inputState.fReserved = 0;
438 inputState.fEngineReserved = 0;
439 // The psControl argument to ScriptItemize should be non-NULL for RTL text,
440 // per http://msdn.microsoft.com/en-us/library/ms776532.aspx . So use a
441 // SCRIPT_CONTROL that is set to all zeros. Zero as a locale ID means the
442 // neutral locale per http://msdn.microsoft.com/en-us/library/ms776294.aspx
443 static SCRIPT_CONTROL inputControl = {0, // uDefaultLanguage :16;
444 0, // fContextDigits :1;
445 0, // fInvertPreBoundDir :1;
446 0, // fInvertPostBoundDir :1;
447 0, // fLinkStringBefore :1;
448 0, // fLinkStringAfter :1;
449 0, // fNeutralOverride :1;
450 0, // fNumericOverride :1;
451 0, // fLegacyBidiClass :1;
452 0, // fMergeNeutralItems :1;
453 0};// fReserved :7;
454 // Calling ScriptApplyDigitSubstitution( NULL, &inputControl, &inputState)
455 // here would be appropriate if we wanted to set the language ID, and get
456 // local digit substitution behavior. For now, don't do it.
457
458 while (true) {
459 int num_items = 0;
460
461 // Ideally, we would have a way to know the runs before and after this
462 // one, and put them into the control parameter of ScriptItemize. This
463 // would allow us to shape characters properly that cross style
464 // boundaries (WebKit bug 6148).
465 //
466 // We tell ScriptItemize that the output list of items is one smaller
467 // than it actually is. According to Mozilla bug 366643, if there is
468 // not enough room in the array on pre-SP2 systems, ScriptItemize will
469 // write one past the end of the buffer.
470 //
471 // ScriptItemize is very strange. It will often require a much larger
472 // ITEM buffer internally than it will give us as output. For example,
473 // it will say a 16-item buffer is not big enough, and will write
474 // interesting numbers into all those items. But when we give it a 32
475 // item buffer and it succeeds, it only has one item output.
476 //
477 // It seems to be doing at least two passes, the first where it puts a
478 // lot of intermediate data into our items, and the second where it
479 // collates them.
480 hr = ScriptItemize(m_input, m_inputLength,
481 static_cast<int>(m_runs.size()) - 1, &inputControl,
482 &inputState,
483 &m_runs[0], &num_items);
484 if (SUCCEEDED(hr)) {
485 m_runs.resize(num_items);
486 break;
487 }
488 if (hr != E_OUTOFMEMORY) {
489 // Some kind of unexpected error.
490 m_runs.resize(0);
491 break;
492 }
493 // There was not enough items for it to write into, expand.
494 m_runs.resize(m_runs.size() * 2);
495 }
496 }
497
498 bool UniscribeHelper::Shape(const UChar* input,
499 int itemLength,
500 int numGlyphs,
501 SCRIPT_ITEM& run,
502 Shaping& shaping)
503 {
504 HFONT hfont = m_hfont;
505 SCRIPT_CACHE* scriptCache = m_scriptCache;
506 SCRIPT_FONTPROPERTIES* fontProperties = m_fontProperties;
507 int ascent = m_ascent;
508 HDC tempDC = NULL;
509 HGDIOBJ oldFont = 0;
510 HRESULT hr;
511 bool lastFallbackTried = false;
512 bool result;
513
514 int generatedGlyphs = 0;
515
516 // In case HFONT passed in ctor cannot render this run, we have to scan
517 // other fonts from the beginning of the font list.
518 ResetFontIndex();
519
520 // Compute shapes.
521 while (true) {
522 shaping.m_logs.resize(itemLength);
523 shaping.m_glyphs.resize(numGlyphs);
524 shaping.m_visattr.resize(numGlyphs);
525
526 // Firefox sets SCRIPT_ANALYSIS.SCRIPT_STATE.fDisplayZWG to true
527 // here. Is that what we want? It will display control characters.
528 hr = ScriptShape(tempDC, scriptCache, input, itemLength,
529 numGlyphs, &run.a,
530 &shaping.m_glyphs[0], &shaping.m_logs[0],
531 &shaping.m_visattr[0], &generatedGlyphs);
532 if (hr == E_PENDING) {
533 // Allocate the DC.
534 tempDC = GetDC(NULL);
535 oldFont = SelectObject(tempDC, hfont);
536 continue;
537 } else if (hr == E_OUTOFMEMORY) {
538 numGlyphs *= 2;
539 continue;
540 } else if (SUCCEEDED(hr) &&
541 (lastFallbackTried ||
542 !ContainsMissingGlyphs(&shaping.m_glyphs[0],
543 generatedGlyphs, fontProperties))) {
544 break;
545 }
546
547 // The current font can't render this run. clear DC and try
548 // next font.
549 if (tempDC) {
550 SelectObject(tempDC, oldFont);
551 ReleaseDC(NULL, tempDC);
552 tempDC = NULL;
553 }
554
555 if (NextWinFontData(&hfont, &scriptCache, &fontProperties, &ascent)) {
556 // The primary font does not support this run. Try next font.
557 // In case of web page rendering, they come from fonts specified in
558 // CSS stylesheets.
559 continue;
560 } else if (!lastFallbackTried) {
561 lastFallbackTried = true;
562
563 // Generate a last fallback font based on the script of
564 // a character to draw while inheriting size and styles
565 // from the primary font
566 if (!m_logfont.lfFaceName[0])
567 SetLogFontAndStyle(m_hfont, &m_logfont, &m_style);
568
569 // TODO(jungshik): generic type should come from webkit for
570 // UniscribeHelperTextRun (a derived class used in webkit).
571 const UChar *family = GetFallbackFamily(input, itemLength,
572 GENERIC_FAMILY_STANDARD, NULL, NULL);
573 bool font_ok = GetDerivedFontData(family, m_style, &m_logfont,
574 &ascent, &hfont, &scriptCache);
575
576 if (!font_ok) {
577 // If this GetDerivedFontData is called from the renderer it
578 // might fail because the sandbox is preventing it from opening
579 // the font files. If we are running in the renderer,
580 // TryToPreloadFont is overridden to ask the browser to preload
581 // the font for us so we can access it.
582 TryToPreloadFont(hfont);
583
584 // Try again.
585 font_ok = GetDerivedFontData(family, m_style, &m_logfont,
586 &ascent, &hfont, &scriptCache);
587 ASSERT(font_ok);
588 }
589
590 // TODO(jungshik) : Currently GetDerivedHFont always returns a
591 // a valid HFONT, but in the future, I may change it to return 0.
592 ASSERT(hfont);
593
594 // We don't need a font_properties for the last resort fallback font
595 // because we don't have anything more to try and are forced to
596 // accept empty glyph boxes. If we tried a series of fonts as
597 // 'last-resort fallback', we'd need it, but currently, we don't.
598 continue;
599 } else if (hr == USP_E_SCRIPT_NOT_IN_FONT) {
600 run.a.eScript = SCRIPT_UNDEFINED;
601 continue;
602 } else if (FAILED(hr)) {
603 // Error shaping.
604 generatedGlyphs = 0;
605 result = false;
606 goto cleanup;
607 }
608 }
609
610 // Sets Windows font data for this run to those corresponding to
611 // a font supporting this run. we don't need to store font_properties
612 // because it's not used elsewhere.
613 shaping.m_hfont = hfont;
614 shaping.m_scriptCache = scriptCache;
615
616 // The ascent of a font for this run can be different from
617 // that of the primary font so that we need to keep track of
618 // the difference per run and take that into account when calling
619 // ScriptTextOut in |Draw|. Otherwise, different runs rendered by
620 // different fonts would not be aligned vertically.
621 shaping.m_ascentOffset = m_ascent ? ascent - m_ascent : 0;
622 result = true;
623
624 cleanup:
625 shaping.m_glyphs.resize(generatedGlyphs);
626 shaping.m_visattr.resize(generatedGlyphs);
627 shaping.m_advance.resize(generatedGlyphs);
628 shaping.m_offsets.resize(generatedGlyphs);
629 if (tempDC) {
630 SelectObject(tempDC, oldFont);
631 ReleaseDC(NULL, tempDC);
632 }
633 // On failure, our logs don't mean anything, so zero those out.
634 if (!result)
635 shaping.m_logs.clear();
636
637 return result;
638 }
639
640 void UniscribeHelper::FillShapes()
641 {
642 m_shapes.resize(m_runs.size());
643 for (size_t i = 0; i < m_runs.size(); i++) {
644 int startItem = m_runs[i].iCharPos;
645 int itemLength = m_inputLength - startItem;
646 if (i < m_runs.size() - 1)
647 itemLength = m_runs[i + 1].iCharPos - startItem;
648
649 int numGlyphs;
650 if (itemLength < UNISCRIBE_HELPER_STACK_CHARS) {
651 // We'll start our buffer sizes with the current stack space
652 // available in our buffers if the current input fits. As long as
653 // it doesn't expand past that we'll save a lot of time mallocing.
654 numGlyphs = UNISCRIBE_HELPER_STACK_CHARS;
655 } else {
656 // When the input doesn't fit, give up with the stack since it will
657 // almost surely not be enough room (unless the input actually
658 // shrinks, which is unlikely) and just start with the length
659 // recommended by the Uniscribe documentation as a "usually fits"
660 // size.
661 numGlyphs = itemLength * 3 / 2 + 16;
662 }
663
664 // Convert a string to a glyph string trying the primary font, fonts in
665 // the fallback list and then script-specific last resort font.
666 Shaping& shaping = m_shapes[i];
667 if (!Shape(&m_input[startItem], itemLength, numGlyphs, m_runs[i],
668 shaping))
669 continue;
670
671 // Compute placements. Note that offsets is documented incorrectly
672 // and is actually an array.
673
674 // DC that we lazily create if Uniscribe commands us to.
675 // (this does not happen often because scriptCache is already
676 // updated when calling ScriptShape).
677 HDC tempDC = NULL;
678 HGDIOBJ oldFont = NULL;
679 HRESULT hr;
680 while (true) {
681 shaping.m_prePadding = 0;
682 hr = ScriptPlace(tempDC, shaping.m_scriptCache,
683 &shaping.m_glyphs[0],
684 static_cast<int>(shaping.m_glyphs.size()),
685 &shaping.m_visattr[0], &m_runs[i].a,
686 &shaping.m_advance[0], &shaping.m_offsets[0],
687 &shaping.m_abc);
688 if (hr != E_PENDING)
689 break;
690
691 // Allocate the DC and run the loop again.
692 tempDC = GetDC(NULL);
693 oldFont = SelectObject(tempDC, shaping.m_hfont);
694 }
695
696 if (FAILED(hr)) {
697 // Some error we don't know how to handle. Nuke all of our data
698 // since we can't deal with partially valid data later.
699 m_runs.clear();
700 m_shapes.clear();
701 m_screenOrder.clear();
702 }
703
704 if (tempDC) {
705 SelectObject(tempDC, oldFont);
706 ReleaseDC(NULL, tempDC);
707 }
708 }
709
710 AdjustSpaceAdvances();
711
712 if (m_letterSpacing != 0 || m_wordSpacing != 0)
713 ApplySpacing();
714 }
715
716 void UniscribeHelper::FillScreenOrder()
717 {
718 m_screenOrder.resize(m_runs.size());
719
720 // We assume that the input has only one text direction in it.
721 // TODO(brettw) are we sure we want to keep this restriction?
722 if (m_isRtl) {
723 for (int i = 0; i < static_cast<int>(m_screenOrder.size()); i++)
724 m_screenOrder[static_cast<int>(m_screenOrder.size()) - i - 1] = i;
300 } else { 725 } else {
301 // Easy case, everybody agrees about directions. We only need to handle 726 for (int i = 0; i < static_cast<int>(m_screenOrder.size()); i++)
302 // boundary conditions to get a range inclusive at the beginning, and 727 m_screenOrder[i] = i;
303 // exclusive at the ending. We have to do some computation to see the 728 }
304 // glyph one past the end. 729 }
305 from_glyph = shaping.logs[from_char < 0 ? 0 : from_char]; 730
306 if (to_char >= shaping.char_length()) 731 void UniscribeHelper::AdjustSpaceAdvances()
307 after_glyph = shaping.glyph_length(); 732 {
308 else 733 if (m_spaceWidth == 0)
309 after_glyph = shaping.logs[to_char]; 734 return;
310 } 735
311 736 int spaceWidthWithoutLetterSpacing = m_spaceWidth - m_letterSpacing;
312 // Account for the characters that were skipped in this run. When 737
313 // WebKit asks us to draw a subset of the run, it actually tells us 738 // This mostly matches what WebKit's UniscribeController::shapeAndPlaceItem.
314 // to draw at the X offset of the beginning of the run, since it 739 for (size_t run = 0; run < m_runs.size(); run++) {
315 // doesn't know the internal position of any of our characters. 740 Shaping& shaping = m_shapes[run];
316 const int* effective_advances = shaping.effective_advances(); 741
317 int inner_offset = 0; 742 for (int i = 0; i < shaping.charLength(); i++) {
318 for (int i = 0; i < from_glyph; i++) 743 if (!TreatAsSpace(m_input[m_runs[run].iCharPos + i]))
319 inner_offset += effective_advances[i]; 744 continue;
320 745
321 // Actually draw the glyphs we found. 746 int glyphIndex = shaping.m_logs[i];
322 int glyph_count = after_glyph - from_glyph; 747 int currentAdvance = shaping.m_advance[glyphIndex];
323 if (from_glyph >= 0 && glyph_count > 0) { 748 // Don't give zero-width spaces a width.
324 // Account for the preceeding space we need to add to this run. We don't 749 if (!currentAdvance)
325 // need to count for the following space because that will be counted 750 continue;
326 // in AdvanceForItem below when we move to the next run. 751
327 inner_offset += shaping.pre_padding; 752 // currentAdvance does not include additional letter-spacing, but
328 753 // space_width does. Here we find out how off we are from the
329 // Pass NULL in when there is no justification. 754 // correct width for the space not including letter-spacing, then
330 const int* justify = shaping.justify->empty() ? 755 // just subtract that diff.
331 NULL : &shaping.justify[from_glyph]; 756 int diff = currentAdvance - spaceWidthWithoutLetterSpacing;
332 757 // The shaping can consist of a run of text, so only subtract the
333 if (first_run) { 758 // difference in the width of the glyph.
334 old_font = SelectObject(dc, shaping.hfont_); 759 shaping.m_advance[glyphIndex] -= diff;
335 first_run = false; 760 shaping.m_abc.abcB -= diff;
336 } else { 761 }
337 SelectObject(dc, shaping.hfont_); 762 }
338 } 763 }
339 764
340 // TODO(brettw) bug 698452: if a half a character is selected, 765 void UniscribeHelper::ApplySpacing()
341 // we should set up a clip rect so we draw the half of the glyph 766 {
342 // correctly. 767 for (size_t run = 0; run < m_runs.size(); run++) {
343 // Fonts with different ascents can be used to render different runs. 768 Shaping& shaping = m_shapes[run];
344 // 'Across-runs' y-coordinate correction needs to be adjusted 769 bool isRtl = m_runs[run].a.fRTL;
345 // for each font. 770
346 HRESULT hr = S_FALSE; 771 if (m_letterSpacing != 0) {
347 for (int executions = 0; executions < 2; ++executions) { 772 // RTL text gets padded to the left of each character. We increment
348 hr = ScriptTextOut(dc, shaping.script_cache_, cur_x + inner_offset, 773 // the run's advance to make this happen. This will be balanced out
349 y - shaping.ascent_offset_, 0, NULL, &item.a, NULL, 774 // by NOT adding additional advance to the last glyph in the run.
350 0, &shaping.glyphs[from_glyph], 775 if (isRtl)
351 glyph_count, &shaping.advance[from_glyph], 776 shaping.m_prePadding += m_letterSpacing;
352 justify, &shaping.offsets[from_glyph]); 777
353 if (S_OK != hr && 0 == executions) { 778 // Go through all the glyphs in this run and increase the "advance"
354 // If this ScriptTextOut is called from the renderer it might fail 779 // to account for letter spacing. We adjust letter spacing only on
355 // because the sandbox is preventing it from opening the font files. 780 // cluster boundaries.
356 // If we are running in the renderer, TryToPreloadFont is overridden 781 //
357 // to ask the browser to preload the font for us so we can access it. 782 // This works for most scripts, but may have problems with some
358 TryToPreloadFont(shaping.hfont_); 783 // indic scripts. This behavior is better than Firefox or IE for
359 continue; 784 // Hebrew.
360 } 785 for (int i = 0; i < shaping.glyphLength(); i++) {
361 break; 786 if (shaping.m_visattr[i].fClusterStart) {
362 } 787 // Ick, we need to assign the extra space so that the glyph
363 788 // comes first, then is followed by the space. This is
364 DCHECK(S_OK == hr); 789 // opposite for RTL.
365 790 if (isRtl) {
366 791 if (i != shaping.glyphLength() - 1) {
367 } 792 // All but the last character just get the spacing
368 793 // applied to their advance. The last character
369 cur_x += AdvanceForItem(item_idx); 794 // doesn't get anything,
370 } 795 shaping.m_advance[i] += m_letterSpacing;
371 796 shaping.m_abc.abcB += m_letterSpacing;
372 if (old_font) 797 }
373 SelectObject(dc, old_font); 798 } else {
374 } 799 // LTR case is easier, we just add to the advance.
375 800 shaping.m_advance[i] += m_letterSpacing;
376 WORD UniscribeState::FirstGlyphForCharacter(int char_offset) const { 801 shaping.m_abc.abcB += m_letterSpacing;
377 // Find the run for the given character. 802 }
378 for (int i = 0; i < static_cast<int>(runs_->size()); i++) { 803 }
379 int first_char = runs_[i].iCharPos;
380 const Shaping& shaping = shapes_[i];
381 int local_offset = char_offset - first_char;
382 if (local_offset >= 0 && local_offset < shaping.char_length()) {
383 // The character is in this run, return the first glyph for it (should
384 // generally be the only glyph). It seems Uniscribe gives glyph 0 for
385 // empty, which is what we want to return in the "missing" case.
386 size_t glyph_index = shaping.logs[local_offset];
387 if (glyph_index >= shaping.glyphs->size()) {
388 // The glyph should be in this run, but the run has too few actual
389 // characters. This can happen when shaping the run fails, in which
390 // case, we should have no data in the logs at all.
391 DCHECK(shaping.glyphs->empty());
392 return 0;
393 }
394 return shaping.glyphs[glyph_index];
395 }
396 }
397 return 0;
398 }
399
400 void UniscribeState::FillRuns() {
401 HRESULT hr;
402 runs_->resize(UNISCRIBE_STATE_STACK_RUNS);
403
404 SCRIPT_STATE input_state;
405 input_state.uBidiLevel = is_rtl_;
406 input_state.fOverrideDirection = directional_override_;
407 input_state.fInhibitSymSwap = false;
408 input_state.fCharShape = false; // Not implemented in Uniscribe
409 input_state.fDigitSubstitute = false; // Do we want this for Arabic?
410 input_state.fInhibitLigate = inhibit_ligate_;
411 input_state.fDisplayZWG = false; // Don't draw control characters.
412 input_state.fArabicNumContext = is_rtl_; // Do we want this for Arabic?
413 input_state.fGcpClusters = false;
414 input_state.fReserved = 0;
415 input_state.fEngineReserved = 0;
416 // The psControl argument to ScriptItemize should be non-NULL for RTL text,
417 // per http://msdn.microsoft.com/en-us/library/ms776532.aspx . So use a
418 // SCRIPT_CONTROL that is set to all zeros. Zero as a locale ID means the
419 // neutral locale per http://msdn.microsoft.com/en-us/library/ms776294.aspx .
420 static SCRIPT_CONTROL input_control = {0, // uDefaultLanguage :16;
421 0, // fContextDigits :1;
422 0, // fInvertPreBoundDir :1;
423 0, // fInvertPostBoundDir :1;
424 0, // fLinkStringBefore :1;
425 0, // fLinkStringAfter :1;
426 0, // fNeutralOverride :1;
427 0, // fNumericOverride :1;
428 0, // fLegacyBidiClass :1;
429 0, // fMergeNeutralItems :1;
430 0};// fReserved :7;
431 // Calling ScriptApplyDigitSubstitution( NULL, &input_control, &input_state)
432 // here would be appropriate if we wanted to set the language ID, and get
433 // local digit substitution behavior. For now, don't do it.
434
435 while (true) {
436 int num_items = 0;
437
438 // Ideally, we would have a way to know the runs before and after this
439 // one, and put them into the control parameter of ScriptItemize. This
440 // would allow us to shape characters properly that cross style
441 // boundaries (WebKit bug 6148).
442 //
443 // We tell ScriptItemize that the output list of items is one smaller
444 // than it actually is. According to Mozilla bug 366643, if there is
445 // not enough room in the array on pre-SP2 systems, ScriptItemize will
446 // write one past the end of the buffer.
447 //
448 // ScriptItemize is very strange. It will often require a much larger
449 // ITEM buffer internally than it will give us as output. For example,
450 // it will say a 16-item buffer is not big enough, and will write
451 // interesting numbers into all those items. But when we give it a 32
452 // item buffer and it succeeds, it only has one item output.
453 //
454 // It seems to be doing at least two passes, the first where it puts a
455 // lot of intermediate data into our items, and the second where it
456 // collates them.
457 hr = ScriptItemize(input_, input_length_,
458 static_cast<int>(runs_->size()) - 1, &input_control, &inp ut_state,
459 &runs_[0], &num_items);
460 if (SUCCEEDED(hr)) {
461 runs_->resize(num_items);
462 break;
463 }
464 if (hr != E_OUTOFMEMORY) {
465 // Some kind of unexpected error.
466 runs_->resize(0);
467 break;
468 }
469 // There was not enough items for it to write into, expand.
470 runs_->resize(runs_->size() * 2);
471 }
472
473 // Fix up the directions of the items so they're what WebKit thinks
474 // they are. WebKit (and we assume any other caller) always knows what
475 // direction it wants things to be in, and will only give us runs that are in
476 // the same direction. Sometimes, Uniscibe disagrees, for example, if you
477 // have embedded ASCII punctuation in an Arabic string, WebKit will
478 // (correctly) know that is should still be rendered RTL, but Uniscibe might
479 // think LTR is better.
480 //
481 // TODO(brettw) bug 747235:
482 // This workaround fixes the bug but causes spacing problems in other cases.
483 // WebKit sometimes gives us a big run that includes ASCII and Arabic, and
484 // this forcing direction makes those cases incorrect. This seems to happen
485 // during layout only, so it ends up that spacing is incorrect (because being
486 // the wrong direction changes ligatures and stuff).
487 //
488 //for (size_t i = 0; i < runs_->size(); i++)
489 // runs_[i].a.fRTL = is_rtl_;
490 }
491
492
493 bool UniscribeState::Shape(const wchar_t* input,
494 int item_length,
495 int num_glyphs,
496 SCRIPT_ITEM& run,
497 Shaping& shaping) {
498 HFONT hfont = hfont_;
499 SCRIPT_CACHE* script_cache = script_cache_;
500 SCRIPT_FONTPROPERTIES* font_properties = font_properties_;
501 int ascent = ascent_;
502 HDC temp_dc = NULL;
503 HGDIOBJ old_font = 0;
504 HRESULT hr;
505 bool lastFallbackTried = false;
506 bool result;
507
508 int generated_glyphs = 0;
509
510 // In case HFONT passed in ctor cannot render this run, we have to scan
511 // other fonts from the beginning of the font list.
512 ResetFontIndex();
513
514 // Compute shapes.
515 while (true) {
516 shaping.logs->resize(item_length);
517 shaping.glyphs->resize(num_glyphs);
518 shaping.visattr->resize(num_glyphs);
519
520 // Firefox sets SCRIPT_ANALYSIS.SCRIPT_STATE.fDisplayZWG to true
521 // here. Is that what we want? It will display control characters.
522 hr = ScriptShape(temp_dc, script_cache, input, item_length,
523 num_glyphs, &run.a,
524 &shaping.glyphs[0], &shaping.logs[0],
525 &shaping.visattr[0], &generated_glyphs);
526 if (hr == E_PENDING) {
527 // Allocate the DC.
528 temp_dc = GetDC(NULL);
529 old_font = SelectObject(temp_dc, hfont);
530 continue;
531 } else if (hr == E_OUTOFMEMORY) {
532 num_glyphs *= 2;
533 continue;
534 } else if (SUCCEEDED(hr) &&
535 (lastFallbackTried || !ContainsMissingGlyphs(&shaping.glyphs[0],
536 generated_glyphs, font_properties))) {
537 break;
538 }
539
540 // The current font can't render this run. clear DC and try
541 // next font.
542 if (temp_dc) {
543 SelectObject(temp_dc, old_font);
544 ReleaseDC(NULL, temp_dc);
545 temp_dc = NULL;
546 }
547
548 if (NextWinFontData(&hfont, &script_cache, &font_properties, &ascent)) {
549 // The primary font does not support this run. Try next font.
550 // In case of web page rendering, they come from fonts specified in
551 // CSS stylesheets.
552 continue;
553 } else if (!lastFallbackTried) {
554 lastFallbackTried = true;
555
556 // Generate a last fallback font based on the script of
557 // a character to draw while inheriting size and styles
558 // from the primary font
559 if (!logfont_.lfFaceName[0])
560 SetLogFontAndStyle(hfont_, &logfont_, &style_);
561
562 // TODO(jungshik): generic type should come from webkit for
563 // UniscribeStateTextRun (a derived class used in webkit).
564 const wchar_t *family = GetFallbackFamily(input, item_length,
565 GENERIC_FAMILY_STANDARD, NULL, NULL);
566 bool font_ok = GetDerivedFontData(family, style_, &logfont_, &ascent, &hfo nt, &script_cache);
567
568 if (!font_ok) {
569 // If this GetDerivedFontData is called from the renderer it might fail
570 // because the sandbox is preventing it from opening the font files.
571 // If we are running in the renderer, TryToPreloadFont is overridden to
572 // ask the browser to preload the font for us so we can access it.
573 TryToPreloadFont(hfont);
574
575 // Try again.
576 font_ok = GetDerivedFontData(family, style_, &logfont_, &ascent, &hfont, &script_cache);
577 DCHECK(font_ok);
578 }
579
580 // TODO(jungshik) : Currently GetDerivedHFont always returns a
581 // a valid HFONT, but in the future, I may change it to return 0.
582 DCHECK(hfont);
583
584 // We don't need a font_properties for the last resort fallback font
585 // because we don't have anything more to try and are forced to
586 // accept empty glyph boxes. If we tried a series of fonts as
587 // 'last-resort fallback', we'd need it, but currently, we don't.
588 continue;
589 } else if (hr == USP_E_SCRIPT_NOT_IN_FONT) {
590 run.a.eScript = SCRIPT_UNDEFINED;
591 continue;
592 } else if (FAILED(hr)) {
593 // Error shaping.
594 generated_glyphs = 0;
595 result = false;
596 goto cleanup;
597 }
598 }
599
600 // Sets Windows font data for this run to those corresponding to
601 // a font supporting this run. we don't need to store font_properties
602 // because it's not used elsewhere.
603 shaping.hfont_ = hfont;
604 shaping.script_cache_ = script_cache;
605
606 // The ascent of a font for this run can be different from
607 // that of the primary font so that we need to keep track of
608 // the difference per run and take that into account when calling
609 // ScriptTextOut in |Draw|. Otherwise, different runs rendered by
610 // different fonts would not be aligned vertically.
611 shaping.ascent_offset_ = ascent_ ? ascent - ascent_ : 0;
612 result = true;
613
614 cleanup:
615 shaping.glyphs->resize(generated_glyphs);
616 shaping.visattr->resize(generated_glyphs);
617 shaping.advance->resize(generated_glyphs);
618 shaping.offsets->resize(generated_glyphs);
619 if (temp_dc) {
620 SelectObject(temp_dc, old_font);
621 ReleaseDC(NULL, temp_dc);
622 }
623 // On failure, our logs don't mean anything, so zero those out.
624 if (!result)
625 shaping.logs->clear();
626
627 return result;
628 }
629
630 void UniscribeState::FillShapes() {
631 shapes_->resize(runs_->size());
632 for (size_t i = 0; i < runs_->size(); i++) {
633 int start_item = runs_[i].iCharPos;
634 int item_length = input_length_ - start_item;
635 if (i < runs_->size() - 1)
636 item_length = runs_[i + 1].iCharPos - start_item;
637
638 int num_glyphs;
639 if (item_length < UNISCRIBE_STATE_STACK_CHARS) {
640 // We'll start our buffer sizes with the current stack space available
641 // in our buffers if the current input fits. As long as it
642 // doesn't expand past that we'll save a lot of time mallocing.
643 num_glyphs = UNISCRIBE_STATE_STACK_CHARS;
644 } else {
645 // When the input doesn't fit, give up with the stack since it will
646 // almost surely not be enough room (unless the input actually shrinks,
647 // which is unlikely) and just start with the length recommended by
648 // the Uniscribe documentation as a "usually fits" size.
649 num_glyphs = item_length * 3 / 2 + 16;
650 }
651
652 // Convert a string to a glyph string trying the primary font,
653 // fonts in the fallback list and then script-specific last resort font.
654 Shaping& shaping = shapes_[i];
655 if (!Shape(&input_[start_item], item_length, num_glyphs, runs_[i], shaping))
656 continue;
657
658 // Compute placements. Note that offsets is documented incorrectly
659 // and is actually an array.
660
661 // DC that we lazily create if Uniscribe commands us to.
662 // (this does not happen often because script_cache is already
663 // updated when calling ScriptShape).
664 HDC temp_dc = NULL;
665 HGDIOBJ old_font = NULL;
666 HRESULT hr;
667 while (true) {
668 shaping.pre_padding = 0;
669 hr = ScriptPlace(temp_dc, shaping.script_cache_, &shaping.glyphs[0],
670 static_cast<int>(shaping.glyphs->size()),
671 &shaping.visattr[0], &runs_[i].a,
672 &shaping.advance[0], &shaping.offsets[0],
673 &shaping.abc);
674 if (hr != E_PENDING)
675 break;
676
677 // Allocate the DC and run the loop again.
678 temp_dc = GetDC(NULL);
679 old_font = SelectObject(temp_dc, shaping.hfont_);
680 }
681
682 if (FAILED(hr)) {
683 // Some error we don't know how to handle. Nuke all of our data
684 // since we can't deal with partially valid data later.
685 runs_->clear();
686 shapes_->clear();
687 screen_order_->clear();
688 }
689
690 if (temp_dc) {
691 SelectObject(temp_dc, old_font);
692 ReleaseDC(NULL, temp_dc);
693 }
694 }
695
696 AdjustSpaceAdvances();
697
698 if (letter_spacing_ != 0 || word_spacing_ != 0)
699 ApplySpacing();
700 }
701
702 void UniscribeState::FillScreenOrder() {
703 screen_order_->resize(runs_->size());
704
705 // We assume that the input has only one text direction in it.
706 // TODO(brettw) are we sure we want to keep this restriction?
707 if (is_rtl_) {
708 for (int i = 0; i < static_cast<int>(screen_order_->size()); i++)
709 screen_order_[static_cast<int>(screen_order_->size()) - i - 1] = i;
710 } else {
711 for (int i = 0; i < static_cast<int>(screen_order_->size()); i++)
712 screen_order_[i] = i;
713 }
714 }
715
716 void UniscribeState::AdjustSpaceAdvances() {
717 if (space_width_ == 0)
718 return;
719
720 int space_width_without_letter_spacing = space_width_ - letter_spacing_;
721
722 // This mostly matches what WebKit's UniscribeController::shapeAndPlaceItem.
723 for (size_t run = 0; run < runs_->size(); run++) {
724 Shaping& shaping = shapes_[run];
725
726 for (int i = 0; i < shaping.char_length(); i++) {
727 if (!TreatAsSpace(input_[runs_[run].iCharPos + i]))
728 continue;
729
730 int glyph_index = shaping.logs[i];
731 int current_advance = shaping.advance[glyph_index];
732 // Don't give zero-width spaces a width.
733 if (!current_advance)
734 continue;
735
736 // current_advance does not include additional letter-spacing, but
737 // space_width does. Here we find out how off we are from the correct
738 // width for the space not including letter-spacing, then just subtract
739 // that diff.
740 int diff = current_advance - space_width_without_letter_spacing;
741 // The shaping can consist of a run of text, so only subtract the
742 // difference in the width of the glyph.
743 shaping.advance[glyph_index] -= diff;
744 shaping.abc.abcB -= diff;
745 }
746 }
747 }
748
749 void UniscribeState::ApplySpacing() {
750 for (size_t run = 0; run < runs_->size(); run++) {
751 Shaping& shaping = shapes_[run];
752 bool is_rtl = runs_[run].a.fRTL;
753
754 if (letter_spacing_ != 0) {
755 // RTL text gets padded to the left of each character. We increment the
756 // run's advance to make this happen. This will be balanced out by NOT
757 // adding additional advance to the last glyph in the run.
758 if (is_rtl)
759 shaping.pre_padding += letter_spacing_;
760
761 // Go through all the glyphs in this run and increase the "advance" to
762 // account for letter spacing. We adjust letter spacing only on cluster
763 // boundaries.
764 //
765 // This works for most scripts, but may have problems with some indic
766 // scripts. This behavior is better than Firefox or IE for Hebrew.
767 for (int i = 0; i < shaping.glyph_length(); i++) {
768 if (shaping.visattr[i].fClusterStart) {
769 // Ick, we need to assign the extra space so that the glyph comes
770 // first, then is followed by the space. This is opposite for RTL.
771 if (is_rtl) {
772 if (i != shaping.glyph_length() - 1) {
773 // All but the last character just get the spacing applied to
774 // their advance. The last character doesn't get anything,
775 shaping.advance[i] += letter_spacing_;
776 shaping.abc.abcB += letter_spacing_;
777 } 804 }
778 } else { 805 }
779 // LTR case is easier, we just add to the advance. 806
780 shaping.advance[i] += letter_spacing_; 807 // Go through all the characters to find whitespace and insert the
781 shaping.abc.abcB += letter_spacing_; 808 // extra wordspacing amount for the glyphs they correspond to.
782 } 809 if (m_wordSpacing != 0) {
783 } 810 for (int i = 0; i < shaping.charLength(); i++) {
784 } 811 if (!TreatAsSpace(m_input[m_runs[run].iCharPos + i]))
785 } 812 continue;
786 813
787 // Go through all the characters to find whitespace and insert the extra 814 // The char in question is a word separator...
788 // wordspacing amount for the glyphs they correspond to. 815 int glyphIndex = shaping.m_logs[i];
789 if (word_spacing_ != 0) { 816
790 for (int i = 0; i < shaping.char_length(); i++) { 817 // Spaces will not have a glyph in Uniscribe, it will just add
791 if (!TreatAsSpace(input_[runs_[run].iCharPos + i])) 818 // additional advance to the character to the left of the
792 continue; 819 // space. The space's corresponding glyph will be the character
793 820 // following it in reading order.
794 // The char in question is a word separator... 821 if (isRtl) {
795 int glyph_index = shaping.logs[i]; 822 // In RTL, the glyph to the left of the space is the same
796 823 // as the first glyph of the following character, so we can
797 // Spaces will not have a glyph in Uniscribe, it will just add 824 // just increment it.
798 // additional advance to the character to the left of the space. The 825 shaping.m_advance[glyphIndex] += m_wordSpacing;
799 // space's corresponding glyph will be the character following it in 826 shaping.m_abc.abcB += m_wordSpacing;
800 // reading order. 827 } else {
801 if (is_rtl) { 828 // LTR is actually more complex here, we apply it to the
802 // In RTL, the glyph to the left of the space is the same as the 829 // previous character if there is one, otherwise we have to
803 // first glyph of the following character, so we can just increment 830 // apply it to the leading space of the run.
804 // it. 831 if (glyphIndex == 0) {
805 shaping.advance[glyph_index] += word_spacing_; 832 shaping.m_prePadding += m_wordSpacing;
806 shaping.abc.abcB += word_spacing_; 833 } else {
807 } else { 834 shaping.m_advance[glyphIndex - 1] += m_wordSpacing;
808 // LTR is actually more complex here, we apply it to the previous 835 shaping.m_abc.abcB += m_wordSpacing;
809 // character if there is one, otherwise we have to apply it to the 836 }
810 // leading space of the run. 837 }
811 if (glyph_index == 0) { 838 }
812 shaping.pre_padding += word_spacing_; 839 } // m_wordSpacing != 0
813 } else { 840
814 shaping.advance[glyph_index - 1] += word_spacing_; 841 // Loop for next run...
815 shaping.abc.abcB += word_spacing_; 842 }
816 }
817 }
818 }
819 } // word_spacing_ != 0
820
821 // Loop for next run...
822 }
823 } 843 }
824 844
825 // The advance is the ABC width of the run 845 // The advance is the ABC width of the run
826 int UniscribeState::AdvanceForItem(int item_index) const { 846 int UniscribeHelper::AdvanceForItem(int item_index) const
827 int accum = 0; 847 {
828 const Shaping& shaping = shapes_[item_index]; 848 int accum = 0;
829 849 const Shaping& shaping = m_shapes[item_index];
830 if (shaping.justify->empty()) { 850
831 // Easy case with no justification, the width is just the ABC width of» t» » 851 if (shaping.m_justify.size() == 0) {
832 // the run. (The ABC width is the sum of the advances). 852 // Easy case with no justification, the width is just the ABC width of
833 return shaping.abc.abcA + shaping.abc.abcB + shaping.abc.abcC + 853 // the run. (The ABC width is the sum of the advances).
834 shaping.pre_padding; 854 return shaping.m_abc.abcA + shaping.m_abc.abcB +
835 } 855 shaping.m_abc.abcC + shaping.m_prePadding;
836 856 }
837 // With justification, we use the justified amounts instead. The 857
838 // justification array contains both the advance and the extra space 858 // With justification, we use the justified amounts instead. The
839 // added for justification, so is the width we want. 859 // justification array contains both the advance and the extra space
840 int justification = 0; 860 // added for justification, so is the width we want.
841 for (size_t i = 0; i < shaping.justify->size(); i++) 861 int justification = 0;
842 justification += shaping.justify[i]; 862 for (size_t i = 0; i < shaping.m_justify.size(); i++)
843 863 justification += shaping.m_justify[i];
844 return shaping.pre_padding + justification; 864
845 } 865 return shaping.m_prePadding + justification;
846 866 }
847 } // namespace gfx 867
848 868 } // namespace WebCore
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698