FontAndroid.cpp revision 6aba9e528d4df20fbf9710d151753373d04a77d3
1/* 2 * Copyright 2009, The Android Open Source Project 3 * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 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 copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include "config.h" 28 29#include "EmojiFont.h" 30#include "GraphicsOperationCollection.h" 31#include "GraphicsOperation.h" 32#include "Font.h" 33#include "FontData.h" 34#include "FontFallbackList.h" 35#include "GraphicsContext.h" 36#include "GlyphBuffer.h" 37#include "IntRect.h" 38#include "NotImplemented.h" 39#include "PlatformGraphicsContext.h" 40#include "SkCanvas.h" 41#include "SkColorFilter.h" 42#include "SkLayerDrawLooper.h" 43#include "SkPaint.h" 44#include "SkTemplates.h" 45#include "SkTypeface.h" 46#include "SkUtils.h" 47#include "TextRun.h" 48 49#ifdef SUPPORT_COMPLEX_SCRIPTS 50#include "HarfbuzzSkia.h" 51#include <unicode/normlzr.h> 52#include <unicode/uchar.h> 53#include <wtf/OwnArrayPtr.h> 54#include <wtf/OwnPtr.h> 55#include <wtf/PassOwnArrayPtr.h> 56#include <wtf/PassOwnPtr.h> 57#include <wtf/unicode/CharacterNames.h> 58#include <wtf/unicode/Unicode.h> 59#endif 60 61using namespace android; 62 63namespace WebCore { 64 65typedef std::pair<int, float> FallbackFontKey; 66typedef HashMap<FallbackFontKey, FontPlatformData*> FallbackHash; 67 68static void updateForFont(SkPaint* paint, const SimpleFontData* font) { 69 font->platformData().setupPaint(paint); 70 paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding); 71} 72 73static SkPaint* setupFill(SkPaint* paint, GraphicsContext* gc, 74 const SimpleFontData* font) { 75 gc->setupFillPaint(paint); 76 updateForFont(paint, font); 77 return paint; 78} 79 80static SkPaint* setupStroke(SkPaint* paint, GraphicsContext* gc, 81 const SimpleFontData* font) { 82 gc->setupStrokePaint(paint); 83 updateForFont(paint, font); 84 return paint; 85} 86 87static bool setupForText(SkPaint* paint, GraphicsContext* gc, 88 const SimpleFontData* font) { 89 int mode = gc->textDrawingMode() & (TextModeFill | TextModeStroke); 90 if (!mode) 91 return false; 92 93 paint->setVerticalText(font->platformData().orientation() == Vertical); 94 95 FloatSize shadowOffset; 96 float shadowBlur; 97 Color shadowColor; 98 ColorSpace shadowColorSpace; 99 bool hasShadow = gc->getShadow(shadowOffset, shadowBlur, shadowColor, shadowColorSpace); 100 bool hasBothStrokeAndFill = 101 (mode & (TextModeStroke | TextModeFill)) == (TextModeStroke | TextModeFill); 102 if (hasShadow || hasBothStrokeAndFill) { 103 SkLayerDrawLooper* looper = new SkLayerDrawLooper; 104 paint->setLooper(looper)->unref(); 105 106 // The layerDrawLooper uses at the root paint to determine the text 107 // encoding so we need to make sure it is properly configured. 108 updateForFont(paint, font); 109 110 // Specify the behavior of the looper 111 SkLayerDrawLooper::LayerInfo info; 112 info.fPaintBits = SkLayerDrawLooper::kEntirePaint_Bits; 113 info.fColorMode = SkXfermode::kSrc_Mode; 114 info.fFlagsMask = SkPaint::kAllFlags; 115 116 // The paint is only valid until the looper receives another call to 117 // addLayer(). Therefore, we must cache certain state for later use. 118 bool hasFillPaint = false; 119 bool hasStrokePaint = false; 120 SkScalar strokeWidth; 121 122 if ((mode & TextModeStroke) && gc->willStroke()) { 123 strokeWidth = setupStroke(looper->addLayer(info), gc, font)->getStrokeWidth(); 124 hasStrokePaint = true; 125 } 126 if ((mode & TextModeFill) && gc->willFill()) { 127 setupFill(looper->addLayer(info), gc, font); 128 hasFillPaint = true; 129 } 130 131 if (hasShadow) { 132 SkPaint shadowPaint; 133 SkPoint offset; 134 if (gc->setupShadowPaint(&shadowPaint, &offset)) { 135 136 // add an offset to the looper when creating a shadow layer 137 info.fOffset.set(offset.fX, offset.fY); 138 139 SkPaint* p = looper->addLayer(info); 140 *p = shadowPaint; 141 142 // Currently, only GraphicsContexts associated with the 143 // HTMLCanvasElement have shadows ignore transforms set. This 144 // allows us to distinguish between CSS and Canvas shadows which 145 // have different rendering specifications. 146 if (gc->shadowsIgnoreTransforms()) { 147 SkColorFilter* cf = SkColorFilter::CreateModeFilter(p->getColor(), 148 SkXfermode::kSrcIn_Mode); 149 p->setColorFilter(cf)->unref(); 150 } else { // in CSS 151 p->setShader(NULL); 152 } 153 154 if (hasStrokePaint && !hasFillPaint) { 155 // stroke the shadow if we have stroke but no fill 156 p->setStyle(SkPaint::kStroke_Style); 157 p->setStrokeWidth(strokeWidth); 158 } 159 updateForFont(p, font); 160 } 161 } 162 } else if (mode & TextModeFill) { 163 (void)setupFill(paint, gc, font); 164 } else if (mode & TextModeStroke) { 165 (void)setupStroke(paint, gc, font); 166 } else { 167 return false; 168 } 169 return true; 170} 171 172bool Font::canReturnFallbackFontsForComplexText() 173{ 174 return false; 175} 176 177bool Font::canExpandAroundIdeographsInComplexText() 178{ 179 return false; 180} 181 182void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font, 183 const GlyphBuffer& glyphBuffer, int from, int numGlyphs, 184 const FloatPoint& point) const 185{ 186 // compile-time assert 187 SkASSERT(sizeof(GlyphBufferGlyph) == sizeof(uint16_t)); 188 189 SkPaint paint; 190 if (!setupForText(&paint, gc, font)) { 191 return; 192 } 193 194 SkScalar x = SkFloatToScalar(point.x()); 195 SkScalar y = SkFloatToScalar(point.y()); 196 const GlyphBufferGlyph* glyphs = glyphBuffer.glyphs(from); 197 const GlyphBufferAdvance* adv = glyphBuffer.advances(from); 198 SkAutoSTMalloc<32, SkPoint> storage(numGlyphs), storage2(numGlyphs), storage3(numGlyphs); 199 SkPoint* pos = storage.get(); 200 201 SkCanvas* canvas = gc->platformContext()->recordingCanvas(); 202 203 /* We need an array of [x,y,x,y,x,y,...], but webkit is giving us 204 point.xy + [width, height, width, height, ...], so we have to convert 205 */ 206 207 if (font->platformData().orientation() == Vertical) 208 y += SkFloatToScalar(font->fontMetrics().floatAscent(IdeographicBaseline) - font->fontMetrics().floatAscent()); 209 210 if (EmojiFont::IsAvailable()) { 211 // set filtering, to make scaled images look nice(r) 212 paint.setFilterBitmap(true); 213 214 int localIndex = 0; 215 int localCount = 0; 216 for (int i = 0; i < numGlyphs; i++) { 217 if (EmojiFont::IsEmojiGlyph(glyphs[i])) { 218 if (localCount) 219 canvas->drawPosText(&glyphs[localIndex], 220 localCount * sizeof(uint16_t), 221 &pos[localIndex], paint); 222 EmojiFont::Draw(canvas, glyphs[i], x, y, paint); 223 // reset local index/count track for "real" glyphs 224 localCount = 0; 225 localIndex = i + 1; 226 } else { 227 pos[i].set(x, y); 228 localCount += 1; 229 } 230 x += SkFloatToScalar(adv[i].width()); 231 y += SkFloatToScalar(adv[i].height()); 232 } 233 // draw the last run of glyphs (if any) 234 if (localCount) 235 canvas->drawPosText(&glyphs[localIndex], 236 localCount * sizeof(uint16_t), 237 &pos[localIndex], paint); 238 } else { 239 for (int i = 0; i < numGlyphs; i++) { 240 pos[i].set(x, y); 241 y += SkFloatToScalar(adv[i].height()); 242 x += SkFloatToScalar(adv[i].width()); 243 } 244 245 if (font->platformData().orientation() == Vertical) { 246 canvas->save(); 247 canvas->rotate(-90); 248 SkMatrix rotator; 249 rotator.reset(); 250 rotator.setRotate(90); 251 rotator.mapPoints(pos, numGlyphs); 252 } 253 254 canvas->drawPosText(glyphs, numGlyphs * sizeof(uint16_t), pos, paint); 255 256 if (font->platformData().orientation() == Vertical) 257 canvas->restore(); 258 } 259 gc->platformContext()->endRecording(); 260} 261 262void Font::drawEmphasisMarksForComplexText(WebCore::GraphicsContext*, WebCore::TextRun const&, WTF::AtomicString const&, WebCore::FloatPoint const&, int, int) const 263{ 264 notImplemented(); 265} 266 267#ifndef SUPPORT_COMPLEX_SCRIPTS 268 269FloatRect Font::selectionRectForComplexText(const TextRun& run, 270 const FloatPoint& point, int h, int, int) const 271{ 272 SkPaint paint; 273 SkScalar width, left; 274 SkPaint::FontMetrics metrics; 275 276 primaryFont()->platformData().setupPaint(&paint); 277 278 width = paint.measureText(run.characters(), run.length() << 1); 279 SkScalar spacing = paint.getFontMetrics(&metrics); 280 281 return FloatRect(point.x(), 282 point.y(), 283 roundf(SkScalarToFloat(width)), 284 roundf(SkScalarToFloat(spacing))); 285} 286 287void Font::drawComplexText(GraphicsContext* gc, TextRun const& run, 288 FloatPoint const& point, int, int) const 289{ 290 SkCanvas* canvas = gc->platformContext()->mCanvas; 291 SkPaint paint; 292 293 if (!setupForText(&paint, gc, primaryFont())) { 294 return; 295 } 296 297 // go to chars, instead of glyphs, which was set by setupForText() 298 paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); 299 300 canvas->drawText(run.characters(), run.length() << 1, 301 SkFloatToScalar(point.x()), SkFloatToScalar(point.y()), 302 paint); 303} 304 305float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>*, GlyphOverflow*) const 306{ 307 SkPaint paint; 308 309 primaryFont()->platformData().setupPaint(&paint); 310 311//printf("--------- complext measure %d chars\n", run.to() - run.from()); 312 313 SkScalar width = paint.measureText(run.characters(), run.length() << 1); 314 return SkScalarToFloat(width); 315} 316 317int Font::offsetForPositionForComplexText(const TextRun& run, float x, 318 bool includePartialGlyphs) const 319{ 320 SkPaint paint; 321 int count = run.length(); 322 SkAutoSTMalloc<64, SkScalar> storage(count); 323 SkScalar* widths = storage.get(); 324 325 primaryFont()->platformData().setupPaint(&paint); 326 327 count = paint.getTextWidths(run.characters(), count << 1, widths); 328 329 if (count > 0) 330 { 331 SkScalar pos = 0; 332 for (int i = 0; i < count; i++) 333 { 334 if (x < SkScalarRound(pos + SkScalarHalf(widths[i]))) 335 return i; 336 pos += widths[i]; 337 } 338 } 339 return count; 340} 341 342#else 343 344// TODO Should we remove the multilayer support? 345// If yes. remove isCanvasMultiLayered() and adjustTextRenderMode(). 346static bool isCanvasMultiLayered(SkCanvas* canvas) 347{ 348 SkCanvas::LayerIter layerIterator(canvas, false); 349 layerIterator.next(); 350 return !layerIterator.done(); 351} 352 353static void adjustTextRenderMode(SkPaint* paint, bool isCanvasMultiLayered) 354{ 355 // Our layers only have a single alpha channel. This means that subpixel 356 // rendered text cannot be compositied correctly when the layer is 357 // collapsed. Therefore, subpixel text is disabled when we are drawing 358 // onto a layer. 359 if (isCanvasMultiLayered) 360 paint->setLCDRenderText(false); 361} 362 363// Harfbuzz uses 26.6 fixed point values for pixel offsets. However, we don't 364// handle subpixel positioning so this function is used to truncate Harfbuzz 365// values to a number of pixels. 366static int truncateFixedPointToInteger(HB_Fixed value) 367{ 368 return value >> 6; 369} 370 371// TextRunWalker walks a TextRun and presents each script run in sequence. A 372// TextRun is a sequence of code-points with the same embedding level (i.e. they 373// are all left-to-right or right-to-left). A script run is a subsequence where 374// all the characters have the same script (e.g. Arabic, Thai etc). Shaping is 375// only ever done with script runs since the shapers only know how to deal with 376// a single script. 377// 378// After creating it, the script runs are either iterated backwards or forwards. 379// It defaults to backwards for RTL and forwards otherwise (which matches the 380// presentation order), however you can set it with |setBackwardsIteration|. 381// 382// Once you have setup the object, call |nextScriptRun| to get the first script 383// run. This will return false when the iteration is complete. At any time you 384// can call |reset| to start over again. 385class TextRunWalker { 386public: 387 TextRunWalker(const TextRun&, int, int, const Font*); 388 ~TextRunWalker(); 389 390 bool isWordBreak(unsigned, bool); 391 // setPadding sets a number of pixels to be distributed across the TextRun. 392 // WebKit uses this to justify text. 393 void setPadding(int); 394 void reset(); 395 void setBackwardsIteration(bool); 396 // Advance to the next script run, returning false when the end of the 397 // TextRun has been reached. 398 bool nextScriptRun(); 399 float widthOfFullRun(); 400 401 // setWordSpacingAdjustment sets a delta (in pixels) which is applied at 402 // each word break in the TextRun. 403 void setWordSpacingAdjustment(int wordSpacingAdjustment) 404 { 405 m_wordSpacingAdjustment = wordSpacingAdjustment; 406 } 407 408 // setLetterSpacingAdjustment sets an additional number of pixels that is 409 // added to the advance after each output cluster. This matches the behaviour 410 // of WidthIterator::advance. 411 // 412 // (NOTE: currently does nothing because I don't know how to get the 413 // cluster information from Harfbuzz.) 414 void setLetterSpacingAdjustment(int letterSpacingAdjustment) 415 { 416 m_letterSpacing = letterSpacingAdjustment; 417 } 418 419 // setWordAndLetterSpacing calls setWordSpacingAdjustment() and 420 // setLetterSpacingAdjustment() to override m_wordSpacingAdjustment 421 // and m_letterSpacing. 422 void setWordAndLetterSpacing(int wordSpacingAdjustment, int letterSpacingAdjustment); 423 424 // Set the x offset for the next script run. This affects the values in 425 // |xPositions| 426 void setXOffsetToZero() { m_offsetX = 0; } 427 bool rtl() const { return m_run.rtl(); } 428 const uint16_t* glyphs() const { return m_glyphs16; } 429 430 // Return the length of the array returned by |glyphs| 431 unsigned length() const { return m_item.num_glyphs; } 432 433 // Return the offset for each of the glyphs. Note that this is translated 434 // by the current x offset and that the x offset is updated for each script 435 // run. 436 const SkPoint* positions() const { return m_positions; } 437 438 // Get the advances (widths) for each glyph. 439 const HB_Fixed* advances() const { return m_item.advances; } 440 441 // Return the width (in px) of the current script run. 442 unsigned width() const { return m_pixelWidth; } 443 444 // Return the cluster log for the current script run. For example: 445 // script run: f i a n c é (fi gets ligatured) 446 // log clutrs: 0 0 1 2 3 4 447 // So, for each input code point, the log tells you which output glyph was 448 // generated for it. 449 const unsigned short* logClusters() const { return m_item.log_clusters; } 450 451 // return the number of code points in the current script run 452 unsigned numCodePoints() const { return m_numCodePoints; } 453 454 const FontPlatformData* fontPlatformDataForScriptRun() { 455 return reinterpret_cast<FontPlatformData*>(m_item.font->userData); 456 } 457 458private: 459 enum CustomScript { 460 Bengali, 461 Devanagari, 462 Hebrew, 463 HebrewBold, 464 Kannada, 465 Malayalam, 466 Naskh, 467 Tamil, 468 TamilBold, 469 Telugu, 470 Thai, 471 NUM_SCRIPTS 472 }; 473 474 static const char* paths[NUM_SCRIPTS]; 475 476 void setupFontForScriptRun(); 477 const FontPlatformData* setupComplexFont(CustomScript script, 478 const FontPlatformData& platformData); 479 HB_FontRec* allocHarfbuzzFont(); 480 void deleteGlyphArrays(); 481 void createGlyphArrays(int); 482 void resetGlyphArrays(); 483 void shapeGlyphs(); 484 void setGlyphPositions(bool); 485 486 static void normalizeSpacesAndMirrorChars(const UChar* source, bool rtl, 487 UChar* destination, int length); 488 static const TextRun& getNormalizedTextRun(const TextRun& originalRun, 489 OwnPtr<TextRun>& normalizedRun, OwnArrayPtr<UChar>& normalizedBuffer); 490 491 // This matches the logic in RenderBlock::findNextLineBreak 492 static bool isCodepointSpace(HB_UChar16 c) { return c == ' ' || c == '\t'; } 493 494 const Font* const m_font; 495 HB_ShaperItem m_item; 496 uint16_t* m_glyphs16; // A vector of 16-bit glyph ids. 497 SkPoint* m_positions; // A vector of positions for each glyph. 498 ssize_t m_indexOfNextScriptRun; // Indexes the script run in |m_run|. 499 const int m_startingX; // Offset in pixels of the first script run. 500 const int m_startingY; // Offset in pixels of the first script run. 501 int m_offsetX; // Offset in pixels to the start of the next script run. 502 unsigned m_pixelWidth; // Width (in px) of the current script run. 503 unsigned m_numCodePoints; // Code points in current script run. 504 unsigned m_glyphsArrayCapacity; // Current size of all the Harfbuzz arrays. 505 506 OwnPtr<TextRun> m_normalizedRun; 507 OwnArrayPtr<UChar> m_normalizedBuffer; // A buffer for normalized run. 508 const TextRun& m_run; 509 bool m_iterateBackwards; 510 int m_wordSpacingAdjustment; // delta adjustment (pixels) for each word break. 511 float m_padding; // pixels to be distributed over the line at word breaks. 512 float m_padPerWordBreak; // pixels to be added to each word break. 513 float m_padError; // |m_padPerWordBreak| might have a fractional component. 514 // Since we only add a whole number of padding pixels at 515 // each word break we accumulate error. This is the 516 // number of pixels that we are behind so far. 517 unsigned m_letterSpacing; // pixels to be added after each glyph. 518}; 519 520 521// Indexed using enum CustomScript 522const char* TextRunWalker::paths[] = { 523 "/system/fonts/Lohit-Bengali.ttf", 524 "/system/fonts/DroidSansDevanagari-Regular.ttf", 525 "/system/fonts/DroidSansHebrew-Regular.ttf", 526 "/system/fonts/DroidSansHebrew-Bold.ttf", 527 "/system/fonts/Lohit-Kannada.ttf", 528 "/system/fonts/AnjaliNewLipi-light.ttf", 529 "/system/fonts/DroidNaskh-Regular.ttf", 530 "/system/fonts/DroidSansTamil-Regular.ttf", 531 "/system/fonts/DroidSansTamil-Bold.ttf", 532 "/system/fonts/Lohit-Telugu.ttf", 533 "/system/fonts/DroidSansThai.ttf" 534}; 535 536TextRunWalker::TextRunWalker(const TextRun& run, int startingX, int startingY, const Font* font) 537 : m_font(font) 538 , m_startingX(startingX) 539 , m_startingY(startingY) 540 , m_offsetX(m_startingX) 541 , m_run(getNormalizedTextRun(run, m_normalizedRun, m_normalizedBuffer)) 542 , m_iterateBackwards(m_run.rtl()) 543 , m_wordSpacingAdjustment(0) 544 , m_padding(0) 545 , m_padPerWordBreak(0) 546 , m_padError(0) 547 , m_letterSpacing(0) 548{ 549 // Do not use |run| inside this constructor. Use |m_run| instead. 550 551 memset(&m_item, 0, sizeof(m_item)); 552 // We cannot know, ahead of time, how many glyphs a given script run 553 // will produce. We take a guess that script runs will not produce more 554 // than twice as many glyphs as there are code points plus a bit of 555 // padding and fallback if we find that we are wrong. 556 createGlyphArrays((m_run.length() + 2) * 2); 557 558 m_item.log_clusters = new unsigned short[m_run.length()]; 559 560 m_item.face = 0; 561 m_item.font = allocHarfbuzzFont(); 562 563 m_item.item.bidiLevel = m_run.rtl(); 564 565 m_item.string = m_run.characters(); 566 m_item.stringLength = m_run.length(); 567 568 reset(); 569} 570 571TextRunWalker::~TextRunWalker() 572{ 573 fastFree(m_item.font); 574 deleteGlyphArrays(); 575 delete[] m_item.log_clusters; 576} 577 578bool TextRunWalker::isWordBreak(unsigned index, bool isRTL) 579{ 580 if (!isRTL) 581 return index && isCodepointSpace(m_item.string[index]) 582 && !isCodepointSpace(m_item.string[index - 1]); 583 return index != m_item.stringLength - 1 && isCodepointSpace(m_item.string[index]) 584 && !isCodepointSpace(m_item.string[index + 1]); 585} 586 587// setPadding sets a number of pixels to be distributed across the TextRun. 588// WebKit uses this to justify text. 589void TextRunWalker::setPadding(int padding) 590{ 591 m_padding = padding; 592 if (!m_padding) 593 return; 594 595 // If we have padding to distribute, then we try to give an equal 596 // amount to each space. The last space gets the smaller amount, if 597 // any. 598 unsigned numWordBreaks = 0; 599 bool isRTL = m_iterateBackwards; 600 601 for (unsigned i = 0; i < m_item.stringLength; i++) { 602 if (isWordBreak(i, isRTL)) 603 numWordBreaks++; 604 } 605 606 if (numWordBreaks) 607 m_padPerWordBreak = m_padding / numWordBreaks; 608 else 609 m_padPerWordBreak = 0; 610} 611 612void TextRunWalker::reset() 613{ 614 if (m_iterateBackwards) 615 m_indexOfNextScriptRun = m_run.length() - 1; 616 else 617 m_indexOfNextScriptRun = 0; 618 m_offsetX = m_startingX; 619} 620 621void TextRunWalker::setBackwardsIteration(bool isBackwards) 622{ 623 m_iterateBackwards = isBackwards; 624 reset(); 625} 626 627// Advance to the next script run, returning false when the end of the 628// TextRun has been reached. 629bool TextRunWalker::nextScriptRun() 630{ 631 if (m_iterateBackwards) { 632 // In right-to-left mode we need to render the shaped glyph backwards and 633 // also render the script runs themselves backwards. So given a TextRun: 634 // AAAAAAACTTTTTTT (A = Arabic, C = Common, T = Thai) 635 // we render: 636 // TTTTTTCAAAAAAA 637 // (and the glyphs in each A, C and T section are backwards too) 638 if (!hb_utf16_script_run_prev(&m_numCodePoints, &m_item.item, m_run.characters(), 639 m_run.length(), &m_indexOfNextScriptRun)) 640 return false; 641 } else { 642 if (!hb_utf16_script_run_next(&m_numCodePoints, &m_item.item, m_run.characters(), 643 m_run.length(), &m_indexOfNextScriptRun)) 644 return false; 645 646 // It is actually wrong to consider script runs at all in this code. 647 // Other WebKit code (e.g. Mac) segments complex text just by finding 648 // the longest span of text covered by a single font. 649 // But we currently need to call hb_utf16_script_run_next anyway to fill 650 // in the harfbuzz data structures to e.g. pick the correct script's shaper. 651 // So we allow that to run first, then do a second pass over the range it 652 // found and take the largest subregion that stays within a single font. 653 const FontData* glyphData = m_font->glyphDataForCharacter( 654 m_item.string[m_item.item.pos], false).fontData; 655 unsigned endOfRun; 656 for (endOfRun = 1; endOfRun < m_item.item.length; ++endOfRun) { 657 const FontData* nextGlyphData = m_font->glyphDataForCharacter( 658 m_item.string[m_item.item.pos + endOfRun], false).fontData; 659 if (nextGlyphData != glyphData) 660 break; 661 } 662 m_item.item.length = endOfRun; 663 m_indexOfNextScriptRun = m_item.item.pos + endOfRun; 664 } 665 666 setupFontForScriptRun(); 667 shapeGlyphs(); 668 setGlyphPositions(rtl()); 669 670 return true; 671} 672 673float TextRunWalker::widthOfFullRun() 674{ 675 float widthSum = 0; 676 while (nextScriptRun()) 677 widthSum += width(); 678 679 return widthSum; 680} 681 682void TextRunWalker::setWordAndLetterSpacing(int wordSpacingAdjustment, 683 int letterSpacingAdjustment) 684{ 685 setWordSpacingAdjustment(wordSpacingAdjustment); 686 setLetterSpacingAdjustment(letterSpacingAdjustment); 687} 688 689const FontPlatformData* TextRunWalker::setupComplexFont( 690 CustomScript script, 691 const FontPlatformData& platformData) 692{ 693 static FallbackHash fallbackPlatformData; 694 695 FallbackFontKey key(script, platformData.size()); 696 FontPlatformData* newPlatformData = 0; 697 698 if (!fallbackPlatformData.contains(key)) { 699 SkTypeface* typeface = SkTypeface::CreateFromFile(paths[script]); 700 newPlatformData = new FontPlatformData(platformData, typeface); 701 SkSafeUnref(typeface); 702 fallbackPlatformData.set(key, newPlatformData); 703 } 704 705 if (!newPlatformData) 706 newPlatformData = fallbackPlatformData.get(key); 707 708 // If we couldn't allocate a new FontPlatformData, revert to the one passed 709 return newPlatformData ? newPlatformData : &platformData; 710} 711 712void TextRunWalker::setupFontForScriptRun() 713{ 714 const FontData* fontData = m_font->glyphDataForCharacter(m_run[0], false).fontData; 715 const FontPlatformData& platformData = 716 fontData->fontDataForCharacter(' ')->platformData(); 717 const FontPlatformData* complexPlatformData = &platformData; 718 719 switch (m_item.item.script) { 720 case HB_Script_Bengali: 721 complexPlatformData = setupComplexFont(Bengali, platformData); 722 break; 723 case HB_Script_Devanagari: 724 complexPlatformData = setupComplexFont(Devanagari, platformData); 725 break; 726 case HB_Script_Hebrew: 727 switch (platformData.typeface()->style()) { 728 case SkTypeface::kBold: 729 case SkTypeface::kBoldItalic: 730 complexPlatformData = setupComplexFont(HebrewBold, platformData); 731 break; 732 case SkTypeface::kNormal: 733 case SkTypeface::kItalic: 734 default: 735 complexPlatformData = setupComplexFont(Hebrew, platformData); 736 break; 737 } 738 break; 739 case HB_Script_Kannada: 740 complexPlatformData = setupComplexFont(Kannada, platformData); 741 break; 742 case HB_Script_Malayalam: 743 complexPlatformData = setupComplexFont(Malayalam, platformData); 744 break; 745 case HB_Script_Arabic: 746 complexPlatformData = setupComplexFont(Naskh, platformData); 747 break; 748 case HB_Script_Tamil: 749 switch (platformData.typeface()->style()) { 750 case SkTypeface::kBold: 751 case SkTypeface::kBoldItalic: 752 complexPlatformData = setupComplexFont(TamilBold, platformData); 753 break; 754 case SkTypeface::kNormal: 755 case SkTypeface::kItalic: 756 default: 757 complexPlatformData = setupComplexFont(Tamil, platformData); 758 break; 759 } 760 break; 761 case HB_Script_Telugu: 762 complexPlatformData = setupComplexFont(Telugu, platformData); 763 break; 764 case HB_Script_Thai: 765 complexPlatformData = setupComplexFont(Thai, platformData); 766 break; 767 default: 768 // HB_Script_Common; includes Ethiopic 769 complexPlatformData = &platformData; 770 break; 771 } 772 m_item.face = complexPlatformData->harfbuzzFace(); 773 m_item.font->userData = const_cast<FontPlatformData*>(complexPlatformData); 774 775 int size = complexPlatformData->size(); 776 m_item.font->x_ppem = size; 777 m_item.font->y_ppem = size; 778 // x_ and y_scale are the conversion factors from font design space (fEmSize) to 1/64th of device pixels in 16.16 format. 779 const int devicePixelFraction = 64; 780 const int multiplyFor16Dot16 = 1 << 16; 781 int scale = devicePixelFraction * size * multiplyFor16Dot16 / complexPlatformData->emSizeInFontUnits(); 782 m_item.font->x_scale = scale; 783 m_item.font->y_scale = scale; 784} 785 786HB_FontRec* TextRunWalker::allocHarfbuzzFont() 787{ 788 HB_FontRec* font = reinterpret_cast<HB_FontRec*>(fastMalloc(sizeof(HB_FontRec))); 789 memset(font, 0, sizeof(HB_FontRec)); 790 font->klass = &harfbuzzSkiaClass; 791 font->userData = 0; 792 793 return font; 794} 795 796void TextRunWalker::deleteGlyphArrays() 797{ 798 delete[] m_item.glyphs; 799 delete[] m_item.attributes; 800 delete[] m_item.advances; 801 delete[] m_item.offsets; 802 delete[] m_glyphs16; 803 delete[] m_positions; 804} 805 806void TextRunWalker::createGlyphArrays(int size) 807{ 808 m_item.glyphs = new HB_Glyph[size]; 809 m_item.attributes = new HB_GlyphAttributes[size]; 810 m_item.advances = new HB_Fixed[size]; 811 m_item.offsets = new HB_FixedPoint[size]; 812 813 m_glyphs16 = new uint16_t[size]; 814 m_positions = new SkPoint[size]; 815 816 m_item.num_glyphs = size; 817 m_glyphsArrayCapacity = size; // Save the GlyphArrays size. 818} 819 820void TextRunWalker::resetGlyphArrays() 821{ 822 int size = m_item.num_glyphs; 823 // All the types here don't have pointers. It is safe to reset to 824 // zero unless Harfbuzz breaks the compatibility in the future. 825 memset(m_item.glyphs, 0, size * sizeof(m_item.glyphs[0])); 826 memset(m_item.attributes, 0, size * sizeof(m_item.attributes[0])); 827 memset(m_item.advances, 0, size * sizeof(m_item.advances[0])); 828 memset(m_item.offsets, 0, size * sizeof(m_item.offsets[0])); 829 memset(m_glyphs16, 0, size * sizeof(m_glyphs16[0])); 830 memset(m_positions, 0, size * sizeof(m_positions[0])); 831} 832 833void TextRunWalker::shapeGlyphs() 834{ 835 // HB_ShapeItem() resets m_item.num_glyphs. If the previous call to 836 // HB_ShapeItem() used less space than was available, the capacity of 837 // the array may be larger than the current value of m_item.num_glyphs. 838 // So, we need to reset the num_glyphs to the capacity of the array. 839 m_item.num_glyphs = m_glyphsArrayCapacity; 840 resetGlyphArrays(); 841 while (!HB_ShapeItem(&m_item)) { 842 // We overflowed our arrays. Resize and retry. 843 // HB_ShapeItem fills in m_item.num_glyphs with the needed size. 844 deleteGlyphArrays(); 845 createGlyphArrays(m_item.num_glyphs << 1); 846 resetGlyphArrays(); 847 } 848} 849 850void TextRunWalker::setGlyphPositions(bool isRTL) 851{ 852 int position = 0; 853 // logClustersIndex indexes logClusters for the first (or last when 854 // RTL) codepoint of the current glyph. Each time we advance a glyph, 855 // we skip over all the codepoints that contributed to the current 856 // glyph. 857 unsigned logClustersIndex = isRTL && m_item.num_glyphs ? m_item.num_glyphs - 1 : 0; 858 859 for (unsigned iter = 0; iter < m_item.num_glyphs; ++iter) { 860 // Glyphs are stored in logical order, but for layout purposes we 861 // always go left to right. 862 int i = isRTL ? m_item.num_glyphs - iter - 1 : iter; 863 864 m_glyphs16[i] = m_item.glyphs[i]; 865 int offsetX = truncateFixedPointToInteger(m_item.offsets[i].x); 866 int offsetY = truncateFixedPointToInteger(m_item.offsets[i].y); 867 m_positions[i].set(SkIntToScalar(m_offsetX + position) + offsetX, m_startingY + offsetY); 868 869 int advance = truncateFixedPointToInteger(m_item.advances[i]); 870 // The first half of the conjunction works around the case where 871 // output glyphs aren't associated with any codepoints by the 872 // clusters log. 873 if (logClustersIndex < m_item.item.length 874 && isWordBreak(m_item.item.pos + logClustersIndex, isRTL)) { 875 advance += m_wordSpacingAdjustment; 876 877 if (m_padding > 0) { 878 int toPad = roundf(m_padPerWordBreak + m_padError); 879 m_padError += m_padPerWordBreak - toPad; 880 881 if (m_padding < toPad) 882 toPad = m_padding; 883 m_padding -= toPad; 884 advance += toPad; 885 } 886 } 887 888 // ZeroWidthJoiners and ZeroWidthNonJoiners should be stripped by 889 // Harfbuzz, but aren't. Check for zwj and zwnj and replace with a 890 // zero width space. We get the glyph data for space instead of 891 // zeroWidthSpace because the latter was seen to render with an 892 // unexpected code point (the symbol for a cloud). Since the standard 893 // space is in page zero and since we've also confirmed that there is 894 // no advance on this glyph, that should be ok. 895 if (0 == m_item.advances[i]) { 896 const HB_UChar16 c = m_item.string[m_item.item.pos + logClustersIndex]; 897 if ((c == zeroWidthJoiner) || (c == zeroWidthNonJoiner)) { 898 static Glyph spaceGlyph = m_font->glyphDataForCharacter(space, false).glyph; 899 m_glyphs16[i] = spaceGlyph; 900 } 901 } 902 903 // TODO We would like to add m_letterSpacing after each cluster, but I 904 // don't know where the cluster information is. This is typically 905 // fine for Roman languages, but breaks more complex languages 906 // terribly. 907 // advance += m_letterSpacing; 908 909 if (isRTL) { 910 while (logClustersIndex > 0 && logClusters()[logClustersIndex] == i) 911 logClustersIndex--; 912 } else { 913 while (logClustersIndex < m_item.item.length 914 && logClusters()[logClustersIndex] == i) 915 logClustersIndex++; 916 } 917 918 position += advance; 919 } 920 921 m_pixelWidth = position; 922 m_offsetX += m_pixelWidth; 923} 924 925void TextRunWalker::normalizeSpacesAndMirrorChars(const UChar* source, bool rtl, 926 UChar* destination, int length) 927{ 928 int position = 0; 929 bool error = false; 930 // Iterate characters in source and mirror character if needed. 931 while (position < length) { 932 UChar32 character; 933 int nextPosition = position; 934 U16_NEXT(source, nextPosition, length, character); 935 936 if (Font::treatAsSpace(character)) 937 character = space; 938 else if (Font::treatAsZeroWidthSpaceInComplexScript(character)) 939 character = zeroWidthSpace; 940 else if (rtl) 941 character = u_charMirror(character); 942 943 U16_APPEND(destination, position, length, character, error); 944 ASSERT(!error); 945 position = nextPosition; 946 } 947} 948 949const TextRun& TextRunWalker::getNormalizedTextRun(const TextRun& originalRun, 950 OwnPtr<TextRun>& normalizedRun, OwnArrayPtr<UChar>& normalizedBuffer) 951{ 952 // Normalize the text run in three ways: 953 // 1) Convert the |originalRun| to NFC normalized form if combining diacritical marks 954 // (U+0300..) are used in the run. This conversion is necessary since most OpenType 955 // fonts (e.g., Arial) don't have substitution rules for the diacritical marks in 956 // their GSUB tables. 957 // 958 // Note that we don't use the icu::Normalizer::isNormalized(UNORM_NFC) API here since 959 // the API returns FALSE (= not normalized) for complex runs that don't require NFC 960 // normalization (e.g., Arabic text). Unless the run contains the diacritical marks, 961 // Harfbuzz will do the same thing for us using the GSUB table. 962 // 2) Convert spacing characters into plain spaces, as some fonts will provide glyphs 963 // for characters like '\n' otherwise. 964 // 3) Convert mirrored characters such as parenthesis for rtl text. 965 966 // Convert to NFC form if the text has diacritical marks. 967 icu::UnicodeString normalizedString; 968 UErrorCode error = U_ZERO_ERROR; 969 970 for (int16_t i = 0; i < originalRun.length(); ++i) { 971 UChar ch = originalRun[i]; 972 if (::ublock_getCode(ch) == UBLOCK_COMBINING_DIACRITICAL_MARKS) { 973 icu::Normalizer::normalize(icu::UnicodeString(originalRun.characters(), 974 originalRun.length()), UNORM_NFC, 0 /* no options */, 975 normalizedString, error); 976 if (U_FAILURE(error)) 977 return originalRun; 978 break; 979 } 980 } 981 982 // Normalize space and mirror parenthesis for rtl text. 983 int normalizedBufferLength; 984 const UChar* sourceText; 985 if (normalizedString.isEmpty()) { 986 normalizedBufferLength = originalRun.length(); 987 sourceText = originalRun.characters(); 988 } else { 989 normalizedBufferLength = normalizedString.length(); 990 sourceText = normalizedString.getBuffer(); 991 } 992 993 normalizedBuffer = adoptArrayPtr(new UChar[normalizedBufferLength + 1]); 994 995 normalizeSpacesAndMirrorChars(sourceText, originalRun.rtl(), normalizedBuffer.get(), 996 normalizedBufferLength); 997 998 normalizedRun = adoptPtr(new TextRun(originalRun)); 999 normalizedRun->setText(normalizedBuffer.get(), normalizedBufferLength); 1000 return *normalizedRun; 1001} 1002 1003FloatRect Font::selectionRectForComplexText(const TextRun& run, 1004 const FloatPoint& point, int height, int from, int to) const 1005{ 1006 1007 int fromX = -1, toX = -1, fromAdvance = -1, toAdvance = -1; 1008 TextRunWalker walker(run, 0, 0, this); 1009 walker.setWordAndLetterSpacing(wordSpacing(), letterSpacing()); 1010 1011 // Base will point to the x offset for the current script run. Note that, in 1012 // the LTR case, width will be 0. 1013 int base = walker.rtl() ? walker.widthOfFullRun() : 0; 1014 const int leftEdge = base; 1015 1016 // We want to enumerate the script runs in code point order in the following 1017 // code. This call also resets |walker|. 1018 walker.setBackwardsIteration(false); 1019 1020 while (walker.nextScriptRun() && (fromX == -1 || toX == -1)) { 1021 // TextRunWalker will helpfully accumulate the x offsets for different 1022 // script runs for us. For this code, however, we always want the x 1023 // offsets to start from zero so we call this before each script run. 1024 walker.setXOffsetToZero(); 1025 1026 if (walker.rtl()) 1027 base -= walker.width(); 1028 1029 int numCodePoints = static_cast<int>(walker.numCodePoints()); 1030 if (fromX == -1 && from < numCodePoints) { 1031 // |from| is within this script run. So we index the clusters log to 1032 // find which glyph this code-point contributed to and find its x 1033 // position. 1034 int glyph = walker.logClusters()[from]; 1035 fromX = base + walker.positions()[glyph].x(); 1036 fromAdvance = walker.advances()[glyph]; 1037 } else if (!walker.rtl()) 1038 from -= numCodePoints; 1039 1040 if (toX == -1 && to < numCodePoints) { 1041 int glyph = walker.logClusters()[to]; 1042 toX = base + walker.positions()[glyph].x(); 1043 toAdvance = walker.advances()[glyph]; 1044 } else if (!walker.rtl()) 1045 to -= numCodePoints; 1046 1047 if (!walker.rtl()) 1048 base += walker.width(); 1049 } 1050 1051 // The position in question might be just after the text. 1052 const int rightEdge = base; 1053 if (fromX == -1 && !from) 1054 fromX = leftEdge; 1055 else if (walker.rtl()) 1056 fromX += truncateFixedPointToInteger(fromAdvance); 1057 1058 if (toX == -1 && !to) 1059 toX = rightEdge; 1060 1061 ASSERT(fromX != -1 && toX != -1); 1062 1063 if (fromX < toX) 1064 return FloatRect(point.x() + fromX, point.y(), toX - fromX, height); 1065 1066 return FloatRect(point.x() + toX, point.y(), fromX - toX, height); 1067} 1068 1069void Font::drawComplexText(GraphicsContext* gc, TextRun const& run, 1070 FloatPoint const& point, int, int) const 1071{ 1072 if (!run.length()) 1073 return; 1074 1075 int mode = gc->textDrawingMode(); 1076 bool fill = mode & TextModeFill; 1077 bool stroke = mode & TextModeStroke; 1078 if (!fill && !stroke) 1079 return; 1080 1081 SkPaint fillPaint, strokePaint; 1082 if (fill) 1083 setupFill(&fillPaint, gc, primaryFont()); 1084 if (stroke) 1085 setupStroke(&strokePaint, gc, primaryFont()); 1086 1087 SkCanvas* canvas = gc->platformContext()->recordingCanvas(); 1088 1089 bool haveMultipleLayers = isCanvasMultiLayered(canvas); 1090 TextRunWalker walker(run, point.x(), point.y(), this); 1091 walker.setWordAndLetterSpacing(wordSpacing(), letterSpacing()); 1092 walker.setPadding(run.expansion()); 1093 1094 while (walker.nextScriptRun()) { 1095 if (fill) { 1096 walker.fontPlatformDataForScriptRun()->setupPaint(&fillPaint); 1097 adjustTextRenderMode(&fillPaint, haveMultipleLayers); 1098 canvas->drawPosText(walker.glyphs(), walker.length() << 1, 1099 walker.positions(), fillPaint); 1100 } 1101 if (stroke) { 1102 walker.fontPlatformDataForScriptRun()->setupPaint(&strokePaint); 1103 adjustTextRenderMode(&strokePaint, haveMultipleLayers); 1104 canvas->drawPosText(walker.glyphs(), walker.length() << 1, 1105 walker.positions(), strokePaint); 1106 } 1107 } 1108 1109 gc->platformContext()->endRecording(); 1110} 1111 1112float Font::floatWidthForComplexText(const TextRun& run, 1113 HashSet<const SimpleFontData*>*, GlyphOverflow*) const 1114{ 1115 TextRunWalker walker(run, 0, 0, this); 1116 walker.setWordAndLetterSpacing(wordSpacing(), letterSpacing()); 1117 return walker.widthOfFullRun(); 1118} 1119 1120static int glyphIndexForXPositionInScriptRun(const TextRunWalker& walker, int x) 1121{ 1122 const HB_Fixed* advances = walker.advances(); 1123 int glyphIndex; 1124 if (walker.rtl()) { 1125 for (glyphIndex = walker.length() - 1; glyphIndex >= 0; --glyphIndex) { 1126 if (x < truncateFixedPointToInteger(advances[glyphIndex])) 1127 break; 1128 x -= truncateFixedPointToInteger(advances[glyphIndex]); 1129 } 1130 } else { 1131 for (glyphIndex = 0; glyphIndex < static_cast<int>(walker.length()); 1132 ++glyphIndex) { 1133 if (x < truncateFixedPointToInteger(advances[glyphIndex])) 1134 break; 1135 x -= truncateFixedPointToInteger(advances[glyphIndex]); 1136 } 1137 } 1138 1139 return glyphIndex; 1140} 1141 1142int Font::offsetForPositionForComplexText(const TextRun& run, float x, 1143 bool includePartialGlyphs) const 1144{ 1145 // (Mac code ignores includePartialGlyphs, and they don't know what it's 1146 // supposed to do, so we just ignore it as well.) 1147 TextRunWalker walker(run, 0, 0, this); 1148 walker.setWordAndLetterSpacing(wordSpacing(), letterSpacing()); 1149 1150 // If this is RTL text, the first glyph from the left is actually the last 1151 // code point. So we need to know how many code points there are total in 1152 // order to subtract. This is different from the length of the TextRun 1153 // because UTF-16 surrogate pairs are a single code point, but 32-bits long. 1154 // In LTR we leave this as 0 so that we get the correct value for 1155 // |basePosition|, below. 1156 unsigned totalCodePoints = 0; 1157 if (walker.rtl()) { 1158 ssize_t offset = 0; 1159 while (offset < run.length()) { 1160 utf16_to_code_point(run.characters(), run.length(), &offset); 1161 totalCodePoints++; 1162 } 1163 } 1164 1165 unsigned basePosition = totalCodePoints; 1166 1167 // For RTL: 1168 // code-point order: abcd efg hijkl 1169 // on screen: lkjih gfe dcba 1170 // ^ ^ 1171 // | | 1172 // basePosition--| | 1173 // totalCodePoints----| 1174 // Since basePosition is currently the total number of code-points, the 1175 // first thing we do is decrement it so that it's pointing to the start of 1176 // the current script-run. 1177 // 1178 // For LTR, basePosition is zero so it already points to the start of the 1179 // first script run. 1180 while (walker.nextScriptRun()) { 1181 if (walker.rtl()) 1182 basePosition -= walker.numCodePoints(); 1183 1184 if (x >= 0 && x < static_cast<int>(walker.width())) { 1185 // The x value in question is within this script run. We consider 1186 // each glyph in presentation order and stop when we find the one 1187 // covering this position. 1188 const int glyphIndex = glyphIndexForXPositionInScriptRun(walker, x); 1189 1190 // Now that we have a glyph index, we have to turn that into a 1191 // code-point index. Because of ligatures, several code-points may 1192 // have gone into a single glyph. We iterate over the clusters log 1193 // and find the first code-point which contributed to the glyph. 1194 1195 // Some shapers (i.e. Khmer) will produce cluster logs which report 1196 // that /no/ code points contributed to certain glyphs. Because of 1197 // this, we take any code point which contributed to the glyph in 1198 // question, or any subsequent glyph. If we run off the end, then 1199 // we take the last code point. 1200 const unsigned short* log = walker.logClusters(); 1201 for (unsigned j = 0; j < walker.numCodePoints(); ++j) { 1202 if (log[j] >= glyphIndex) 1203 return basePosition + j; 1204 } 1205 1206 return basePosition + walker.numCodePoints() - 1; 1207 } 1208 1209 x -= walker.width(); 1210 1211 if (!walker.rtl()) 1212 basePosition += walker.numCodePoints(); 1213 } 1214 1215 return basePosition; 1216} 1217#endif 1218 1219} 1220