+2007-12-11 Dan Bernstein <mitz@apple.com>
+
+ Reviewed by Darin Adler.
+
+ - test for <rdar://problem/5631507> Text doesn't wrap properly at Tamil version of Wikipedia
+
+ * fast/text/international/complex-character-based-fallback.html: Added.
+ * platform/mac-leopard/fast/text/international/complex-character-based-fallback-expected.checksum: Added.
+ * platform/mac-leopard/fast/text/international/complex-character-based-fallback-expected.png: Added.
+ * platform/mac/fast/text/international/complex-character-based-fallback-expected.txt: Added.
+ * platform/win/fast/text: Added.
+ * platform/win/fast/text/international: Added.
+ * platform/win/fast/text/international/complex-character-based-fallback-expected.checksum: Added.
+ * platform/win/fast/text/international/complex-character-based-fallback-expected.png: Added.
+ * platform/win/fast/text/international/complex-character-based-fallback-expected.txt: Added.
+
2007-12-07 Alexey Proskuryakov <ap@webkit.org>
Reviewed by Darin.
--- /dev/null
+c82a724bccc52e6dedf03f0c9feb8876
\ No newline at end of file
--- /dev/null
+layer at (0,0) size 800x600
+ RenderView at (0,0) size 800x600
+layer at (0,0) size 800x600
+ RenderBlock {HTML} at (0,0) size 800x600
+ RenderBody {BODY} at (8,8) size 784x576
+ RenderBlock {P} at (0,0) size 784x36
+ RenderText {#text} at (0,0) size 780x36
+ text run at (0,0) width 780: "This tests that complex text that requires font fallback wraps correctly. The text is Tamil, which is not covered by the default"
+ text run at (0,18) width 421: "font, so fallback occurs. Note that the exclamation point in the end "
+ RenderInline {I} at (0,0) size 10x18
+ RenderText {#text} at (421,18) size 10x18
+ text run at (421,18) width 10: "is"
+ RenderText {#text} at (431,18) size 327x18
+ text run at (431,18) width 327: " covered by the default font and is rendered using it."
+ RenderBlock {P} at (0,52) size 306x132 [border: (3px solid #000000)]
+ RenderText {#text} at (3,3) size 285x126
+ text run at (3,3) width 159: "\x{B87}\x{BA4}\x{BC1} \x{B89}\x{B99}\x{BCD}\x{B95}\x{BB3}\x{BC8}\x{BAA}\x{BCD} \x{BAA}\x{BCB}\x{BA9}\x{BCD}\x{BB1}"
+ text run at (3,21) width 255: "\x{B86}\x{BB0}\x{BCD}\x{BB5}\x{BAE}\x{BC1}\x{B9F}\x{BC8}\x{BAF}\x{BB5}\x{BB0}\x{BCD}\x{B95}\x{BB3}\x{BBE}\x{BB2}\x{BCD} \x{BA4}\x{BCA}\x{B95}\x{BC1}\x{B95}\x{BCD}\x{B95}\x{BAA}\x{BCD}\x{BAA}\x{B9F}\x{BCD}\x{B9F}\x{BC1}"
+ text run at (3,39) width 267: "\x{B95}\x{B9F}\x{BCD}\x{B9F}\x{BC1}\x{BAA}\x{BCD}\x{BAA}\x{BBE}\x{B9F}\x{BC1}\x{B95}\x{BB3}\x{BCD} \x{B8F}\x{BA4}\x{BC1}\x{BAE}\x{BCD} \x{B87}\x{BA9}\x{BCD}\x{BB1}\x{BBF} \x{B87}\x{BB2}\x{BB5}\x{B9A}\x{BAE}\x{BBE}\x{B95}\x{BAA}\x{BCD}"
+ text run at (3,57) width 285: "\x{BAA}\x{BAF}\x{BA9}\x{BCD}\x{BAA}\x{B9F}\x{BC1}\x{BA4}\x{BCD}\x{BA4}\x{B95}\x{BCD}\x{B95}\x{BC2}\x{B9F}\x{BBF}\x{BAF} \x{B92}\x{BB0}\x{BC1} \x{BAA}\x{BA9}\x{BCD}\x{BAE}\x{BCA}\x{BB4}\x{BBF}\x{B95}\x{BCD} \x{B95}\x{BB2}\x{BC8}\x{B95}\x{BCD}"
+ text run at (3,75) width 264: "\x{B95}\x{BB3}\x{B9E}\x{BCD}\x{B9A}\x{BBF}\x{BAF}\x{BA4}\x{BCD} \x{BA4}\x{BBF}\x{B9F}\x{BCD}\x{B9F}\x{BAE}\x{BBE}\x{B95}\x{BC1}\x{BAE}\x{BCD}. \x{B87}\x{B99}\x{BCD}\x{B95}\x{BC1} \x{BA8}\x{BC0}\x{B99}\x{BCD}\x{B95}\x{BB3}\x{BC1}\x{BAE}\x{BCD}"
+ text run at (3,93) width 253: "\x{B89}\x{B99}\x{BCD}\x{B95}\x{BB3}\x{BC1}\x{B95}\x{BCD}\x{B95}\x{BC1} \x{BB5}\x{BBF}\x{BB0}\x{BC1}\x{BAA}\x{BCD}\x{BAA}\x{BAE}\x{BBE}\x{BA9} \x{BA4}\x{BB2}\x{BC8}\x{BAA}\x{BCD}\x{BAA}\x{BC1}\x{B95}\x{BB3}\x{BBF}\x{BB2}\x{BCD}"
+ text run at (3,111) width 149: "\x{BAA}\x{BC1}\x{BA4}\x{BBF}\x{BA4}\x{BBE}\x{B95}\x{B95}\x{BCD} \x{B95}\x{B9F}\x{BCD}\x{B9F}\x{BC1}\x{BB0}\x{BC8}\x{B95}\x{BB3}\x{BCD}!"
+ RenderBlock {P} at (0,200) size 784x36
+ RenderText {#text} at (0,0) size 778x36
+ text run at (0,0) width 778: "This is the same text but specifying a font that covers Tamil. Note that the exclamation point is rendered using the same font"
+ text run at (0,18) width 67: "as the text."
+ RenderBlock {P} at (0,252) size 306x132 [border: (3px solid #000000)]
+ RenderText {#text} at (3,3) size 285x126
+ text run at (3,3) width 159: "\x{B87}\x{BA4}\x{BC1} \x{B89}\x{B99}\x{BCD}\x{B95}\x{BB3}\x{BC8}\x{BAA}\x{BCD} \x{BAA}\x{BCB}\x{BA9}\x{BCD}\x{BB1}"
+ text run at (3,21) width 255: "\x{B86}\x{BB0}\x{BCD}\x{BB5}\x{BAE}\x{BC1}\x{B9F}\x{BC8}\x{BAF}\x{BB5}\x{BB0}\x{BCD}\x{B95}\x{BB3}\x{BBE}\x{BB2}\x{BCD} \x{BA4}\x{BCA}\x{B95}\x{BC1}\x{B95}\x{BCD}\x{B95}\x{BAA}\x{BCD}\x{BAA}\x{B9F}\x{BCD}\x{B9F}\x{BC1}"
+ text run at (3,39) width 267: "\x{B95}\x{B9F}\x{BCD}\x{B9F}\x{BC1}\x{BAA}\x{BCD}\x{BAA}\x{BBE}\x{B9F}\x{BC1}\x{B95}\x{BB3}\x{BCD} \x{B8F}\x{BA4}\x{BC1}\x{BAE}\x{BCD} \x{B87}\x{BA9}\x{BCD}\x{BB1}\x{BBF} \x{B87}\x{BB2}\x{BB5}\x{B9A}\x{BAE}\x{BBE}\x{B95}\x{BAA}\x{BCD}"
+ text run at (3,57) width 285: "\x{BAA}\x{BAF}\x{BA9}\x{BCD}\x{BAA}\x{B9F}\x{BC1}\x{BA4}\x{BCD}\x{BA4}\x{B95}\x{BCD}\x{B95}\x{BC2}\x{B9F}\x{BBF}\x{BAF} \x{B92}\x{BB0}\x{BC1} \x{BAA}\x{BA9}\x{BCD}\x{BAE}\x{BCA}\x{BB4}\x{BBF}\x{B95}\x{BCD} \x{B95}\x{BB2}\x{BC8}\x{B95}\x{BCD}"
+ text run at (3,75) width 264: "\x{B95}\x{BB3}\x{B9E}\x{BCD}\x{B9A}\x{BBF}\x{BAF}\x{BA4}\x{BCD} \x{BA4}\x{BBF}\x{B9F}\x{BCD}\x{B9F}\x{BAE}\x{BBE}\x{B95}\x{BC1}\x{BAE}\x{BCD}. \x{B87}\x{B99}\x{BCD}\x{B95}\x{BC1} \x{BA8}\x{BC0}\x{B99}\x{BCD}\x{B95}\x{BB3}\x{BC1}\x{BAE}\x{BCD}"
+ text run at (3,93) width 253: "\x{B89}\x{B99}\x{BCD}\x{B95}\x{BB3}\x{BC1}\x{B95}\x{BCD}\x{B95}\x{BC1} \x{BB5}\x{BBF}\x{BB0}\x{BC1}\x{BAA}\x{BCD}\x{BAA}\x{BAE}\x{BBE}\x{BA9} \x{BA4}\x{BB2}\x{BC8}\x{BAA}\x{BCD}\x{BAA}\x{BC1}\x{B95}\x{BB3}\x{BBF}\x{BB2}\x{BCD}"
+ text run at (3,111) width 149: "\x{BAA}\x{BC1}\x{BA4}\x{BBF}\x{BA4}\x{BBE}\x{B95}\x{B95}\x{BCD} \x{B95}\x{B9F}\x{BCD}\x{B9F}\x{BC1}\x{BB0}\x{BC8}\x{B95}\x{BB3}\x{BCD}!"
--- /dev/null
+c729f453d98b9c42e954330ce984e837
\ No newline at end of file
--- /dev/null
+layer at (0,0) size 785x624
+ RenderView at (0,0) size 785x600
+layer at (0,0) size 785x624
+ RenderBlock {HTML} at (0,0) size 785x624
+ RenderBody {BODY} at (8,8) size 769x600
+ RenderBlock {P} at (0,0) size 769x54
+ RenderText {#text} at (0,0) size 733x36
+ text run at (0,0) width 733: "This tests that complex text that requires font fallback wraps correctly. The text is Tamil, which is not covered by the"
+ text run at (0,18) width 468: "default font, so fallback occurs. Note that the exclamation point in the end "
+ RenderInline {I} at (0,0) size 10x18
+ RenderText {#text} at (468,18) size 10x18
+ text run at (468,18) width 10: "is"
+ RenderText {#text} at (478,18) size 751x36
+ text run at (478,18) width 273: " covered by the default font and is rendered"
+ text run at (0,36) width 50: "using it."
+ RenderBlock {P} at (0,70) size 306x186 [border: (3px solid #000000)]
+ RenderText {#text} at (3,3) size 265x180
+ text run at (3,3) width 206: "\x{B87}\x{BA4}\x{BC1} \x{B89}\x{B99}\x{BCD}\x{B95}\x{BB3}\x{BC8}\x{BAA}\x{BCD} \x{BAA}\x{BCB}\x{BA9}\x{BCD}\x{BB1}"
+ text run at (3,21) width 209: "\x{B86}\x{BB0}\x{BCD}\x{BB5}\x{BAE}\x{BC1}\x{B9F}\x{BC8}\x{BAF}\x{BB5}\x{BB0}\x{BCD}\x{B95}\x{BB3}\x{BBE}\x{BB2}\x{BCD}"
+ text run at (3,39) width 265: "\x{BA4}\x{BCA}\x{B95}\x{BC1}\x{B95}\x{BCD}\x{B95}\x{BAA}\x{BCD}\x{BAA}\x{B9F}\x{BCD}\x{B9F}\x{BC1} \x{B95}\x{B9F}\x{BCD}\x{B9F}\x{BC1}\x{BAA}\x{BCD}\x{BAA}\x{BBE}\x{B9F}\x{BC1}\x{B95}\x{BB3}\x{BCD}"
+ text run at (3,57) width 222: "\x{B8F}\x{BA4}\x{BC1}\x{BAE}\x{BCD} \x{B87}\x{BA9}\x{BCD}\x{BB1}\x{BBF} \x{B87}\x{BB2}\x{BB5}\x{B9A}\x{BAE}\x{BBE}\x{B95}\x{BAA}\x{BCD}"
+ text run at (3,75) width 208: "\x{BAA}\x{BAF}\x{BA9}\x{BCD}\x{BAA}\x{B9F}\x{BC1}\x{BA4}\x{BCD}\x{BA4}\x{B95}\x{BCD}\x{B95}\x{BC2}\x{B9F}\x{BBF}\x{BAF} \x{B92}\x{BB0}\x{BC1}"
+ text run at (3,93) width 263: "\x{BAA}\x{BA9}\x{BCD}\x{BAE}\x{BCA}\x{BB4}\x{BBF}\x{B95}\x{BCD} \x{B95}\x{BB2}\x{BC8}\x{B95}\x{BCD} \x{B95}\x{BB3}\x{B9E}\x{BCD}\x{B9A}\x{BBF}\x{BAF}\x{BA4}\x{BCD}"
+ text run at (3,111) width 246: "\x{BA4}\x{BBF}\x{B9F}\x{BCD}\x{B9F}\x{BAE}\x{BBE}\x{B95}\x{BC1}\x{BAE}\x{BCD}. \x{B87}\x{B99}\x{BCD}\x{B95}\x{BC1} \x{BA8}\x{BC0}\x{B99}\x{BCD}\x{B95}\x{BB3}\x{BC1}\x{BAE}\x{BCD}"
+ text run at (3,129) width 213: "\x{B89}\x{B99}\x{BCD}\x{B95}\x{BB3}\x{BC1}\x{B95}\x{BCD}\x{B95}\x{BC1} \x{BB5}\x{BBF}\x{BB0}\x{BC1}\x{BAA}\x{BCD}\x{BAA}\x{BAE}\x{BBE}\x{BA9}"
+ text run at (3,147) width 207: "\x{BA4}\x{BB2}\x{BC8}\x{BAA}\x{BCD}\x{BAA}\x{BC1}\x{B95}\x{BB3}\x{BBF}\x{BB2}\x{BCD} \x{BAA}\x{BC1}\x{BA4}\x{BBF}\x{BA4}\x{BBE}\x{B95}\x{B95}\x{BCD}"
+ text run at (3,165) width 109: "\x{B95}\x{B9F}\x{BCD}\x{B9F}\x{BC1}\x{BB0}\x{BC8}\x{B95}\x{BB3}\x{BCD}!"
+ RenderBlock {P} at (0,272) size 769x36
+ RenderText {#text} at (0,0) size 749x36
+ text run at (0,0) width 749: "This is the same text but specifying a font that covers Tamil. Note that the exclamation point is rendered using the same"
+ text run at (0,18) width 96: "font as the text."
+ RenderBlock {P} at (0,324) size 306x276 [border: (3px solid #000000)]
+ RenderText {#text} at (3,3) size 273x270
+ text run at (3,3) width 216: "\x{B87}\x{BA4}\x{BC1} \x{B89}\x{B99}\x{BCD}\x{B95}\x{BB3}\x{BC8}\x{BAA}\x{BCD} \x{BAA}\x{BCB}\x{BA9}\x{BCD}\x{BB1}"
+ text run at (3,30) width 209: "\x{B86}\x{BB0}\x{BCD}\x{BB5}\x{BAE}\x{BC1}\x{B9F}\x{BC8}\x{BAF}\x{BB5}\x{BB0}\x{BCD}\x{B95}\x{BB3}\x{BBE}\x{BB2}\x{BCD}"
+ text run at (3,57) width 270: "\x{BA4}\x{BCA}\x{B95}\x{BC1}\x{B95}\x{BCD}\x{B95}\x{BAA}\x{BCD}\x{BAA}\x{B9F}\x{BCD}\x{B9F}\x{BC1} \x{B95}\x{B9F}\x{BCD}\x{B9F}\x{BC1}\x{BAA}\x{BCD}\x{BAA}\x{BBE}\x{B9F}\x{BC1}\x{B95}\x{BB3}\x{BCD}"
+ text run at (3,84) width 232: "\x{B8F}\x{BA4}\x{BC1}\x{BAE}\x{BCD} \x{B87}\x{BA9}\x{BCD}\x{BB1}\x{BBF} \x{B87}\x{BB2}\x{BB5}\x{B9A}\x{BAE}\x{BBE}\x{B95}\x{BAA}\x{BCD}"
+ text run at (3,111) width 213: "\x{BAA}\x{BAF}\x{BA9}\x{BCD}\x{BAA}\x{B9F}\x{BC1}\x{BA4}\x{BCD}\x{BA4}\x{B95}\x{BCD}\x{B95}\x{BC2}\x{B9F}\x{BBF}\x{BAF} \x{B92}\x{BB0}\x{BC1}"
+ text run at (3,138) width 273: "\x{BAA}\x{BA9}\x{BCD}\x{BAE}\x{BCA}\x{BB4}\x{BBF}\x{B95}\x{BCD} \x{B95}\x{BB2}\x{BC8}\x{B95}\x{BCD} \x{B95}\x{BB3}\x{B9E}\x{BCD}\x{B9A}\x{BBF}\x{BAF}\x{BA4}\x{BCD}"
+ text run at (3,165) width 256: "\x{BA4}\x{BBF}\x{B9F}\x{BCD}\x{B9F}\x{BAE}\x{BBE}\x{B95}\x{BC1}\x{BAE}\x{BCD}. \x{B87}\x{B99}\x{BCD}\x{B95}\x{BC1} \x{BA8}\x{BC0}\x{B99}\x{BCD}\x{B95}\x{BB3}\x{BC1}\x{BAE}\x{BCD}"
+ text run at (3,192) width 218: "\x{B89}\x{B99}\x{BCD}\x{B95}\x{BB3}\x{BC1}\x{B95}\x{BCD}\x{B95}\x{BC1} \x{BB5}\x{BBF}\x{BB0}\x{BC1}\x{BAA}\x{BCD}\x{BAA}\x{BAE}\x{BBE}\x{BA9}"
+ text run at (3,219) width 212: "\x{BA4}\x{BB2}\x{BC8}\x{BAA}\x{BCD}\x{BAA}\x{BC1}\x{B95}\x{BB3}\x{BBF}\x{BB2}\x{BCD} \x{BAA}\x{BC1}\x{BA4}\x{BBF}\x{BA4}\x{BBE}\x{B95}\x{B95}\x{BCD}"
+ text run at (3,246) width 109: "\x{B95}\x{B9F}\x{BCD}\x{B9F}\x{BC1}\x{BB0}\x{BC8}\x{B95}\x{BB3}\x{BCD}!"
+2007-12-11 Dan Bernstein <mitz@apple.com>
+
+ Reviewed by Darin Adler.
+
+ - fix <rdar://problem/5631507> Text doesn't wrap properly at Tamil version of Wikipedia
+
+ Test: fast/text/international/complex-character-based-fallback.html
+
+ * platform/graphics/Font.cpp:
+ (WebCore::Font::glyphDataForCharacter): Added a forceSmallCaps argument
+ that forces this function to use the small caps font. It is used for
+ combining marks that need to combine with a small cap.
+ * platform/graphics/Font.h:
+ * platform/win/UniscribeController.cpp:
+ (WebCore::UniscribeController::advance): Changed to split the string
+ into runs of characters that will be rendered using the same FontData.
+ This is done by calling glyphDataForCharacter() for each cahracter to
+ find the FontData it should be rendered with.
+ (WebCore::UniscribeController::itemizeShapeAndPlace): Added a fontData
+ argument that is passed on to shapeAndPlaceItem() instead of the
+ smallCaps argument.
+ (WebCore::UniscribeController::shapeAndPlaceItem): Added a fontData
+ argument and removed the font fallback logic from this function, as
+ it is now expected to be called with an item all of whose characters
+ can be rendered with the given fontData.
+ * platform/win/UniscribeController.h:
+
2007-12-07 Alexey Proskuryakov <ap@webkit.org>
Reviewed by Darin.
&& m_letterSpacing == other.m_letterSpacing
&& m_wordSpacing == other.m_wordSpacing;
}
-
-// FIXME: It is unfortunate that this function needs to be passed the original cluster.
-// It is only required for the platform's FontCache::getFontDataForCharacters(), and it means
-// that this function is not correct if it transforms the character to uppercase and calls
-// FontCache::getFontDataForCharacters() afterwards.
-const GlyphData& Font::glyphDataForCharacter(UChar32 c, bool mirror) const
+
+const GlyphData& Font::glyphDataForCharacter(UChar32 c, bool mirror, bool forceSmallCaps) const
{
- bool useSmallCapsFont = false;
+ bool useSmallCapsFont = forceSmallCaps;
if (m_fontDescription.smallCaps()) {
UChar32 upperC = Unicode::toUpper(c);
if (upperC != c) {
#if !PLATFORM(QT)
const FontData* primaryFont() const;
const FontData* fontDataAt(unsigned) const;
- const GlyphData& glyphDataForCharacter(UChar32, bool mirror) const;
+ const GlyphData& glyphDataForCharacter(UChar32, bool mirror, bool forceSmallCaps = false) const;
// Used for complex text, and does not utilize the glyph map cache.
const FontData* fontDataForCharacters(const UChar*, int length) const;
if (length <= 0)
return;
- // We break up itemization of the string if small caps is involved.
- // Adjust the characters to account for small caps if it is set.
- if (m_font.isSmallCaps()) {
- // FIXME: It's inconsistent that we use logical order when itemizing, since this
- // does not match normal RTL.
- Vector<UChar> smallCapsBuffer(length);
- memcpy(smallCapsBuffer.data(), cp, length * sizeof(UChar));
- bool isSmallCaps = false;
- unsigned indexOfCaseShift = m_run.rtl() ? length - 1 : 0;
- const UChar* curr = m_run.rtl() ? cp + length - 1: cp;
- const UChar* end = m_run.rtl() ? cp - 1: cp + length;
- while (curr != end) {
- int index = curr - cp;
- UChar c = smallCapsBuffer[index];
- UChar newC;
- curr = m_run.rtl() ? curr - 1 : curr + 1;
- if (U_GET_GC_MASK(c) & U_GC_M_MASK)
- continue;
- if (!u_isUUppercase(c) && (newC = u_toupper(c)) != c) {
- smallCapsBuffer[index] = newC;
- if (!isSmallCaps) {
- isSmallCaps = true;
- int itemStart = m_run.rtl() ? index : indexOfCaseShift;
- int itemLength = m_run.rtl() ? indexOfCaseShift - index : index - indexOfCaseShift;
- itemizeShapeAndPlace(smallCapsBuffer.data() + itemStart, itemLength, false, glyphBuffer);
- indexOfCaseShift = index;
- }
- } else if (isSmallCaps) {
- isSmallCaps = false;
- int itemStart = m_run.rtl() ? index : indexOfCaseShift;
- int itemLength = m_run.rtl() ? indexOfCaseShift - index : index - indexOfCaseShift;
- itemizeShapeAndPlace(smallCapsBuffer.data() + itemStart, itemLength, true, glyphBuffer);
- indexOfCaseShift = index;
- }
+ // We break up itemization of the string by fontData and (if needed) the use of small caps.
+
+ // FIXME: It's inconsistent that we use logical order when itemizing, since this
+ // does not match normal RTL.
+
+ // FIXME: This function should decode surrogate pairs. Currently it makes little difference that
+ // it does not because the font cache on Windows does not support non-BMP characters.
+ Vector<UChar, 256> smallCapsBuffer;
+ if (m_font.isSmallCaps())
+ smallCapsBuffer.resize(length);
+
+ unsigned indexOfFontTransition = m_run.rtl() ? length - 1 : 0;
+ const UChar* curr = m_run.rtl() ? cp + length - 1 : cp;
+ const UChar* end = m_run.rtl() ? cp - 1 : cp + length;
+
+ const FontData* fontData;
+ const FontData* nextFontData = m_font.glyphDataForCharacter(*curr, false).fontData;
+
+ UChar newC;
+
+ bool isSmallCaps;
+ bool nextIsSmallCaps = m_font.isSmallCaps() && !(U_GET_GC_MASK(*curr) & U_GC_M_MASK) && (newC = u_toupper(*curr)) != *curr;
+
+ if (nextIsSmallCaps)
+ smallCapsBuffer[curr - cp] = newC;
+
+ while (true) {
+ curr = m_run.rtl() ? curr - 1 : curr + 1;
+ if (curr == end)
+ break;
+
+ fontData = nextFontData;
+ isSmallCaps = nextIsSmallCaps;
+ int index = curr - cp;
+ UChar c = *curr;
+
+ bool forceSmallCaps = isSmallCaps && (U_GET_GC_MASK(c) & U_GC_M_MASK);
+ nextFontData = m_font.glyphDataForCharacter(*curr, false, forceSmallCaps).fontData;
+ if (m_font.isSmallCaps()) {
+ nextIsSmallCaps = forceSmallCaps || (newC = u_toupper(c)) != c;
+ if (nextIsSmallCaps)
+ smallCapsBuffer[index] = forceSmallCaps ? c : newC;
}
-
- int itemLength = m_run.rtl() ? indexOfCaseShift + 1 : length - indexOfCaseShift;
- if (itemLength) {
- int itemStart = m_run.rtl() ? 0 : indexOfCaseShift;
- itemizeShapeAndPlace(smallCapsBuffer.data() + itemStart, itemLength, isSmallCaps, glyphBuffer);
+
+ if (nextFontData != fontData || nextIsSmallCaps != isSmallCaps) {
+ int itemStart = m_run.rtl() ? index : indexOfFontTransition;
+ int itemLength = m_run.rtl() ? indexOfFontTransition - index : index - indexOfFontTransition;
+ itemizeShapeAndPlace((isSmallCaps ? smallCapsBuffer.data() : cp) + itemStart, itemLength, fontData, glyphBuffer);
+ indexOfFontTransition = index;
}
- } else
- itemizeShapeAndPlace(cp, length, false, glyphBuffer);
+ }
+
+ int itemLength = m_run.rtl() ? indexOfFontTransition + 1 : length - indexOfFontTransition;
+ if (itemLength) {
+ int itemStart = m_run.rtl() ? 0 : indexOfFontTransition;
+ itemizeShapeAndPlace((nextIsSmallCaps ? smallCapsBuffer.data() : cp) + itemStart, itemLength, nextFontData, glyphBuffer);
+ }
}
-void UniscribeController::itemizeShapeAndPlace(const UChar* cp, unsigned length, bool smallCaps, GlyphBuffer* glyphBuffer)
+void UniscribeController::itemizeShapeAndPlace(const UChar* cp, unsigned length, const FontData* fontData, GlyphBuffer* glyphBuffer)
{
// ScriptItemize (in Windows XP versions prior to SP2) can overflow by 1. This is why there is an extra empty item
// hanging out at the end of the array
if (m_run.rtl()) {
for (int i = m_items.size() - 2; i >= 0; i--) {
- if (!shapeAndPlaceItem(cp, i, smallCaps, glyphBuffer))
+ if (!shapeAndPlaceItem(cp, i, fontData, glyphBuffer))
return;
}
} else {
for (unsigned i = 0; i < m_items.size() - 1; i++) {
- if (!shapeAndPlaceItem(cp, i, smallCaps, glyphBuffer))
+ if (!shapeAndPlaceItem(cp, i, fontData, glyphBuffer))
return;
}
}
m_state.fOverrideDirection = m_run.directionalOverride();
}
-bool UniscribeController::shapeAndPlaceItem(const UChar* cp, unsigned i, bool smallCaps, GlyphBuffer* glyphBuffer)
+bool UniscribeController::shapeAndPlaceItem(const UChar* cp, unsigned i, const FontData* fontData, GlyphBuffer* glyphBuffer)
{
// Determine the string for this item.
const UChar* str = cp + m_items[i].iCharPos;
int len = m_items[i+1].iCharPos - m_items[i].iCharPos;
SCRIPT_ITEM item = m_items[i];
- // Get our current FontData that we are using.
- unsigned dataIndex = 0;
- const FontData* fontData = m_font.fontDataAt(dataIndex);
- if (smallCaps)
- fontData = fontData->smallCapsFontData(m_font.fontDescription());
-
// Set up buffers to hold the results of shaping the item.
Vector<WORD> glyphs;
Vector<WORD> clusters;
Vector<SCRIPT_VISATTR> visualAttributes;
clusters.resize(len);
- // Shape the item. This will provide us with glyphs for the item. We will
- // attempt to shape using the first available FontData. If the shaping produces a result with missing
- // glyphs, then we will fall back to the next FontData.
- // FIXME: This isn't as good as per-glyph fallback, but in practice it should be pretty good, since
- // items are broken up by "shaping engine", meaning unique scripts will be broken up into
- // separate items.
- bool lastResortFontTried = false;
- while (fontData) {
- // The recommended size for the glyph buffer is 1.5 * the character length + 16 in the uniscribe docs.
- // Apparently this is a good size to avoid having to make repeated calls to ScriptShape.
- glyphs.resize(1.5 * len + 16);
- visualAttributes.resize(glyphs.size());
-
- if (shape(str, len, item, fontData, glyphs, clusters, visualAttributes))
- break;
-
- // Try again with the next font in the list.
- if (lastResortFontTried) {
- fontData = 0;
- break;
- }
-
- fontData = m_font.fontDataAt(++dataIndex);
- if (!fontData) {
- // Out of fonts. Get a font data based on the actual characters.
- fontData = m_font.fontDataForCharacters(str, len);
- lastResortFontTried = true;
- }
- if (smallCaps)
- fontData = fontData->smallCapsFontData(m_font.fontDescription());
- }
+ // Shape the item.
+ // The recommended size for the glyph buffer is 1.5 * the character length + 16 in the uniscribe docs.
+ // Apparently this is a good size to avoid having to make repeated calls to ScriptShape.
+ glyphs.resize(1.5 * len + 16);
+ visualAttributes.resize(glyphs.size());
- // Just give up. We were unable to shape.
- if (!fontData)
+ if (!shape(str, len, item, fontData, glyphs, clusters, visualAttributes))
return true;
// We now have a collection of glyphs.
// array.
glyphs[clusters[k]] = fontData->m_spaceGlyph;
advances[clusters[k]] = logicalSpaceWidth;
- spaceCharacters[clusters[k]] = m_currentCharacter + k + m_items[i].iCharPos;
+ spaceCharacters[clusters[k]] = m_currentCharacter + k + item.iCharPos;
}
if (Font::isRoundingHackCharacter(ch))
- roundingHackCharacters[clusters[k]] = m_currentCharacter + k + m_items[i].iCharPos;
+ roundingHackCharacters[clusters[k]] = m_currentCharacter + k + item.iCharPos;
- int boundary = k + m_currentCharacter + m_items[i].iCharPos;
+ int boundary = k + m_currentCharacter + item.iCharPos;
if (boundary < m_run.length() &&
Font::isRoundingHackCharacter(*(str + k + 1)))
roundingHackWordBoundaries[clusters[k]] = boundary;
// Account for word-spacing.
int characterIndex = spaceCharacters[k];
- if (characterIndex > 0 && !Font::treatAsSpace(*m_run.data(characterIndex-1)) && m_font.wordSpacing())
+ if (characterIndex > 0 && !Font::treatAsSpace(*m_run.data(characterIndex - 1)) && m_font.wordSpacing())
advance += m_font.wordSpacing();
}
}
private:
void resetControlAndState();
- void itemizeShapeAndPlace(const UChar*, unsigned length, bool smallCaps, GlyphBuffer* glyphBuffer);
- bool shapeAndPlaceItem(const UChar*, unsigned index, bool smallCaps, GlyphBuffer*);
+ void itemizeShapeAndPlace(const UChar*, unsigned length, const FontData*, GlyphBuffer*);
+ bool shapeAndPlaceItem(const UChar*, unsigned index, const FontData*, GlyphBuffer*);
bool shape(const UChar* str, int len, SCRIPT_ITEM item, const FontData* fontData,
Vector<WORD>& glyphs, Vector<WORD>& clusters,
Vector<SCRIPT_VISATTR>& visualAttributes);