| 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 <hb.h> |
| 35 #include <unicode/uchar.h> |
| 36 #include <unicode/uscript.h> |
| 37 #include <algorithm> |
| 38 #include <memory> |
| 39 #include "platform/LayoutUnit.h" |
| 34 #include "platform/fonts/Font.h" | 40 #include "platform/fonts/Font.h" |
| 35 #include "platform/fonts/FontDescription.h" | 41 #include "platform/fonts/FontDescription.h" |
| 36 #include "platform/fonts/FontFallbackIterator.h" | 42 #include "platform/fonts/FontFallbackIterator.h" |
| 37 #include "platform/fonts/SmallCapsIterator.h" | 43 #include "platform/fonts/SmallCapsIterator.h" |
| 38 #include "platform/fonts/UTF16TextIterator.h" | 44 #include "platform/fonts/UTF16TextIterator.h" |
| 39 #include "platform/fonts/opentype/OpenTypeCapsSupport.h" | 45 #include "platform/fonts/opentype/OpenTypeCapsSupport.h" |
| 40 #include "platform/fonts/shaping/CaseMappingHarfBuzzBufferFiller.h" | 46 #include "platform/fonts/shaping/CaseMappingHarfBuzzBufferFiller.h" |
| 41 #include "platform/fonts/shaping/HarfBuzzFace.h" | 47 #include "platform/fonts/shaping/HarfBuzzFace.h" |
| 42 #include "platform/fonts/shaping/ShapeResultInlineHeaders.h" | 48 #include "platform/fonts/shaping/ShapeResultInlineHeaders.h" |
| 43 #include "platform/text/TextBreakIterator.h" | 49 #include "platform/text/TextBreakIterator.h" |
| 44 #include "wtf/Compiler.h" | 50 #include "wtf/Compiler.h" |
| 45 #include "wtf/MathExtras.h" | 51 #include "wtf/MathExtras.h" |
| 46 #include "wtf/PtrUtil.h" | 52 #include "wtf/PtrUtil.h" |
| 47 #include "wtf/text/Unicode.h" | 53 #include "wtf/text/Unicode.h" |
| 48 #include <algorithm> | |
| 49 #include <hb.h> | |
| 50 #include <memory> | |
| 51 #include <unicode/uchar.h> | |
| 52 #include <unicode/uscript.h> | |
| 53 | 54 |
| 54 namespace blink { | 55 namespace blink { |
| 55 enum HolesQueueItemAction { HolesQueueNextFont, HolesQueueRange }; | 56 enum HolesQueueItemAction { HolesQueueNextFont, HolesQueueRange }; |
| 56 | 57 |
| 57 struct HolesQueueItem { | 58 struct HolesQueueItem { |
| 58 DISALLOW_NEW_EXCEPT_PLACEMENT_NEW(); | 59 DISALLOW_NEW_EXCEPT_PLACEMENT_NEW(); |
| 59 HolesQueueItemAction m_action; | 60 HolesQueueItemAction m_action; |
| 60 unsigned m_startIndex; | 61 unsigned m_startIndex; |
| 61 unsigned m_numCharacters; | 62 unsigned m_numCharacters; |
| 62 HolesQueueItem(HolesQueueItemAction action, unsigned start, unsigned num) | 63 HolesQueueItem(HolesQueueItemAction action, unsigned start, unsigned num) |
| (...skipping 637 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 700 if (start < segmentRange.end && end > segmentRange.start) | 701 if (start < segmentRange.end && end > segmentRange.start) |
| 701 shapeSegment(&rangeData, segmentRange, result.get()); | 702 shapeSegment(&rangeData, segmentRange, result.get()); |
| 702 } | 703 } |
| 703 return result.release(); | 704 return result.release(); |
| 704 } | 705 } |
| 705 | 706 |
| 706 PassRefPtr<ShapeResult> HarfBuzzShaper::shape(const Font* font, | 707 PassRefPtr<ShapeResult> HarfBuzzShaper::shape(const Font* font, |
| 707 TextDirection direction) const { | 708 TextDirection direction) const { |
| 708 return shape(font, direction, 0, m_textLength); | 709 return shape(font, direction, 0, m_textLength); |
| 709 } | 710 } |
| 711 |
| 712 namespace { |
| 713 |
| 714 unsigned previousBreakOpportunity(unsigned start, |
| 715 unsigned offset, |
| 716 unsigned textLength, |
| 717 LazyLineBreakIterator& breakIterator, |
| 718 LineBreakType breakType) { |
| 719 unsigned pos = std::min(start + offset, textLength); |
| 720 for (; pos > start; pos--) { |
| 721 int nextBreak = 0; |
| 722 if (breakIterator.isBreakable(pos, nextBreak, breakType)) |
| 723 return pos; |
| 724 } |
| 725 return start; |
| 726 } |
| 727 |
| 728 unsigned nextBreakOpportunity(unsigned offset, |
| 729 LazyLineBreakIterator& breakIterator, |
| 730 LineBreakType breakType) { |
| 731 int nextBreak = 0; |
| 732 breakIterator.isBreakable(offset, nextBreak, breakType); |
| 733 return nextBreak; |
| 734 } |
| 735 |
| 736 unsigned previousSafeToBreakAfter(const UChar* text, |
| 737 unsigned start, |
| 738 unsigned offset) { |
| 739 // TODO: This should use HarfBuzz safe to break info when available. For now |
| 740 // it'll scan back until the previous space character or something. |
| 741 for (; offset > start; offset--) { |
| 742 if (text[offset - 1] == spaceCharacter) |
| 743 break; |
| 744 } |
| 745 return offset; |
| 746 } |
| 747 |
| 748 unsigned nextSafeToBreakBefore(const UChar* text, |
| 749 unsigned end, |
| 750 unsigned offset) { |
| 751 for (; offset < end; offset++) { |
| 752 if (text[offset] == spaceCharacter) |
| 753 break; |
| 754 } |
| 755 return offset; |
| 756 } |
| 757 |
| 758 } // namespace |
| 759 |
| 760 // Shapes a line of text by finding a valid and appropriate break opportunity |
| 761 // based on the shaping results for the entire paragraph. Re-shapes the start |
| 762 // and end of the line as needed. |
| 763 // |
| 764 // Definitions: |
| 765 // Candidate break opportunity: Ideal point to break, disregarding line |
| 766 // breaking rules. May be in the middle of a word |
| 767 // or inside a ligature. |
| 768 // Valid break opportunity: A point where a break is allowed according to |
| 769 // the relevant breaking rules. |
| 770 // Safe-to-break: A point where a break may occur without |
| 771 // affecting the rendering or metrics of the |
| 772 // text. Breaking at safe-to-break point does not |
| 773 // require reshaping. |
| 774 // |
| 775 // For example: |
| 776 // Given the string "Line breaking example", an available space of 100px and a |
| 777 // mono-space font where each glyph is 10px wide. |
| 778 // |
| 779 // Line breaking example |
| 780 // | | |
| 781 // 0 100px |
| 782 // |
| 783 // The candidate (or ideal) break opportunity would be at an offset of 10 as |
| 784 // the break would happen at exactly 100px in that case. |
| 785 // The previous valid break opportunity though is at an offset of 5. |
| 786 // If we further assume that the font kerns with space then even though it's a |
| 787 // valid break opportunity reshaping is required as the combined width of the |
| 788 // two segments "Line " and "breaking" may be different from "Line breaking". |
| 789 PassRefPtr<ShapeResult> HarfBuzzShaper::shapeLine(const Font* font, |
| 790 const ShapeResult* result, |
| 791 unsigned startOffset, |
| 792 const AtomicString locale, |
| 793 LineBreakType breakType, |
| 794 LayoutUnit availableSpace, |
| 795 unsigned* breakOffset) const { |
| 796 // The start position in the original shape results. |
| 797 LayoutUnit startPosition = result->snappedStartPositionForOffset(startOffset); |
| 798 |
| 799 // If the start offset is not at a safe-to-break boundary the content between |
| 800 // the start and the next safe-to-break boundary needs to be reshaped and the |
| 801 // available space adjusted to take the reshaping into account. |
| 802 RefPtr<ShapeResult> lineStartResult; |
| 803 unsigned firstSafe = nextSafeToBreakBefore(m_text, m_textLength, startOffset); |
| 804 if (firstSafe != startOffset) { |
| 805 LayoutUnit originalWidth = |
| 806 result->snappedEndPositionForOffset(firstSafe) - startPosition; |
| 807 lineStartResult = shape(font, result->direction(), startOffset, firstSafe); |
| 808 availableSpace += lineStartResult->snappedWidth() - originalWidth; |
| 809 } |
| 810 |
| 811 // Find a candidate break opportunity by identifying the last offset before |
| 812 // exceeding the available space and the determine the closest valid break |
| 813 // preceding the candidate. |
| 814 LazyLineBreakIterator breakIterator(String(m_text, m_textLength), locale); |
| 815 LayoutUnit endPosition = startPosition + availableSpace; |
| 816 unsigned candidateBreak = result->offsetForPosition(endPosition, false); |
| 817 unsigned breakOpportunity = previousBreakOpportunity( |
| 818 startOffset, candidateBreak, m_textLength, breakIterator, breakType); |
| 819 if (breakOpportunity <= startOffset) { |
| 820 breakOpportunity = |
| 821 nextBreakOpportunity(candidateBreak, breakIterator, breakType); |
| 822 } |
| 823 |
| 824 RefPtr<ShapeResult> lineEndResult; |
| 825 unsigned lastSafe = breakOpportunity; |
| 826 while (breakOpportunity > startOffset) { |
| 827 // If the previous valid break opportunity is not at a safe-to-break |
| 828 // boundary reshape between the safe-to-break offset and the valid break |
| 829 // offset. If the resulting width exceeds the available space the |
| 830 // preceding boundary is tried until the available space is sufficient. |
| 831 unsigned previousSafe = std::max( |
| 832 previousSafeToBreakAfter(m_text, startOffset, breakOpportunity), |
| 833 startOffset); |
| 834 if (previousSafe != breakOpportunity) { |
| 835 LayoutUnit safePosition = |
| 836 result->snappedStartPositionForOffset(previousSafe); |
| 837 while (breakOpportunity > previousSafe && previousSafe > startOffset) { |
| 838 lineEndResult = |
| 839 shape(font, result->direction(), previousSafe, breakOpportunity); |
| 840 if (safePosition + lineEndResult->snappedWidth() <= endPosition) |
| 841 break; |
| 842 lineEndResult = nullptr; |
| 843 breakOpportunity = |
| 844 previousBreakOpportunity(startOffset, breakOpportunity - 1, |
| 845 m_textLength, breakIterator, breakType); |
| 846 } |
| 847 } |
| 848 |
| 849 if (breakOpportunity > startOffset) { |
| 850 lastSafe = previousSafe; |
| 851 break; |
| 852 } |
| 853 |
| 854 // No suitable break opportunity, not exceeding the available space, |
| 855 // found. Choose the next valid one even though it will overflow. |
| 856 breakOpportunity = |
| 857 nextBreakOpportunity(candidateBreak, breakIterator, breakType); |
| 858 } |
| 859 |
| 860 // Create shape results for the line by copying from the re-shaped result (if |
| 861 // reshaping was needed) and the original shape results. |
| 862 RefPtr<ShapeResult> lineResult = |
| 863 ShapeResult::create(font, 0, result->direction()); |
| 864 unsigned maxLength = std::numeric_limits<unsigned>::max(); |
| 865 if (lineStartResult) |
| 866 lineStartResult->copyRange(0, maxLength, lineResult.get()); |
| 867 if (lastSafe > firstSafe) |
| 868 result->copyRange(firstSafe, lastSafe, lineResult.get()); |
| 869 if (lineEndResult) |
| 870 lineEndResult->copyRange(lastSafe, maxLength, lineResult.get()); |
| 871 |
| 872 *breakOffset = breakOpportunity; |
| 873 return lineResult.release(); |
| 874 } |
| 875 |
| 710 } // namespace blink | 876 } // namespace blink |
| OLD | NEW |