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

Side by Side Diff: Source/core/platform/graphics/chromium/UniscribeHelper.cpp

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

Powered by Google App Engine
This is Rietveld 408576698