OLD | NEW |
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 |
OLD | NEW |