| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (c) 2012 Google Inc. All rights reserved. | 2 * Copyright (c) 2012 Google Inc. All rights reserved. |
| 3 * Copyright (C) 2013 BlackBerry Limited. All rights reserved. | 3 * Copyright (C) 2013 BlackBerry Limited. All rights reserved. |
| 4 * | 4 * |
| 5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
| 6 * modification, are permitted provided that the following conditions are | 6 * modification, are permitted provided that the following conditions are |
| 7 * met: | 7 * met: |
| 8 * | 8 * |
| 9 * * Redistributions of source code must retain the above copyright | 9 * * Redistributions of source code must retain the above copyright |
| 10 * notice, this list of conditions and the following disclaimer. | 10 * notice, this list of conditions and the following disclaimer. |
| (...skipping 13 matching lines...) Expand all Loading... |
| 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 30 */ | 30 */ |
| 31 | 31 |
| 32 #include "platform/fonts/shaping/HarfBuzzShaper.h" | 32 #include "platform/fonts/shaping/HarfBuzzShaper.h" |
| 33 | 33 |
| 34 #include "platform/RuntimeEnabledFeatures.h" | |
| 35 #include "platform/fonts/Font.h" | 34 #include "platform/fonts/Font.h" |
| 36 #include "platform/fonts/FontDescription.h" | 35 #include "platform/fonts/FontDescription.h" |
| 37 #include "platform/fonts/FontFallbackIterator.h" | 36 #include "platform/fonts/FontFallbackIterator.h" |
| 38 #include "platform/fonts/GlyphBuffer.h" | 37 #include "platform/fonts/GlyphBuffer.h" |
| 39 #include "platform/fonts/SmallCapsIterator.h" | 38 #include "platform/fonts/SmallCapsIterator.h" |
| 40 #include "platform/fonts/UTF16TextIterator.h" | 39 #include "platform/fonts/UTF16TextIterator.h" |
| 41 #include "platform/fonts/opentype/OpenTypeCapsSupport.h" | 40 #include "platform/fonts/opentype/OpenTypeCapsSupport.h" |
| 42 #include "platform/fonts/shaping/CaseMappingHarfBuzzBufferFiller.h" | 41 #include "platform/fonts/shaping/CaseMappingHarfBuzzBufferFiller.h" |
| 43 #include "platform/fonts/shaping/HarfBuzzFace.h" | 42 #include "platform/fonts/shaping/HarfBuzzFace.h" |
| 44 #include "platform/fonts/shaping/RunSegmenter.h" | 43 #include "platform/fonts/shaping/RunSegmenter.h" |
| 45 #include "platform/fonts/shaping/ShapeResultInlineHeaders.h" | 44 #include "platform/fonts/shaping/ShapeResultInlineHeaders.h" |
| 46 #include "platform/text/Character.h" | |
| 47 #include "platform/text/TextBreakIterator.h" | 45 #include "platform/text/TextBreakIterator.h" |
| 48 #include "wtf/Compiler.h" | 46 #include "wtf/Compiler.h" |
| 49 #include "wtf/MathExtras.h" | 47 #include "wtf/MathExtras.h" |
| 50 #include "wtf/PtrUtil.h" | 48 #include "wtf/PtrUtil.h" |
| 51 #include "wtf/text/Unicode.h" | 49 #include "wtf/text/Unicode.h" |
| 52 #include <algorithm> | 50 #include <algorithm> |
| 53 #include <hb.h> | 51 #include <hb.h> |
| 54 #include <memory> | 52 #include <memory> |
| 55 #include <unicode/uchar.h> | 53 #include <unicode/uchar.h> |
| 56 #include <unicode/uscript.h> | 54 #include <unicode/uscript.h> |
| (...skipping 19 matching lines...) Expand all Loading... |
| 76 } | 74 } |
| 77 | 75 |
| 78 T* get() { return m_ptr; } | 76 T* get() { return m_ptr; } |
| 79 void set(T* ptr) { m_ptr = ptr; } | 77 void set(T* ptr) { m_ptr = ptr; } |
| 80 | 78 |
| 81 private: | 79 private: |
| 82 T* m_ptr; | 80 T* m_ptr; |
| 83 DestroyFunction m_destroy; | 81 DestroyFunction m_destroy; |
| 84 }; | 82 }; |
| 85 | 83 |
| 86 static void normalizeCharacters(const TextRun& run, | 84 HarfBuzzShaper::HarfBuzzShaper(const UChar* text, |
| 87 unsigned length, | 85 unsigned length, |
| 88 UChar* destination, | 86 TextDirection direction) |
| 89 unsigned* destinationLength) { | 87 : m_normalizedBuffer(text), |
| 90 unsigned position = 0; | 88 m_normalizedBufferLength(length), |
| 91 bool error = false; | 89 m_textDirection(direction) {} |
| 92 const UChar* source; | |
| 93 String stringFor8BitRun; | |
| 94 if (run.is8Bit()) { | |
| 95 stringFor8BitRun = | |
| 96 String::make16BitFrom8BitSource(run.characters8(), run.length()); | |
| 97 source = stringFor8BitRun.characters16(); | |
| 98 } else { | |
| 99 source = run.characters16(); | |
| 100 } | |
| 101 | |
| 102 *destinationLength = 0; | |
| 103 while (position < length) { | |
| 104 UChar32 character; | |
| 105 U16_NEXT(source, position, length, character); | |
| 106 // Don't normalize tabs as they are not treated as spaces for word-end. | |
| 107 if (run.normalizeSpace() && | |
| 108 Character::isNormalizedCanvasSpaceCharacter(character)) { | |
| 109 character = spaceCharacter; | |
| 110 } else if (Character::treatAsSpace(character) && | |
| 111 character != noBreakSpaceCharacter) { | |
| 112 character = spaceCharacter; | |
| 113 } else if (!RuntimeEnabledFeatures:: | |
| 114 renderUnicodeControlCharactersEnabled() && | |
| 115 Character::legacyTreatAsZeroWidthSpaceInComplexScript( | |
| 116 character)) { | |
| 117 character = zeroWidthSpaceCharacter; | |
| 118 } else if (Character::treatAsZeroWidthSpaceInComplexScript(character)) { | |
| 119 character = zeroWidthSpaceCharacter; | |
| 120 } | |
| 121 | |
| 122 U16_APPEND(destination, *destinationLength, length, character, error); | |
| 123 DCHECK(!error); | |
| 124 } | |
| 125 } | |
| 126 | |
| 127 HarfBuzzShaper::HarfBuzzShaper(const TextRun& run) | |
| 128 : m_textRun(run), m_normalizedBufferLength(0) { | |
| 129 m_normalizedBuffer = wrapArrayUnique(new UChar[m_textRun.length() + 1]); | |
| 130 normalizeCharacters(m_textRun, m_textRun.length(), m_normalizedBuffer.get(), | |
| 131 &m_normalizedBufferLength); | |
| 132 } | |
| 133 | 90 |
| 134 namespace { | 91 namespace { |
| 135 | 92 |
| 136 // A port of hb_icu_script_to_script because harfbuzz on CrOS is built | 93 // A port of hb_icu_script_to_script because harfbuzz on CrOS is built |
| 137 // without hb-icu. See http://crbug.com/356929 | 94 // without hb-icu. See http://crbug.com/356929 |
| 138 static inline hb_script_t ICUScriptToHBScript(UScriptCode script) { | 95 static inline hb_script_t ICUScriptToHBScript(UScriptCode script) { |
| 139 if (UNLIKELY(script == USCRIPT_INVALID_CODE)) | 96 if (UNLIKELY(script == USCRIPT_INVALID_CODE)) |
| 140 return HB_SCRIPT_INVALID; | 97 return HB_SCRIPT_INVALID; |
| 141 | 98 |
| 142 return hb_script_from_string(uscript_getShortName(script), -1); | 99 return hb_script_from_string(uscript_getShortName(script), -1); |
| (...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 296 | 253 |
| 297 // If numCharacters is 0, that means we hit a NotDef before shaping the | 254 // If numCharacters is 0, that means we hit a NotDef before shaping the |
| 298 // whole grapheme. We do not append it here. For the next glyph we | 255 // whole grapheme. We do not append it here. For the next glyph we |
| 299 // encounter, atChange will be true, and the characters corresponding to | 256 // encounter, atChange will be true, and the characters corresponding to |
| 300 // the grapheme will be added to the TODO queue again, attempting to | 257 // the grapheme will be added to the TODO queue again, attempting to |
| 301 // shape the whole grapheme with the next font. | 258 // shape the whole grapheme with the next font. |
| 302 // When we're getting here with the last resort font, we have no other | 259 // When we're getting here with the last resort font, we have no other |
| 303 // choice than adding boxes to the ShapeResult. | 260 // choice than adding boxes to the ShapeResult. |
| 304 if ((currentClusterResult == NotDef && numCharacters) || isLastResort) { | 261 if ((currentClusterResult == NotDef && numCharacters) || isLastResort) { |
| 305 hb_direction_t direction = TextDirectionToHBDirection( | 262 hb_direction_t direction = TextDirectionToHBDirection( |
| 306 m_textRun.direction(), font->getFontDescription().orientation(), | 263 m_textDirection, font->getFontDescription().orientation(), |
| 307 currentFont); | 264 currentFont); |
| 308 // Here we need to specify glyph positions. | 265 // Here we need to specify glyph positions. |
| 309 ShapeResult::RunInfo* run = new ShapeResult::RunInfo( | 266 ShapeResult::RunInfo* run = new ShapeResult::RunInfo( |
| 310 currentFont, direction, ICUScriptToHBScript(currentRunScript), | 267 currentFont, direction, ICUScriptToHBScript(currentRunScript), |
| 311 startIndex, numGlyphsToInsert, numCharacters); | 268 startIndex, numGlyphsToInsert, numCharacters); |
| 312 shapeResult->insertRun(wrapUnique(run), lastChangePosition, | 269 shapeResult->insertRun(wrapUnique(run), lastChangePosition, |
| 313 numGlyphsToInsert, harfBuzzBuffer); | 270 numGlyphsToInsert, harfBuzzBuffer); |
| 314 } | 271 } |
| 315 lastChangePosition = glyphIndex; | 272 lastChangePosition = glyphIndex; |
| 316 } | 273 } |
| (...skipping 24 matching lines...) Expand all Loading... |
| 341 hint.clear(); | 298 hint.clear(); |
| 342 | 299 |
| 343 size_t numCharsAdded = 0; | 300 size_t numCharsAdded = 0; |
| 344 for (auto it = holesQueue.begin(); it != holesQueue.end(); ++it) { | 301 for (auto it = holesQueue.begin(); it != holesQueue.end(); ++it) { |
| 345 if (it->m_action == HolesQueueNextFont) | 302 if (it->m_action == HolesQueueNextFont) |
| 346 break; | 303 break; |
| 347 | 304 |
| 348 UChar32 hintChar; | 305 UChar32 hintChar; |
| 349 RELEASE_ASSERT(it->m_startIndex + it->m_numCharacters <= | 306 RELEASE_ASSERT(it->m_startIndex + it->m_numCharacters <= |
| 350 m_normalizedBufferLength); | 307 m_normalizedBufferLength); |
| 351 UTF16TextIterator iterator(m_normalizedBuffer.get() + it->m_startIndex, | 308 UTF16TextIterator iterator(m_normalizedBuffer + it->m_startIndex, |
| 352 it->m_numCharacters); | 309 it->m_numCharacters); |
| 353 while (iterator.consume(hintChar)) { | 310 while (iterator.consume(hintChar)) { |
| 354 hint.append(hintChar); | 311 hint.append(hintChar); |
| 355 numCharsAdded++; | 312 numCharsAdded++; |
| 356 iterator.advance(); | 313 iterator.advance(); |
| 357 } | 314 } |
| 358 } | 315 } |
| 359 return numCharsAdded > 0; | 316 return numCharsAdded > 0; |
| 360 } | 317 } |
| 361 | 318 |
| (...skipping 228 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 590 m_countFeatures++; | 547 m_countFeatures++; |
| 591 } | 548 } |
| 592 | 549 |
| 593 CapsFeatureSettingsScopedOverlay::~CapsFeatureSettingsScopedOverlay() { | 550 CapsFeatureSettingsScopedOverlay::~CapsFeatureSettingsScopedOverlay() { |
| 594 m_features->remove(0, m_countFeatures); | 551 m_features->remove(0, m_countFeatures); |
| 595 } | 552 } |
| 596 | 553 |
| 597 } // namespace | 554 } // namespace |
| 598 | 555 |
| 599 PassRefPtr<ShapeResult> HarfBuzzShaper::shapeResult(const Font* font) const { | 556 PassRefPtr<ShapeResult> HarfBuzzShaper::shapeResult(const Font* font) const { |
| 600 RefPtr<ShapeResult> result = ShapeResult::create( | 557 RefPtr<ShapeResult> result = |
| 601 font, m_normalizedBufferLength, m_textRun.direction()); | 558 ShapeResult::create(font, m_normalizedBufferLength, m_textDirection); |
| 602 HarfBuzzScopedPtr<hb_buffer_t> harfBuzzBuffer(hb_buffer_create(), | 559 HarfBuzzScopedPtr<hb_buffer_t> harfBuzzBuffer(hb_buffer_create(), |
| 603 hb_buffer_destroy); | 560 hb_buffer_destroy); |
| 604 FeaturesVector fontFeatures; | 561 FeaturesVector fontFeatures; |
| 605 setFontFeatures(font, &fontFeatures); | 562 setFontFeatures(font, &fontFeatures); |
| 606 const FontDescription& fontDescription = font->getFontDescription(); | 563 const FontDescription& fontDescription = font->getFontDescription(); |
| 607 const hb_language_t language = | 564 const hb_language_t language = |
| 608 fontDescription.localeOrDefault().harfbuzzLanguage(); | 565 fontDescription.localeOrDefault().harfbuzzLanguage(); |
| 609 | 566 |
| 610 bool needsCapsHandling = | 567 bool needsCapsHandling = |
| 611 fontDescription.variantCaps() != FontDescription::CapsNormal; | 568 fontDescription.variantCaps() != FontDescription::CapsNormal; |
| 612 OpenTypeCapsSupport capsSupport; | 569 OpenTypeCapsSupport capsSupport; |
| 613 FontOrientation orientation = font->getFontDescription().orientation(); | 570 FontOrientation orientation = font->getFontDescription().orientation(); |
| 614 | 571 |
| 615 RunSegmenter::RunSegmenterRange segmentRange = { | 572 RunSegmenter::RunSegmenterRange segmentRange = { |
| 616 0, 0, USCRIPT_INVALID_CODE, OrientationIterator::OrientationInvalid, | 573 0, 0, USCRIPT_INVALID_CODE, OrientationIterator::OrientationInvalid, |
| 617 FontFallbackPriority::Invalid}; | 574 FontFallbackPriority::Invalid}; |
| 618 RunSegmenter runSegmenter(m_normalizedBuffer.get(), m_normalizedBufferLength, | 575 RunSegmenter runSegmenter(m_normalizedBuffer, m_normalizedBufferLength, |
| 619 orientation); | 576 orientation); |
| 620 | 577 |
| 621 Vector<UChar32> fallbackCharsHint; | 578 Vector<UChar32> fallbackCharsHint; |
| 622 | 579 |
| 623 // TODO: Check whether this treatAsZerowidthspace from the previous script | 580 // TODO: Check whether this treatAsZerowidthspace from the previous script |
| 624 // segmentation plays a role here, does the new scriptRuniterator handle that | 581 // segmentation plays a role here, does the new scriptRuniterator handle that |
| 625 // correctly? | 582 // correctly? |
| 626 Deque<HolesQueueItem> holesQueue; | 583 Deque<HolesQueueItem> holesQueue; |
| 627 while (runSegmenter.consume(&segmentRange)) { | 584 while (runSegmenter.consume(&segmentRange)) { |
| 628 RefPtr<FontFallbackIterator> fallbackIterator = | 585 RefPtr<FontFallbackIterator> fallbackIterator = |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 663 | 620 |
| 664 const SimpleFontData* fontData = currentFontDataForRangeSet->fontData(); | 621 const SimpleFontData* fontData = currentFontDataForRangeSet->fontData(); |
| 665 SmallCapsIterator::SmallCapsBehavior smallCapsBehavior = | 622 SmallCapsIterator::SmallCapsBehavior smallCapsBehavior = |
| 666 SmallCapsIterator::SmallCapsSameCase; | 623 SmallCapsIterator::SmallCapsSameCase; |
| 667 if (needsCapsHandling) { | 624 if (needsCapsHandling) { |
| 668 capsSupport = | 625 capsSupport = |
| 669 OpenTypeCapsSupport(fontData->platformData().harfBuzzFace(), | 626 OpenTypeCapsSupport(fontData->platformData().harfBuzzFace(), |
| 670 fontDescription.variantCaps(), | 627 fontDescription.variantCaps(), |
| 671 ICUScriptToHBScript(segmentRange.script)); | 628 ICUScriptToHBScript(segmentRange.script)); |
| 672 if (capsSupport.needsRunCaseSplitting()) { | 629 if (capsSupport.needsRunCaseSplitting()) { |
| 673 splitUntilNextCaseChange(m_normalizedBuffer.get(), &holesQueue, | 630 splitUntilNextCaseChange(m_normalizedBuffer, &holesQueue, |
| 674 currentQueueItem, smallCapsBehavior); | 631 currentQueueItem, smallCapsBehavior); |
| 675 } | 632 } |
| 676 } | 633 } |
| 677 | 634 |
| 678 ASSERT(currentQueueItem.m_numCharacters); | 635 ASSERT(currentQueueItem.m_numCharacters); |
| 679 | 636 |
| 680 const SimpleFontData* smallcapsAdjustedFont = | 637 const SimpleFontData* smallcapsAdjustedFont = |
| 681 needsCapsHandling && capsSupport.needsSyntheticFont(smallCapsBehavior) | 638 needsCapsHandling && capsSupport.needsSyntheticFont(smallCapsBehavior) |
| 682 ? fontData->smallCapsFontData(fontDescription).get() | 639 ? fontData->smallCapsFontData(fontDescription).get() |
| 683 : fontData; | 640 : fontData; |
| 684 | 641 |
| 685 // Compatibility with SimpleFontData approach of keeping a flag for | 642 // Compatibility with SimpleFontData approach of keeping a flag for |
| 686 // overriding drawing direction. | 643 // overriding drawing direction. |
| 687 // TODO: crbug.com/506224 This should go away in favor of storing that | 644 // TODO: crbug.com/506224 This should go away in favor of storing that |
| 688 // information elsewhere, for example in ShapeResult. | 645 // information elsewhere, for example in ShapeResult. |
| 689 const SimpleFontData* directionAndSmallCapsAdjustedFont = | 646 const SimpleFontData* directionAndSmallCapsAdjustedFont = |
| 690 fontDataAdjustedForOrientation(smallcapsAdjustedFont, orientation, | 647 fontDataAdjustedForOrientation(smallcapsAdjustedFont, orientation, |
| 691 segmentRange.renderOrientation); | 648 segmentRange.renderOrientation); |
| 692 | 649 |
| 693 CaseMapIntend caseMapIntend = CaseMapIntend::KeepSameCase; | 650 CaseMapIntend caseMapIntend = CaseMapIntend::KeepSameCase; |
| 694 if (needsCapsHandling) | 651 if (needsCapsHandling) |
| 695 caseMapIntend = capsSupport.needsCaseChange(smallCapsBehavior); | 652 caseMapIntend = capsSupport.needsCaseChange(smallCapsBehavior); |
| 696 | 653 |
| 697 CaseMappingHarfBuzzBufferFiller( | 654 CaseMappingHarfBuzzBufferFiller( |
| 698 caseMapIntend, fontDescription.localeOrDefault(), | 655 caseMapIntend, fontDescription.localeOrDefault(), |
| 699 harfBuzzBuffer.get(), m_normalizedBuffer.get(), | 656 harfBuzzBuffer.get(), m_normalizedBuffer, m_normalizedBufferLength, |
| 700 m_normalizedBufferLength, currentQueueItem.m_startIndex, | 657 currentQueueItem.m_startIndex, currentQueueItem.m_numCharacters); |
| 701 currentQueueItem.m_numCharacters); | |
| 702 | 658 |
| 703 CapsFeatureSettingsScopedOverlay capsOverlay( | 659 CapsFeatureSettingsScopedOverlay capsOverlay( |
| 704 &fontFeatures, capsSupport.fontFeatureToUse(smallCapsBehavior)); | 660 &fontFeatures, capsSupport.fontFeatureToUse(smallCapsBehavior)); |
| 705 | 661 |
| 706 hb_direction_t direction = | 662 hb_direction_t direction = TextDirectionToHBDirection( |
| 707 TextDirectionToHBDirection(m_textRun.direction(), orientation, | 663 m_textDirection, orientation, directionAndSmallCapsAdjustedFont); |
| 708 directionAndSmallCapsAdjustedFont); | |
| 709 | 664 |
| 710 if (!shapeRange(harfBuzzBuffer.get(), | 665 if (!shapeRange(harfBuzzBuffer.get(), |
| 711 fontFeatures.isEmpty() ? 0 : fontFeatures.data(), | 666 fontFeatures.isEmpty() ? 0 : fontFeatures.data(), |
| 712 fontFeatures.size(), directionAndSmallCapsAdjustedFont, | 667 fontFeatures.size(), directionAndSmallCapsAdjustedFont, |
| 713 currentFontDataForRangeSet->ranges(), segmentRange.script, | 668 currentFontDataForRangeSet->ranges(), segmentRange.script, |
| 714 direction, language)) | 669 direction, language)) |
| 715 DLOG(ERROR) << "Shaping range failed."; | 670 DLOG(ERROR) << "Shaping range failed."; |
| 716 | 671 |
| 717 if (!extractShapeResults( | 672 if (!extractShapeResults( |
| 718 harfBuzzBuffer.get(), result.get(), fontCycleQueued, &holesQueue, | 673 harfBuzzBuffer.get(), result.get(), fontCycleQueued, &holesQueue, |
| 719 currentQueueItem, font, directionAndSmallCapsAdjustedFont, | 674 currentQueueItem, font, directionAndSmallCapsAdjustedFont, |
| 720 segmentRange.script, !fallbackIterator->hasNext())) | 675 segmentRange.script, !fallbackIterator->hasNext())) |
| 721 DLOG(ERROR) << "Shape result extraction failed."; | 676 DLOG(ERROR) << "Shape result extraction failed."; |
| 722 | 677 |
| 723 hb_buffer_reset(harfBuzzBuffer.get()); | 678 hb_buffer_reset(harfBuzzBuffer.get()); |
| 724 } | 679 } |
| 725 } | 680 } |
| 726 return result.release(); | 681 return result.release(); |
| 727 } | 682 } |
| 728 | 683 |
| 729 } // namespace blink | 684 } // namespace blink |
| OLD | NEW |