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: Source/core/platform/graphics/win/UniscribeHelper.cpp

Issue 99103006: Moving GraphicsContext and dependencies from core to platform. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Final patch - fixes Android Created 7 years 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
OLDNEW
(Empty)
1 /*
2 * Copyright (c) 2006, 2007, 2008, 2009, 2012 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include "config.h"
32 #include "core/platform/graphics/win/UniscribeHelper.h"
33
34 #include "core/platform/graphics/Font.h"
35 #include "core/platform/graphics/GraphicsContext.h"
36 #include "core/platform/graphics/skia/SkiaFontWin.h"
37 #include "platform/fonts/FontFallbackWin.h"
38 #include "platform/win/HWndDC.h"
39 #include "third_party/skia/include/core/SkPoint.h"
40 #include "wtf/Assertions.h"
41
42 namespace WebCore {
43
44 // The function types for ScriptItemizeOpenType() and ScriptShapeOpenType().
45 // We want to use these functions for OpenType feature support, but we can't
46 // call them directly because usp10.dll does not always have them.
47 // Instead, we use GetProcAddress() to check whether we can actually use these
48 // function. If we can't use these functions, we substitute ScriptItemze() and
49 // ScriptShape().
50 typedef HRESULT (WINAPI *ScriptItemizeOpenTypeFunc)(const WCHAR*, int, int,
51 const SCRIPT_CONTROL*,
52 const SCRIPT_STATE*,
53 SCRIPT_ITEM*,
54 OPENTYPE_TAG*, int*);
55 typedef HRESULT (WINAPI *ScriptShapeOpenTypeFunc)(HDC, SCRIPT_CACHE*,
56 SCRIPT_ANALYSIS*,
57 OPENTYPE_TAG, OPENTYPE_TAG,
58 int*, TEXTRANGE_PROPERTIES**,
59 int, const WCHAR*, int, int,
60 WORD*, SCRIPT_CHARPROP*,
61 WORD*, SCRIPT_GLYPHPROP*,
62 int*);
63
64 static ScriptItemizeOpenTypeFunc gScriptItemizeOpenTypeFunc = 0;
65 static ScriptShapeOpenTypeFunc gScriptShapeOpenTypeFunc = 0;
66 static bool gOpenTypeFunctionsLoaded = false;
67
68 static void loadOpenTypeFunctions()
69 {
70 HMODULE hModule = GetModuleHandle(L"usp10");
71 if (hModule) {
72 gScriptItemizeOpenTypeFunc = reinterpret_cast<ScriptItemizeOpenTypeFunc> (GetProcAddress(hModule, "ScriptItemizeOpenType"));
73 gScriptShapeOpenTypeFunc = reinterpret_cast<ScriptShapeOpenTypeFunc>(Get ProcAddress(hModule, "ScriptShapeOpenType"));
74 }
75 if (!gScriptItemizeOpenTypeFunc || !gScriptShapeOpenTypeFunc) {
76 gScriptItemizeOpenTypeFunc = 0;
77 gScriptShapeOpenTypeFunc = 0;
78 }
79 gOpenTypeFunctionsLoaded = true;
80 }
81
82 enum {
83 FontStyleNormal = 0,
84 FontStyleBold = 1,
85 FontStyleItalic = 2,
86 FontStyleUnderlined = 4
87 };
88
89 int getStyleFromLogfont(const LOGFONT* logfont)
90 {
91 // FIXME: consider defining UNDEFINED or INVALID for style and
92 // returning it when logfont is 0
93 if (!logfont) {
94 ASSERT_NOT_REACHED();
95 return FontStyleNormal;
96 }
97 return (logfont->lfItalic ? FontStyleItalic : FontStyleNormal) |
98 (logfont->lfUnderline ? FontStyleUnderlined : FontStyleNormal) |
99 (logfont->lfWeight >= 700 ? FontStyleBold : FontStyleNormal);
100 }
101
102
103 // HFONT is the 'incarnation' of 'everything' about font, but it's an opaque
104 // handle and we can't directly query it to make a new HFONT sharing
105 // its characteristics (height, style, etc) except for family name.
106 // This function uses GetObject to convert HFONT back to LOGFONT,
107 // resets the fields of LOGFONT and calculates style to use later
108 // for the creation of a font identical to HFONT other than family name.
109 static void setLogFontAndStyle(HFONT hfont, LOGFONT *logfont, int *style)
110 {
111 ASSERT(hfont && logfont);
112 if (!hfont || !logfont)
113 return;
114
115 GetObject(hfont, sizeof(LOGFONT), logfont);
116 // We reset these fields to values appropriate for CreateFontIndirect.
117 // while keeping lfHeight, which is the most important value in creating
118 // a new font similar to hfont.
119 logfont->lfWidth = 0;
120 logfont->lfEscapement = 0;
121 logfont->lfOrientation = 0;
122 logfont->lfCharSet = DEFAULT_CHARSET;
123 logfont->lfOutPrecision = OUT_TT_ONLY_PRECIS;
124 logfont->lfQuality = DEFAULT_QUALITY; // Honor user's desktop settings.
125 logfont->lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
126 if (style)
127 *style = getStyleFromLogfont(logfont);
128 }
129
130 // This memory DC will NOT be released but it's OK
131 // since we want to keep it for the whole life span of the process.
132 HDC UniscribeHelper::m_cachedDC = 0;
133
134 static bool canUseGlyphIndex(const SCRIPT_ITEM& run)
135 {
136 // On early version of Uniscribe, ScriptShape() sets run.a.fNoGlyphIndex
137 // to TRUE when it can't shape the run with glyph indexes. This could
138 // occur when we use CFF webfonts(See http://crbug.com/39017).
139 // We don't use the font in that case and try to use fallback fonts.
140 return !run.a.fNoGlyphIndex;
141 }
142
143 UniscribeHelper::UniscribeHelper(const UChar* input,
144 int inputLength,
145 bool isRtl,
146 HFONT hfont,
147 SCRIPT_CACHE* scriptCache,
148 SCRIPT_FONTPROPERTIES* fontProperties,
149 WORD spaceGlyph)
150 : m_input(input)
151 , m_inputLength(inputLength)
152 , m_isRtl(isRtl)
153 , m_hfont(hfont)
154 , m_scriptCache(scriptCache)
155 , m_fontProperties(fontProperties)
156 , m_spaceGlyph(spaceGlyph)
157 , m_directionalOverride(false)
158 , m_inhibitLigate(false)
159 , m_letterSpacing(0)
160 , m_spaceWidth(0)
161 , m_wordSpacing(0)
162 , m_ascent(0)
163 , m_disableFontFallback(false)
164
165 {
166 m_logfont.lfFaceName[0] = 0;
167 if (!gOpenTypeFunctionsLoaded)
168 loadOpenTypeFunctions();
169 }
170
171 UniscribeHelper::~UniscribeHelper()
172 {
173 }
174
175 void UniscribeHelper::initWithOptionalLengthProtection(bool lengthProtection)
176 {
177 // We cap the input length and just don't do anything. We'll allocate a lot
178 // of things of the size of the number of characters, so the allocated
179 // memory will be several times the input length. Plus shaping such a large
180 // buffer may be a form of denial of service. No legitimate text should be
181 // this long. It also appears that Uniscribe flatly rejects very long
182 // strings, so we don't lose anything by doing this.
183 //
184 // The input length protection may be disabled by the unit tests to cause
185 // an error condition.
186 static const int kMaxInputLength = 65535;
187 if (m_inputLength == 0 || (lengthProtection && m_inputLength > kMaxInputLeng th))
188 return;
189
190 fillRuns();
191 fillShapes();
192 fillScreenOrder();
193 }
194
195 int UniscribeHelper::width() const
196 {
197 int width = 0;
198 for (int itemIndex = 0; itemIndex < static_cast<int>(m_runs.size()); itemInd ex++)
199 width += advanceForItem(itemIndex);
200 return width;
201 }
202
203 void UniscribeHelper::justify(int additionalSpace)
204 {
205 // Count the total number of glyphs we have so we know how big to make the
206 // buffers below.
207 int totalGlyphs = 0;
208 for (size_t run = 0; run < m_runs.size(); run++) {
209 int runIndex = m_screenOrder[run];
210 totalGlyphs += static_cast<int>(m_shapes[runIndex].glyphLength());
211 }
212 if (totalGlyphs == 0)
213 return; // Nothing to do.
214
215 // We make one big buffer in screen order of all the glyphs we are drawing
216 // across runs so that the justification function will adjust evenly across
217 // all glyphs.
218 Vector<SCRIPT_VISATTR, 64> visualAttributes;
219 visualAttributes.resize(totalGlyphs);
220 Vector<int, 64> advances;
221 advances.resize(totalGlyphs);
222 Vector<int, 64> justify;
223 justify.resize(totalGlyphs);
224
225 // Build the packed input.
226 int destIndex = 0;
227 for (size_t run = 0; run < m_runs.size(); run++) {
228 int runIndex = m_screenOrder[run];
229 const Shaping& shaping = m_shapes[runIndex];
230
231 for (int i = 0; i < shaping.glyphLength(); i++, destIndex++) {
232 memcpy(&visualAttributes[destIndex], &shaping.m_visualAttributes[i],
233 sizeof(SCRIPT_VISATTR));
234 advances[destIndex] = shaping.m_advance[i];
235 }
236 }
237
238 // The documentation for Scriptjustify is wrong, the parameter is the space
239 // to add and not the width of the column you want.
240 int minKashida;
241 // Disable kashida justification based on
242 // http://blogs.msdn.com/b/michkap/archive/2010/08/31/10056140.aspx.
243 for (int i = 0; i < totalGlyphs; ++i) {
244 if (visualAttributes[i].uJustification == SCRIPT_JUSTIFY_ARABIC_KASHIDA)
245 visualAttributes[i].uJustification = SCRIPT_JUSTIFY_NONE;
246 }
247 minKashida = 0;
248 ScriptJustify(&visualAttributes[0], &advances[0], totalGlyphs,
249 additionalSpace, minKashida, &justify[0]);
250
251 // Now we have to unpack the justification amounts back into the runs so
252 // the glyph indices match.
253 int globalGlyphIndex = 0;
254 for (size_t run = 0; run < m_runs.size(); run++) {
255 int runIndex = m_screenOrder[run];
256 Shaping& shaping = m_shapes[runIndex];
257
258 shaping.m_justify.resize(shaping.glyphLength());
259 for (int i = 0; i < shaping.glyphLength(); i++, globalGlyphIndex++)
260 shaping.m_justify[i] = justify[globalGlyphIndex];
261 }
262 }
263
264 int UniscribeHelper::characterToX(int offset) const
265 {
266 HRESULT hr;
267 ASSERT(offset <= m_inputLength);
268
269 // Our algorithm is to traverse the items in screen order from left to
270 // right, adding in each item's screen width until we find the item with
271 // the requested character in it.
272 int width = 0;
273 for (size_t screenIndex = 0; screenIndex < m_runs.size(); screenIndex++) {
274 // Compute the length of this run.
275 int itemIndex = m_screenOrder[screenIndex];
276 const SCRIPT_ITEM& item = m_runs[itemIndex];
277 const Shaping& shaping = m_shapes[itemIndex];
278 int itemLength = shaping.charLength();
279
280 if (offset >= item.iCharPos && offset <= item.iCharPos + itemLength) {
281 // Character offset is in this run.
282 int charLength = offset - item.iCharPos;
283
284 int curX = 0;
285 hr = ScriptCPtoX(charLength, FALSE, itemLength,
286 shaping.glyphLength(),
287 &shaping.m_logs[0], &shaping.m_visualAttributes[0],
288 shaping.effectiveAdvances(), &item.a, &curX);
289 if (FAILED(hr))
290 return 0;
291
292 width += curX + shaping.m_prePadding;
293 ASSERT(width >= 0);
294 return width;
295 }
296
297 // Move to the next item.
298 width += advanceForItem(itemIndex);
299 }
300 ASSERT(width >= 0);
301 return width;
302 }
303
304 int UniscribeHelper::xToCharacter(int x) const
305 {
306 // We iterate in screen order until we find the item with the given pixel
307 // position in it. When we find that guy, we ask Uniscribe for the
308 // character index.
309 HRESULT hr;
310 for (size_t screenIndex = 0; screenIndex < m_runs.size(); screenIndex++) {
311 int itemIndex = m_screenOrder[screenIndex];
312 int itemAdvance = advanceForItem(itemIndex);
313
314 // Note that the run may be empty if shaping failed, so we want to skip
315 // over it.
316 const Shaping& shaping = m_shapes[itemIndex];
317 int itemLength = shaping.charLength();
318 if (x <= itemAdvance && itemLength > 0) {
319 // The requested offset is within this item.
320 const SCRIPT_ITEM& item = m_runs[itemIndex];
321
322 // Account for the leading space we've added to this run that
323 // Uniscribe doesn't know about.
324 x -= shaping.m_prePadding;
325
326 int charX = 0;
327 int trailing;
328 hr = ScriptXtoCP(x, itemLength, shaping.glyphLength(),
329 &shaping.m_logs[0], &shaping.m_visualAttributes[0],
330 shaping.effectiveAdvances(), &item.a, &charX,
331 &trailing);
332
333 // The character offset is within the item. We need to add the
334 // item's offset to transform it into the space of the TextRun
335 return charX + item.iCharPos;
336 }
337
338 // The offset is beyond this item, account for its length and move on.
339 x -= itemAdvance;
340 }
341
342 // Error condition, we don't know what to do if we don't have that X
343 // position in any of our items.
344 return 0;
345 }
346
347 void UniscribeHelper::draw(GraphicsContext* graphicsContext,
348 const FontPlatformData& fontPlatformData, HDC dc, int x, int y,
349 const FloatRect& textRect, int from, int to)
350 {
351 HGDIOBJ oldFont = 0;
352 int curX = x;
353 bool firstRun = true;
354
355 for (size_t screenIndex = 0; screenIndex < m_runs.size(); screenIndex++) {
356 int itemIndex = m_screenOrder[screenIndex];
357 const SCRIPT_ITEM& item = m_runs[itemIndex];
358 const Shaping& shaping = m_shapes[itemIndex];
359
360 // Character offsets within this run. THESE MAY NOT BE IN RANGE and may
361 // be negative, etc. The code below handles this.
362 int fromChar = from - item.iCharPos;
363 int toChar = to - item.iCharPos;
364
365 // See if we need to draw any characters in this item.
366 if (shaping.charLength() == 0 ||
367 fromChar >= shaping.charLength() || toChar <= 0) {
368 // No chars in this item to display.
369 curX += advanceForItem(itemIndex);
370 continue;
371 }
372
373 // Compute the starting glyph within this span. |from| and |to| are
374 // global offsets that may intersect arbitrarily with our local run.
375 int fromGlyph, afterGlyph;
376 if (item.a.fRTL) {
377 // To compute the first glyph when going RTL, we use |to|.
378 if (toChar >= shaping.charLength())
379 // The end of the text is after (to the left) of us.
380 fromGlyph = 0;
381 else {
382 // Since |to| is exclusive, the first character we draw on the
383 // left is actually the one right before (to the right) of
384 // |to|.
385 fromGlyph = shaping.m_logs[toChar - 1];
386 }
387
388 // The last glyph is actually the first character in the range.
389 if (fromChar <= 0) {
390 // The first character to draw is before (to the right) of this
391 // span, so draw all the way to the end.
392 afterGlyph = shaping.glyphLength();
393 } else {
394 // We want to draw everything up until the character to the
395 // right of |from|. To the right is - 1, so we look that up
396 // (remember our character could be more than one glyph, so we
397 // can't look up our glyph and add one).
398 afterGlyph = shaping.m_logs[fromChar - 1];
399 }
400 } else {
401 // Easy case, everybody agrees about directions. We only need to
402 // handle boundary conditions to get a range inclusive at the
403 // beginning, and exclusive at the ending. We have to do some
404 // computation to see the glyph one past the end.
405 fromGlyph = shaping.m_logs[fromChar < 0 ? 0 : fromChar];
406 if (toChar >= shaping.charLength())
407 afterGlyph = shaping.glyphLength();
408 else
409 afterGlyph = shaping.m_logs[toChar];
410 }
411
412 // Account for the characters that were skipped in this run. When
413 // WebKit asks us to draw a subset of the run, it actually tells us
414 // to draw at the X offset of the beginning of the run, since it
415 // doesn't know the internal position of any of our characters.
416 const int* effectiveAdvances = shaping.effectiveAdvances();
417 int innerOffset = 0;
418 for (int i = 0; i < fromGlyph; i++)
419 innerOffset += effectiveAdvances[i];
420
421 // Actually draw the glyphs we found.
422 int glyphCount = afterGlyph - fromGlyph;
423 if (fromGlyph >= 0 && glyphCount > 0) {
424 // Account for the preceding space we need to add to this run. We
425 // don't need to count for the following space because that will be
426 // counted in advanceForItem below when we move to the next run.
427 innerOffset += shaping.m_prePadding;
428
429 // Pass 0 in when there is no justification.
430 const int* justify = shaping.m_justify.size() == 0 ? 0 : &shaping.m_ justify[fromGlyph];
431
432 const int* advances = shaping.m_justify.size() ?
433 &shaping.m_justify[fromGlyph]
434 : &shaping.m_advance[fromGlyph];
435
436 // Fonts with different ascents can be used to render different
437 // runs. 'Across-runs' y-coordinate correction needs to be
438 // adjusted for each font.
439 bool textOutOk = false;
440 for (int executions = 0; executions < 2; ++executions) {
441 SkPoint origin;
442 origin.fX = curX + + innerOffset;
443 origin.fY = y + m_ascent;
444 paintSkiaText(graphicsContext,
445 fontPlatformData,
446 shaping.m_hfont,
447 glyphCount,
448 &shaping.m_glyphs[fromGlyph],
449 advances,
450 &shaping.m_offsets[fromGlyph],
451 origin,
452 textRect);
453 textOutOk = true;
454
455 if (!textOutOk && 0 == executions) {
456 // If TextOut is called from the renderer it might fail
457 // because the sandbox is preventing it from opening the
458 // font files. If we are running in the renderer,
459 // TryToPreloadFont is overridden to ask the browser to
460 // preload the font for us so we can access it.
461 tryToPreloadFont(shaping.m_hfont);
462 continue;
463 }
464 break;
465 }
466 }
467
468 curX += advanceForItem(itemIndex);
469 }
470
471 if (oldFont)
472 SelectObject(dc, oldFont);
473 }
474
475 WORD UniscribeHelper::firstGlyphForCharacter(int charOffset) const
476 {
477 // Find the run for the given character.
478 for (int i = 0; i < static_cast<int>(m_runs.size()); i++) {
479 int firstChar = m_runs[i].iCharPos;
480 const Shaping& shaping = m_shapes[i];
481 int localOffset = charOffset - firstChar;
482 if (localOffset >= 0 && localOffset < shaping.charLength()) {
483 // The character is in this run, return the first glyph for it
484 // (should generally be the only glyph). It seems Uniscribe gives
485 // glyph 0 for empty, which is what we want to return in the
486 // "missing" case.
487 size_t glyphIndex = shaping.m_logs[localOffset];
488 if (glyphIndex >= shaping.m_glyphs.size()) {
489 // The glyph should be in this run, but the run has too few
490 // actual characters. This can happen when shaping the run
491 // fails, in which case, we should have no data in the logs at
492 // all.
493 ASSERT(shaping.m_glyphs.size() == 0);
494 return 0;
495 }
496 return shaping.m_glyphs[glyphIndex];
497 }
498 }
499
500 return 0;
501 }
502
503 void UniscribeHelper::fillRuns()
504 {
505 HRESULT hr;
506 m_runs.resize(cUniscribeHelperStackRuns);
507 m_scriptTags.resize(cUniscribeHelperStackRuns);
508
509 SCRIPT_STATE inputState;
510 inputState.uBidiLevel = m_isRtl;
511 inputState.fOverrideDirection = m_directionalOverride;
512 inputState.fInhibitSymSwap = false;
513 inputState.fCharShape = false; // Not implemented in Uniscribe
514 inputState.fDigitSubstitute = false; // Do we want this for Arabic?
515 inputState.fInhibitLigate = m_inhibitLigate;
516 inputState.fDisplayZWG = false; // Don't draw control characters.
517 inputState.fArabicNumContext = m_isRtl; // Do we want this for Arabic?
518 inputState.fGcpClusters = false;
519 inputState.fReserved = 0;
520 inputState.fEngineReserved = 0;
521 // The psControl argument to ScriptItemize should be non-0 for RTL text,
522 // per http://msdn.microsoft.com/en-us/library/ms776532.aspx . So use a
523 // SCRIPT_CONTROL that is set to all zeros. Zero as a locale ID means the
524 // neutral locale per http://msdn.microsoft.com/en-us/library/ms776294.aspx
525 static SCRIPT_CONTROL inputControl = {0, // uDefaultLanguage :16;
526 0, // fContextDigits :1;
527 0, // fInvertPreBoundDir :1;
528 0, // fInvertPostBoundDir :1;
529 0, // fLinkStringBefore :1;
530 0, // fLinkStringAfter :1;
531 0, // fNeutralOverride :1;
532 0, // fNumericOverride :1;
533 0, // fLegacyBidiClass :1;
534 0, // fMergeNeutralItems :1;
535 0};// fReserved :7;
536 // Calling ScriptApplyDigitSubstitution( 0, &inputControl, &inputState)
537 // here would be appropriate if we wanted to set the language ID, and get
538 // local digit substitution behavior. For now, don't do it.
539
540 while (true) {
541 int numberOfItems = 0;
542
543 // Ideally, we would have a way to know the runs before and after this
544 // one, and put them into the control parameter of ScriptItemize. This
545 // would allow us to shape characters properly that cross style
546 // boundaries (WebKit bug 6148).
547 //
548 // We tell ScriptItemize that the output list of items is one smaller
549 // than it actually is. According to Mozilla bug 366643, if there is
550 // not enough room in the array on pre-SP2 systems, ScriptItemize will
551 // write one past the end of the buffer.
552 //
553 // ScriptItemize is very strange. It will often require a much larger
554 // ITEM buffer internally than it will give us as output. For example,
555 // it will say a 16-item buffer is not big enough, and will write
556 // interesting numbers into all those items. But when we give it a 32
557 // item buffer and it succeeds, it only has one item output.
558 //
559 // It seems to be doing at least two passes, the first where it puts a
560 // lot of intermediate data into our items, and the second where it
561 // collates them.
562 if (gScriptItemizeOpenTypeFunc) {
563 hr = gScriptItemizeOpenTypeFunc(m_input, m_inputLength,
564 static_cast<int>(m_runs.size()) - 1,
565 &inputControl, &inputState,
566 &m_runs[0], &m_scriptTags[0],
567 &numberOfItems);
568
569 if (SUCCEEDED(hr)) {
570 // Pack consecutive runs, the script tag of which are
571 // SCRIPT_TAG_UNKNOWN, to reduce the number of runs.
572 for (int i = 0; i < numberOfItems; ++i) {
573 // Do not pack with whitespace characters at the head.
574 // Otherwise whole the run is rendered as a whitespace.
575 WCHAR ch = m_input[m_runs[i].iCharPos];
576 if (m_scriptTags[i] == SCRIPT_TAG_UNKNOWN && !Font::treatAsS pace(ch) && !Font::treatAsZeroWidthSpace(ch)) {
577 int j = 1;
578 while (i + j < numberOfItems && m_scriptTags[i + j] == S CRIPT_TAG_UNKNOWN)
579 ++j;
580 if (--j) {
581 m_runs.remove(i + 1, j);
582 m_scriptTags.remove(i + 1, j);
583 numberOfItems -= j;
584 }
585 }
586 }
587 m_scriptTags.resize(numberOfItems);
588 }
589 } else {
590 hr = ScriptItemize(m_input, m_inputLength,
591 static_cast<int>(m_runs.size()) - 1,
592 &inputControl, &inputState, &m_runs[0],
593 &numberOfItems);
594 }
595 if (SUCCEEDED(hr)) {
596 m_runs.resize(numberOfItems);
597 break;
598 }
599 if (hr != E_OUTOFMEMORY) {
600 // Some kind of unexpected error.
601 m_runs.resize(0);
602 break;
603 }
604 // There was not enough items for it to write into, expand.
605 m_runs.resize(m_runs.size() * 2);
606 m_scriptTags.resize(m_runs.size());
607 }
608 }
609
610 const int kUndefinedAscent = std::numeric_limits<int>::min();
611
612 // Given an HFONT, return the ascent. If GetTextMetrics fails,
613 // kUndefinedAscent is returned, instead.
614 int getAscent(HFONT hfont)
615 {
616 HWndDC dc(0);
617 HGDIOBJ oldFont = SelectObject(dc, hfont);
618 TEXTMETRIC tm;
619 BOOL gotMetrics = GetTextMetrics(dc, &tm);
620 SelectObject(dc, oldFont);
621 return gotMetrics ? tm.tmAscent : kUndefinedAscent;
622 }
623
624 const WORD kUnsupportedGlyph = 0xffff;
625
626 WORD getSpaceGlyph(HFONT hfont)
627 {
628 HWndDC dc(0);
629 HGDIOBJ oldFont = SelectObject(dc, hfont);
630 WCHAR space = L' ';
631 WORD spaceGlyph = kUnsupportedGlyph;
632 GetGlyphIndices(dc, &space, 1, &spaceGlyph, GGI_MARK_NONEXISTING_GLYPHS);
633 SelectObject(dc, oldFont);
634 return spaceGlyph;
635 }
636
637 struct ShaperFontData {
638 ShaperFontData()
639 : hfont(0)
640 , ascent(kUndefinedAscent)
641 , scriptCache(0)
642 , spaceGlyph(0)
643 {
644 }
645
646 HFONT hfont;
647 int ascent;
648 mutable SCRIPT_CACHE scriptCache;
649 WORD spaceGlyph;
650 };
651
652 // Again, using hash_map does not earn us much here. page_cycler_test intl2
653 // gave us a 'better' result with map than with hash_map even though they're
654 // well-within 1-sigma of each other so that the difference is not significant.
655 // On the other hand, some pages in intl2 seem to take longer to load with map
656 // in the 1st pass. Need to experiment further.
657 typedef HashMap<String, ShaperFontData> ShaperFontDataCache;
658
659 // Derive a new HFONT by replacing lfFaceName of LOGFONT with |family|,
660 // calculate the ascent for the derived HFONT, and initialize SCRIPT_CACHE
661 // in ShaperFontData.
662 // |style| is only used for cache key generation. |style| is
663 // bit-wise OR of BOLD(1), UNDERLINED(2) and ITALIC(4) and
664 // should match what's contained in LOGFONT. It should be calculated
665 // by calling GetStyleFromLogFont.
666 // Returns false if the font is not accessible, in which case |ascent| field
667 // of |ShaperFontData| is set to kUndefinedAscent.
668 // Be aware that this is not thread-safe.
669 // FIXME: Instead of having three out params, we'd better have one
670 // (|*ShaperFontData|), but somehow it mysteriously messes up the layout for
671 // certain complex script pages (e.g. hi.wikipedia.org) and also crashes
672 // at the start-up if recently visited page list includes pages with complex
673 // scripts in their title. Moreover, somehow the very first-pass of
674 // intl2 page-cycler test is noticeably slower with one out param than
675 // the current version although the subsequent 9 passes take about the
676 // same time.
677 // Be aware that this is not thread-safe.
678 static bool getDerivedFontData(const UChar* family, int style, LOGFONT* logfont,
679 int* ascent, HFONT* hfont, SCRIPT_CACHE** scriptCache, WORD* spaceGlyph)
680 {
681 ASSERT(logfont);
682 ASSERT(family);
683 ASSERT(*family);
684
685 // It does not matter that we leak font data when we exit.
686 static ShaperFontDataCache* gFontDataCache = 0;
687 if (!gFontDataCache)
688 gFontDataCache = new ShaperFontDataCache();
689
690 // FIXME: This comes up pretty high in the profile so that
691 // we need to measure whether using SHA256 (after coercing all the
692 // fields to char*) is faster than String::format.
693 String fontKey = String::format("%1d:%d:%ls", style, logfont->lfHeight, fami ly);
694 ShaperFontDataCache::iterator iter = gFontDataCache->find(fontKey);
695 ShaperFontData* derived;
696 if (iter == gFontDataCache->end()) {
697 ASSERT(wcslen(family) < LF_FACESIZE);
698 wcscpy_s(logfont->lfFaceName, LF_FACESIZE, family);
699 // FIXME: CreateFontIndirect always comes up with
700 // a font even if there's no font matching the name. Need to
701 // check it against what we actually want (as is done in
702 // FontCacheWin.cpp)
703 ShaperFontDataCache::AddResult entry = gFontDataCache->add(fontKey, Shap erFontData());
704 derived = &entry.iterator->value;
705 derived->hfont = CreateFontIndirect(logfont);
706 // GetAscent may return kUndefinedAscent, but we still want to
707 // cache it so that we won't have to call CreateFontIndirect once
708 // more for HFONT next time.
709 derived->ascent = getAscent(derived->hfont);
710 derived->spaceGlyph = getSpaceGlyph(derived->hfont);
711 } else {
712 derived = &iter->value;
713 // Last time, getAscent or getSpaceGlyph failed so that only HFONT was
714 // cached. Try once more assuming that TryPreloadFont
715 // was called by a caller between calls.
716 if (kUndefinedAscent == derived->ascent)
717 derived->ascent = getAscent(derived->hfont);
718 if (kUnsupportedGlyph == derived->spaceGlyph)
719 derived->spaceGlyph = getSpaceGlyph(derived->hfont);
720 }
721 *hfont = derived->hfont;
722 *ascent = derived->ascent;
723 *scriptCache = &(derived->scriptCache);
724 *spaceGlyph = derived->spaceGlyph;
725 return *ascent != kUndefinedAscent && *spaceGlyph != kUnsupportedGlyph;
726 }
727
728 bool UniscribeHelper::shape(const UChar* input,
729 int itemLength,
730 int numGlyphs,
731 SCRIPT_ITEM& run,
732 OPENTYPE_TAG scriptTag,
733 Shaping& shaping)
734 {
735 HFONT hfont = m_hfont;
736 SCRIPT_CACHE* scriptCache = m_scriptCache;
737 SCRIPT_FONTPROPERTIES* fontProperties = m_fontProperties;
738 Vector<SCRIPT_CHARPROP, cUniscribeHelperStackChars> charProps;
739 Vector<SCRIPT_GLYPHPROP, cUniscribeHelperStackChars> glyphProps;
740 int ascent = m_ascent;
741 WORD spaceGlyph = m_spaceGlyph;
742 HRESULT hr;
743 // When used to fill up glyph pages for simple scripts in non-BMP,
744 // we don't want any font fallback in this class. The simple script
745 // font path can take care of font fallback.
746 bool lastFallbackTried = m_disableFontFallback;
747 bool result;
748
749 int generatedGlyphs = 0;
750
751 // In case HFONT passed in ctor cannot render this run, we have to scan
752 // other fonts from the beginning of the font list.
753 resetFontIndex();
754
755 // Compute shapes.
756 while (true) {
757 shaping.m_logs.resize(itemLength);
758 shaping.m_glyphs.resize(numGlyphs);
759 shaping.m_visualAttributes.resize(numGlyphs);
760 charProps.resize(itemLength);
761 glyphProps.resize(numGlyphs);
762 run.a.fNoGlyphIndex = FALSE;
763
764 #ifdef PURIFY
765 // http://code.google.com/p/chromium/issues/detail?id=5309
766 // Purify isn't able to track the assignments that ScriptShape makes to
767 // shaping.m_glyphs. Consequently, any bytes with value 0xCD that it
768 // writes, will be considered un-initialized data.
769 //
770 // This hack avoid the false-positive UMRs by marking the buffer as
771 // initialized.
772 //
773 // FIXME: A better solution would be to use Purify's API and mark only
774 // the populated range as initialized:
775 //
776 // PurifyMarkAsInitialized(
777 // &shaping.m_glyphs[0],
778 // sizeof(shaping.m_glyphs[0] * generatedGlyphs);
779
780 ZeroMemory(&shaping.m_glyphs[0],
781 sizeof(shaping.m_glyphs[0]) * shaping.m_glyphs.size());
782 #endif
783 // If our DC is already created, select the font in it so we can use it now.
784 // Otherwise, we'll create it as needed afterward...
785 if (m_cachedDC)
786 SelectObject(m_cachedDC, hfont);
787
788 // Firefox sets SCRIPT_ANALYSIS.SCRIPT_STATE.fDisplayZWG to true
789 // here. Is that what we want? It will display control characters.
790 if (gScriptShapeOpenTypeFunc) {
791 TEXTRANGE_PROPERTIES* rangeProps = m_featureRecords.size() ? &m_rang eProperties : 0;
792 hr = gScriptShapeOpenTypeFunc(m_cachedDC, scriptCache, &run.a,
793 scriptTag, 0, &itemLength,
794 &rangeProps, rangeProps ? 1 : 0,
795 input, itemLength, numGlyphs,
796 &shaping.m_logs[0], &charProps[0],
797 &shaping.m_glyphs[0], &glyphProps[0],
798 &generatedGlyphs);
799 if (SUCCEEDED(hr)) {
800 // If we use ScriptShapeOpenType(), visual attributes
801 // information for each characters are stored in
802 // |glyphProps[i].sva|.
803 for (int i = 0; i < generatedGlyphs; ++i)
804 memcpy(&shaping.m_visualAttributes[i], &glyphProps[i].sva, s izeof(SCRIPT_VISATTR));
805 }
806 } else {
807 hr = ScriptShape(m_cachedDC, scriptCache, input, itemLength,
808 numGlyphs, &run.a,
809 &shaping.m_glyphs[0], &shaping.m_logs[0],
810 &shaping.m_visualAttributes[0], &generatedGlyphs);
811 }
812 // We receive E_PENDING when we need to try again with a Drawing Context ,
813 // but we don't want to retry again if we already tried with non-zero DC .
814 if (hr == E_PENDING && !m_cachedDC) {
815 EnsureCachedDCCreated();
816 continue;
817 }
818 if (hr == E_OUTOFMEMORY) {
819 numGlyphs *= 2;
820 continue;
821 }
822 if (SUCCEEDED(hr) && (lastFallbackTried || !containsMissingGlyphs(shapin g, run, fontProperties) && canUseGlyphIndex(run)))
823 break;
824
825 // The current font can't render this run, try next font.
826 if (!m_disableFontFallback &&
827 nextWinFontData(hfont, scriptCache, fontProperties, ascent, spaceGly ph)) {
828 // The primary font does not support this run. Try next font.
829 // In case of web page rendering, they come from fonts specified in
830 // CSS stylesheets.
831 continue;
832 } else if (!lastFallbackTried) {
833 lastFallbackTried = true;
834
835 // Generate a last fallback font based on the script of
836 // a character to draw while inheriting size and styles
837 // from the primary font
838 if (!m_logfont.lfFaceName[0])
839 setLogFontAndStyle(m_hfont, &m_logfont, &m_style);
840
841 // TODO(jungshik): generic type should come from webkit for
842 // UniscribeHelperTextRun (a derived class used in webkit).
843 const UChar *family = getFallbackFamilyForFirstNonCommonCharacter(in put, itemLength,
844 FontDescription::StandardFamily);
845 bool fontOk = getDerivedFontData(family, m_style, &m_logfont,
846 &ascent, &hfont, &scriptCache,
847 &spaceGlyph);
848
849
850 if (!fontOk) {
851 // If this GetDerivedFontData is called from the renderer it
852 // might fail because the sandbox is preventing it from opening
853 // the font files. If we are running in the renderer,
854 // TryToPreloadFont is overridden to ask the browser to preload
855 // the font for us so we can access it.
856 tryToPreloadFont(hfont);
857
858 // Try again.
859 fontOk = getDerivedFontData(family, m_style, &m_logfont,
860 &ascent, &hfont, &scriptCache,
861 &spaceGlyph);
862 ASSERT(fontOk);
863 }
864
865 // TODO(jungshik) : Currently GetDerivedHFont always returns a
866 // a valid HFONT, but in the future, I may change it to return 0.
867 ASSERT(hfont);
868
869 // We don't need a font_properties for the last resort fallback font
870 // because we don't have anything more to try and are forced to
871 // accept empty glyph boxes. If we tried a series of fonts as
872 // 'last-resort fallback', we'd need it, but currently, we don't.
873 continue;
874 } else if (hr == USP_E_SCRIPT_NOT_IN_FONT) {
875 run.a.eScript = SCRIPT_UNDEFINED;
876 continue;
877 } else if (FAILED(hr)) {
878 // Error shaping.
879 generatedGlyphs = 0;
880 result = false;
881 goto cleanup;
882 }
883 }
884
885 // Sets Windows font data for this run to those corresponding to
886 // a font supporting this run. we don't need to store font_properties
887 // because it's not used elsewhere.
888 shaping.m_hfont = hfont;
889 shaping.m_scriptCache = scriptCache;
890 shaping.m_spaceGlyph = spaceGlyph;
891
892 // The ascent of a font for this run can be different from
893 // that of the primary font so that we need to keep track of
894 // the difference per run and take that into account when calling
895 // ScriptTextOut in |draw|. Otherwise, different runs rendered by
896 // different fonts would not be aligned vertically.
897 shaping.m_ascentOffset = m_ascent ? ascent - m_ascent : 0;
898 result = true;
899
900 cleanup:
901 shaping.m_glyphs.resize(generatedGlyphs);
902 shaping.m_visualAttributes.resize(generatedGlyphs);
903 shaping.m_advance.resize(generatedGlyphs);
904 shaping.m_offsets.resize(generatedGlyphs);
905
906 // On failure, our logs don't mean anything, so zero those out.
907 if (!result)
908 shaping.m_logs.clear();
909
910 return result;
911 }
912
913 void UniscribeHelper::EnsureCachedDCCreated()
914 {
915 if (m_cachedDC)
916 return;
917 // Allocate a memory DC that is compatible with the Desktop DC since we don' t have any window,
918 // and we don't want to use the Desktop DC directly since it can have nasty side effects
919 // as identified in Chrome Issue http://crbug.com/59315.
920 HWndDC screenDC(0);
921 m_cachedDC = ::CreateCompatibleDC(screenDC);
922 ASSERT(m_cachedDC);
923 }
924
925 void UniscribeHelper::fillShapes()
926 {
927 m_shapes.resize(m_runs.size());
928 for (size_t i = 0; i < m_runs.size(); i++) {
929 int startItem = m_runs[i].iCharPos;
930 int itemLength = m_inputLength - startItem;
931 if (i < m_runs.size() - 1)
932 itemLength = m_runs[i + 1].iCharPos - startItem;
933
934 int numGlyphs;
935 if (itemLength < cUniscribeHelperStackChars) {
936 // We'll start our buffer sizes with the current stack space
937 // available in our buffers if the current input fits. As long as
938 // it doesn't expand past that we'll save a lot of time mallocing.
939 numGlyphs = cUniscribeHelperStackChars;
940 } else {
941 // When the input doesn't fit, give up with the stack since it will
942 // almost surely not be enough room (unless the input actually
943 // shrinks, which is unlikely) and just start with the length
944 // recommended by the Uniscribe documentation as a "usually fits"
945 // size.
946 numGlyphs = itemLength * 3 / 2 + 16;
947 }
948
949 // Convert a string to a glyph string trying the primary font, fonts in
950 // the fallback list and then script-specific last resort font.
951 Shaping& shaping = m_shapes[i];
952 if (!shape(&m_input[startItem], itemLength, numGlyphs, m_runs[i], m_scri ptTags[i], shaping))
953 continue;
954
955 // At the moment, the only time m_disableFontFallback is set is
956 // when we look up glyph indices for non-BMP code ranges. So,
957 // we can skip the glyph placement. When that becomes not the case
958 // any more, we have to add a new flag to control glyph placement.
959 if (m_disableFontFallback)
960 continue;
961
962 // Compute placements. Note that offsets is documented incorrectly
963 // and is actually an array.
964 EnsureCachedDCCreated();
965 SelectObject(m_cachedDC, shaping.m_hfont);
966 shaping.m_prePadding = 0;
967 if (FAILED(ScriptPlace(m_cachedDC, shaping.m_scriptCache,
968 &shaping.m_glyphs[0],
969 static_cast<int>(shaping.m_glyphs.size()),
970 &shaping.m_visualAttributes[0], &m_runs[i].a,
971 &shaping.m_advance[0], &shaping.m_offsets[0],
972 &shaping.m_abc))) {
973 // Some error we don't know how to handle. Nuke all of our data
974 // since we can't deal with partially valid data later.
975 m_runs.clear();
976 m_scriptTags.clear();
977 m_shapes.clear();
978 m_screenOrder.clear();
979 }
980 }
981
982 adjustSpaceAdvances();
983
984 if (m_letterSpacing != 0 || m_wordSpacing != 0)
985 applySpacing();
986 }
987
988 void UniscribeHelper::fillScreenOrder()
989 {
990 m_screenOrder.resize(m_runs.size());
991
992 // We assume that the input has only one text direction in it.
993 // TODO(brettw) are we sure we want to keep this restriction?
994 if (m_isRtl) {
995 for (int i = 0; i < static_cast<int>(m_screenOrder.size()); i++)
996 m_screenOrder[static_cast<int>(m_screenOrder.size()) - i - 1] = i;
997 } else {
998 for (int i = 0; i < static_cast<int>(m_screenOrder.size()); i++)
999 m_screenOrder[i] = i;
1000 }
1001 }
1002
1003 void UniscribeHelper::adjustSpaceAdvances()
1004 {
1005 if (m_spaceWidth == 0)
1006 return;
1007
1008 int spaceWidthWithoutLetterSpacing = m_spaceWidth - m_letterSpacing;
1009
1010 // This mostly matches what WebKit's UniscribeController::shapeAndPlaceItem.
1011 for (size_t run = 0; run < m_runs.size(); run++) {
1012 Shaping& shaping = m_shapes[run];
1013
1014 // FIXME: This loop is not UTF-16-safe. Unicode 6.0 has a couple
1015 // of complex script blocks in Plane 1.
1016 for (int i = 0; i < shaping.charLength(); i++) {
1017 UChar c = m_input[m_runs[run].iCharPos + i];
1018 bool treatAsSpace = Font::treatAsSpace(c);
1019 if (!treatAsSpace && !Font::treatAsZeroWidthSpaceInComplexScript(c))
1020 continue;
1021
1022 int glyphIndex = shaping.m_logs[i];
1023 int currentAdvance = shaping.m_advance[glyphIndex];
1024
1025 shaping.m_glyphs[glyphIndex] = shaping.m_spaceGlyph;
1026
1027 if (treatAsSpace) {
1028 // currentAdvance does not include additional letter-spacing,
1029 // but m_spaceWidth does. Here we find out how off we are from
1030 // the correct width (spaceWidthWithoutLetterSpacing) and
1031 // just subtract that diff.
1032 int diff = currentAdvance - spaceWidthWithoutLetterSpacing;
1033 // The shaping can consist of a run of text, so only subtract
1034 // the difference in the width of the glyph.
1035 shaping.m_advance[glyphIndex] -= diff;
1036 shaping.m_abc.abcB -= diff;
1037 continue;
1038 }
1039
1040 // For characters treated as zero-width space in complex
1041 // scripts, set the advance width to zero, adjust
1042 // |abcB| of the current run accordingly and set
1043 // the glyph to m_spaceGlyph (invisible).
1044 shaping.m_advance[glyphIndex] = 0;
1045 shaping.m_abc.abcB -= currentAdvance;
1046 shaping.m_offsets[glyphIndex].du = 0;
1047 shaping.m_offsets[glyphIndex].dv = 0;
1048 }
1049 }
1050 }
1051
1052 void UniscribeHelper::applySpacing()
1053 {
1054 for (size_t run = 0; run < m_runs.size(); run++) {
1055 Shaping& shaping = m_shapes[run];
1056 bool isRtl = m_runs[run].a.fRTL;
1057
1058 if (m_letterSpacing != 0) {
1059 // RTL text gets padded to the left of each character. We increment
1060 // the run's advance to make this happen. This will be balanced out
1061 // by NOT adding additional advance to the last glyph in the run.
1062 if (isRtl)
1063 shaping.m_prePadding += m_letterSpacing;
1064
1065 // Go through all the glyphs in this run and increase the "advance"
1066 // to account for letter spacing. We adjust letter spacing only on
1067 // cluster boundaries.
1068 //
1069 // This works for most scripts, but may have problems with some
1070 // indic scripts. This behavior is better than Firefox or IE for
1071 // Hebrew.
1072 for (int i = 0; i < shaping.glyphLength(); i++) {
1073 if (shaping.m_visualAttributes[i].fClusterStart) {
1074 // Ick, we need to assign the extra space so that the glyph
1075 // comes first, then is followed by the space. This is
1076 // opposite for RTL.
1077 if (isRtl) {
1078 if (i != shaping.glyphLength() - 1) {
1079 // All but the last character just get the spacing
1080 // applied to their advance. The last character
1081 // doesn't get anything,
1082 shaping.m_advance[i] += m_letterSpacing;
1083 shaping.m_abc.abcB += m_letterSpacing;
1084 }
1085 } else {
1086 // LTR case is easier, we just add to the advance.
1087 shaping.m_advance[i] += m_letterSpacing;
1088 shaping.m_abc.abcB += m_letterSpacing;
1089 }
1090 }
1091 }
1092 }
1093
1094 // Go through all the characters to find whitespace and insert the
1095 // extra wordspacing amount for the glyphs they correspond to.
1096 if (m_wordSpacing != 0) {
1097 for (int i = 0; i < shaping.charLength(); i++) {
1098 if (!Font::treatAsSpace(m_input[m_runs[run].iCharPos + i]))
1099 continue;
1100
1101 // The char in question is a word separator...
1102 int glyphIndex = shaping.m_logs[i];
1103
1104 // Spaces will not have a glyph in Uniscribe, it will just add
1105 // additional advance to the character to the left of the
1106 // space. The space's corresponding glyph will be the character
1107 // following it in reading order.
1108 if (isRtl) {
1109 // In RTL, the glyph to the left of the space is the same
1110 // as the first glyph of the following character, so we can
1111 // just increment it.
1112 shaping.m_advance[glyphIndex] += m_wordSpacing;
1113 shaping.m_abc.abcB += m_wordSpacing;
1114 } else {
1115 // LTR is actually more complex here, we apply it to the
1116 // previous character if there is one, otherwise we have to
1117 // apply it to the leading space of the run.
1118 if (glyphIndex == 0)
1119 shaping.m_prePadding += m_wordSpacing;
1120 else {
1121 shaping.m_advance[glyphIndex - 1] += m_wordSpacing;
1122 shaping.m_abc.abcB += m_wordSpacing;
1123 }
1124 }
1125 }
1126 } // m_wordSpacing != 0
1127
1128 // Loop for next run...
1129 }
1130 }
1131
1132 // The advance is the ABC width of the run
1133 int UniscribeHelper::advanceForItem(int itemIndex) const
1134 {
1135 int accum = 0;
1136 const Shaping& shaping = m_shapes[itemIndex];
1137
1138 if (shaping.m_justify.size() == 0) {
1139 // Easy case with no justification, the width is just the ABC width of
1140 // the run. (The ABC width is the sum of the advances).
1141 return shaping.m_abc.abcA + shaping.m_abc.abcB +
1142 shaping.m_abc.abcC + shaping.m_prePadding;
1143 }
1144
1145 // With justification, we use the justified amounts instead. The
1146 // justification array contains both the advance and the extra space
1147 // added for justification, so is the width we want.
1148 int justification = 0;
1149 for (size_t i = 0; i < shaping.m_justify.size(); i++)
1150 justification += shaping.m_justify[i];
1151
1152 return shaping.m_prePadding + justification;
1153 }
1154
1155 // SCRIPT_FONTPROPERTIES contains glyph indices for default, invalid
1156 // and blank glyphs. Just because ScriptShape succeeds does not mean
1157 // that a text run is rendered correctly. Some characters may be rendered
1158 // with default/invalid/blank glyphs. Therefore, we need to check if the glyph
1159 // array returned by ScriptShape contains any of those glyphs to make
1160 // sure that the text run is rendered successfully.
1161 // However, we should not subject zero-width characters to this test.
1162
1163 bool UniscribeHelper::containsMissingGlyphs(const Shaping& shaping,
1164 const SCRIPT_ITEM& run,
1165 const SCRIPT_FONTPROPERTIES* propert ies) const
1166 {
1167 for (int i = 0; i < shaping.charLength(); i++) {
1168 UChar c = m_input[run.iCharPos + i];
1169 // Skip zero-width space characters because they're not considered to
1170 // be missing in a font.
1171 if (Font::treatAsZeroWidthSpaceInComplexScript(c))
1172 continue;
1173 int glyphIndex = shaping.m_logs[i];
1174 WORD glyph = shaping.m_glyphs[glyphIndex];
1175 // Note on the thrid condition: Windows Vista sometimes returns glyphs
1176 // equal to wgBlank (instead of wgDefault), with fZeroWidth set. Treat
1177 // such cases as having missing glyphs if the corresponding character
1178 // is not a zero width whitespace.
1179 if (glyph == properties->wgDefault
1180 || (glyph == properties->wgInvalid && glyph != properties->wgBlank)
1181 || (glyph == properties->wgBlank && shaping.m_visualAttributes[glyph Index].fZeroWidth && !Font::treatAsZeroWidthSpace(c)))
1182 return true;
1183 }
1184 return false;
1185 }
1186
1187 static OPENTYPE_TAG convertFeatureTag(const String& tag)
1188 {
1189 return ((tag[0] & 0xFF) | ((tag[1] & 0xFF) << 8) | ((tag[2] & 0xFF) << 16) | ((tag[3] & 0xFF) << 24));
1190 }
1191
1192 void UniscribeHelper::setRangeProperties(const FontFeatureSettings* featureSetti ngs)
1193 {
1194 if (!featureSettings || !featureSettings->size()) {
1195 m_featureRecords.resize(0);
1196 return;
1197 }
1198
1199 m_featureRecords.resize(featureSettings->size());
1200 for (unsigned i = 0; i < featureSettings->size(); ++i) {
1201 m_featureRecords[i].lParameter = featureSettings->at(i).value();
1202 m_featureRecords[i].tagFeature = convertFeatureTag(featureSettings->at(i ).tag());
1203 }
1204 m_rangeProperties.potfRecords = &m_featureRecords[0];
1205 m_rangeProperties.cotfRecords = m_featureRecords.size();
1206 }
1207
1208 } // namespace WebCore
OLDNEW
« no previous file with comments | « Source/core/platform/graphics/win/UniscribeHelper.h ('k') | Source/core/platform/graphics/win/UniscribeHelperTextRun.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698