bool SurrogatePairAwareTextIterator::consumeSlowCase(UChar32& character, unsigned& clusterLength) { if (character <= 0x30FE) { // Deal with Hiragana and Katakana voiced and semi-voiced syllables. // Normalize into composed form, and then look for glyph with base + combined mark. // Check above for character range to minimize performance impact. if (UChar32 normalized = normalizeVoicingMarks()) { character = normalized; clusterLength = 2; } return true; } if (!U16_IS_SURROGATE(character)) return true; // If we have a surrogate pair, make sure it starts with the high part. if (!U16_IS_SURROGATE_LEAD(character)) return false; // Do we have a surrogate pair? If so, determine the full Unicode (32 bit) code point before glyph lookup. // Make sure we have another character and it's a low surrogate. if (m_currentCharacter + 1 >= m_endCharacter) return false; UChar low = m_characters[1]; if (!U16_IS_TRAIL(low)) return false; character = U16_GET_SUPPLEMENTARY(character, low); clusterLength = 2; return true; }
// FIXME: This function may not work if the emphasis mark uses a complex script, but none of the // standard emphasis marks do so. bool Font::getEmphasisMarkGlyphData(const AtomicString& mark, GlyphData& glyphData) const { if (mark.isEmpty()) return false; #if ENABLE(SVG_FONTS) // FIXME: Implement for SVG fonts. if (primaryFont()->isSVGFont()) return false; #endif UChar32 character = mark[0]; if (U16_IS_SURROGATE(character)) { if (!U16_IS_SURROGATE_LEAD(character)) return false; if (mark.length() < 2) return false; UChar low = mark[1]; if (!U16_IS_TRAIL(low)) return false; character = U16_GET_SUPPLEMENTARY(character, low); } glyphData = glyphDataForCharacter(character, false, EmphasisMarkVariant); return true; }
static UChar32 surrogatePairAwareFirstCharacter(const UChar* characters, unsigned length) { if (U16_IS_SURROGATE(characters[0])) { if (!U16_IS_SURROGATE_LEAD(characters[0]) || length < 2 || !U16_IS_TRAIL(characters[1])) return ' '; return U16_GET_SUPPLEMENTARY(characters[0], characters[1]); } return characters[0]; }
static String getFontFamilyForCharacters(const UChar* characters, size_t numCharacters) { FcCharSet* cset = FcCharSetCreate(); for (size_t i = 0; i < numCharacters; ++i) { if (U16_IS_SURROGATE(characters[i]) && U16_IS_SURROGATE_LEAD(characters[i]) && i != numCharacters - 1 && U16_IS_TRAIL(characters[i + 1])) { if (FcCharSetAddChar(cset, U16_GET_SUPPLEMENTARY(characters[i], characters[i+1])) == FcFalse) return String(); i++; } else if (FcCharSetAddChar(cset, characters[i]) == FcFalse) return String(); } FcPattern *pattern = FcPatternCreate(); FcPatternAddCharSet(pattern, FC_CHARSET, cset); FcConfigSubstitute(0, pattern, FcMatchPattern); FcDefaultSubstitute(pattern); FcResult result; FcPattern *match = FcFontMatch(0, pattern, &result); FcChar8 *filename; if (FcPatternGetString(match, FC_FILE, 0, &filename) != FcResultMatch) { FcCharSetDestroy(cset); FcPatternDestroy(match); FcPatternDestroy(pattern); return String(); } FcChar8* family; if (FcPatternGetString(match, FC_FAMILY, 0, &family) == FcResultMatch) { FcCharSetDestroy(cset); FcPatternDestroy(match); FcPatternDestroy(pattern); const char* charFamily = reinterpret_cast<char*>(family); return String(charFamily); } FcPatternDestroy(match); FcCharSetDestroy(cset); FcPatternDestroy(pattern); return String(); }
bool UTF16TextIterator::isValidSurrogatePair(UChar32& character) { // If we have a surrogate pair, make sure it starts with the high part. if (!U16_IS_SURROGATE_LEAD(character)) return false; // Do we have a surrogate pair? If so, determine the full Unicode (32 bit) // code point before glyph lookup. // Make sure we have another character and it's a low surrogate. if (m_characters + 1 >= m_charactersEnd) return false; UChar low = m_characters[1]; if (!U16_IS_TRAIL(low)) return false; return true; }
FcPattern* createFontConfigPatternForCharacters(const UChar* characters, int length) { FcPattern* pattern = FcPatternCreate(); FcCharSet* fontConfigCharSet = FcCharSetCreate(); for (int i = 0; i < length; ++i) { if (U16_IS_SURROGATE(characters[i]) && U16_IS_SURROGATE_LEAD(characters[i]) && i != length - 1 && U16_IS_TRAIL(characters[i + 1])) { FcCharSetAddChar(fontConfigCharSet, U16_GET_SUPPLEMENTARY(characters[i], characters[i+1])); i++; } else FcCharSetAddChar(fontConfigCharSet, characters[i]); } FcPatternAddCharSet(pattern, FC_CHARSET, fontConfigCharSet); FcCharSetDestroy(fontConfigCharSet); FcPatternAddBool(pattern, FC_SCALABLE, FcTrue); FcConfigSubstitute(0, pattern, FcMatchPattern); FcDefaultSubstitute(pattern); return pattern; }
// FIXME: This function may not work if the emphasis mark uses a complex script, but none of the // standard emphasis marks do so. bool Font::getEmphasisMarkGlyphData(const AtomicString& mark, GlyphData& glyphData) const { if (mark.isEmpty()) return false; UChar32 character = mark[0]; if (U16_IS_SURROGATE(character)) { if (!U16_IS_SURROGATE_LEAD(character)) return false; if (mark.length() < 2) return false; UChar low = mark[1]; if (!U16_IS_TRAIL(low)) return false; character = U16_GET_SUPPLEMENTARY(character, low); } glyphData = glyphDataForCharacter(character, false, EmphasisMarkVariant); return true; }
bool readUTFChar(const UChar* str, int* begin, int length, unsigned* codePoint) { if (U16_IS_SURROGATE(str[*begin])) { if (!U16_IS_SURROGATE_LEAD(str[*begin]) || *begin + 1 >= length || !U16_IS_TRAIL(str[*begin + 1])) { // Invalid surrogate pair. *codePoint = kUnicodeReplacementCharacter; return false; } // Valid surrogate pair. *codePoint = U16_GET_SUPPLEMENTARY(str[*begin], str[*begin + 1]); (*begin)++; } else { // Not a surrogate, just one 16-bit word. *codePoint = str[*begin]; } if (U_IS_UNICODE_CHAR(*codePoint)) return true; // Invalid code point. *codePoint = kUnicodeReplacementCharacter; return false; }
void WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer) { if (offset > m_end) offset = m_end; int currentCharacter = m_currentCharacter; const UChar* cp = m_run.data(currentCharacter); bool rtl = m_run.rtl(); bool hasExtraSpacing = (m_font->letterSpacing() || m_font->wordSpacing() || m_padding) && !m_run.spacingDisabled(); float widthSinceLastRounding = m_runWidthSoFar; m_runWidthSoFar = floorf(m_runWidthSoFar); widthSinceLastRounding -= m_runWidthSoFar; float lastRoundingWidth = m_finalRoundingWidth; FloatRect bounds; const SimpleFontData* primaryFont = m_font->primaryFont(); const SimpleFontData* lastFontData = primaryFont; while (currentCharacter < offset) { UChar32 c = *cp; unsigned clusterLength = 1; if (c >= 0x3041) { if (c <= 0x30FE) { // Deal with Hiragana and Katakana voiced and semi-voiced syllables. // Normalize into composed form, and then look for glyph with base + combined mark. // Check above for character range to minimize performance impact. UChar32 normalized = normalizeVoicingMarks(currentCharacter); if (normalized) { c = normalized; clusterLength = 2; } } else if (U16_IS_SURROGATE(c)) { if (!U16_IS_SURROGATE_LEAD(c)) break; // Do we have a surrogate pair? If so, determine the full Unicode (32 bit) // code point before glyph lookup. // Make sure we have another character and it's a low surrogate. if (currentCharacter + 1 >= m_run.length()) break; UChar low = cp[1]; if (!U16_IS_TRAIL(low)) break; c = U16_GET_SUPPLEMENTARY(c, low); clusterLength = 2; } } const GlyphData& glyphData = m_font->glyphDataForCharacter(c, rtl); Glyph glyph = glyphData.glyph; const SimpleFontData* fontData = glyphData.fontData; ASSERT(fontData); // Now that we have a glyph and font data, get its width. float width; if (c == '\t' && m_run.allowTabs()) { float tabWidth = m_font->tabWidth(*fontData); width = tabWidth - fmodf(m_run.xPos() + m_runWidthSoFar + widthSinceLastRounding, tabWidth); } else { width = fontData->widthForGlyph(glyph); #if ENABLE(SVG) // SVG uses horizontalGlyphStretch(), when textLength is used to stretch/squeeze text. width *= m_run.horizontalGlyphStretch(); #endif // We special case spaces in two ways when applying word rounding. // First, we round spaces to an adjusted width in all fonts. // Second, in fixed-pitch fonts we ensure that all characters that // match the width of the space character have the same width as the space character. if (width == fontData->spaceWidth() && (fontData->pitch() == FixedPitch || glyph == fontData->spaceGlyph()) && m_run.applyWordRounding()) width = fontData->adjustedSpaceWidth(); } if (fontData != lastFontData && width) { lastFontData = fontData; if (m_fallbackFonts && fontData != primaryFont) { // FIXME: This does a little extra work that could be avoided if // glyphDataForCharacter() returned whether it chose to use a small caps font. if (!m_font->isSmallCaps() || c == toUpper(c)) m_fallbackFonts->add(fontData); else { const GlyphData& uppercaseGlyphData = m_font->glyphDataForCharacter(toUpper(c), rtl); if (uppercaseGlyphData.fontData != primaryFont) m_fallbackFonts->add(uppercaseGlyphData.fontData); } } } if (hasExtraSpacing) { // Account for letter-spacing. if (width && m_font->letterSpacing()) width += m_font->letterSpacing(); if (Font::treatAsSpace(c)) { // Account for padding. WebCore uses space padding to justify text. // We distribute the specified padding over the available spaces in the run. if (m_padding) { // Use left over padding if not evenly divisible by number of spaces. if (m_padding < m_padPerSpace) { width += m_padding; m_padding = 0; } else { float previousPadding = m_padding; m_padding -= m_padPerSpace; width += roundf(previousPadding) - roundf(m_padding); } } // Account for word spacing. // We apply additional space between "words" by adding width to the space character. if (currentCharacter != 0 && !Font::treatAsSpace(cp[-1]) && m_font->wordSpacing()) width += m_font->wordSpacing(); } } if (m_accountForGlyphBounds) { bounds = fontData->boundsForGlyph(glyph); if (!currentCharacter) m_firstGlyphOverflow = max<float>(0, -bounds.x()); } if (m_forTextEmphasis && !Font::canReceiveTextEmphasis(c)) glyph = 0; // Advance past the character we just dealt with. cp += clusterLength; currentCharacter += clusterLength; // Account for float/integer impedance mismatch between CG and KHTML. "Words" (characters // followed by a character defined by isRoundingHackCharacter()) are always an integer width. // We adjust the width of the last character of a "word" to ensure an integer width. // If we move KHTML to floats we can remove this (and related) hacks. float oldWidth = width; // Force characters that are used to determine word boundaries for the rounding hack // to be integer width, so following words will start on an integer boundary. if (m_run.applyWordRounding() && Font::isRoundingHackCharacter(c)) { width = ceilf(width); // Since widthSinceLastRounding can lose precision if we include measurements for // preceding whitespace, we bypass it here. m_runWidthSoFar += width; // Since this is a rounding hack character, we should have reset this sum on the previous // iteration. ASSERT(!widthSinceLastRounding); } else { // Check to see if the next character is a "rounding hack character", if so, adjust // width so that the total run width will be on an integer boundary. if ((m_run.applyWordRounding() && currentCharacter < m_run.length() && Font::isRoundingHackCharacter(*cp)) || (m_run.applyRunRounding() && currentCharacter >= m_end)) { float totalWidth = widthSinceLastRounding + width; widthSinceLastRounding = ceilf(totalWidth); width += widthSinceLastRounding - totalWidth; m_runWidthSoFar += widthSinceLastRounding; widthSinceLastRounding = 0; } else widthSinceLastRounding += width; } if (glyphBuffer) glyphBuffer->add(glyph, fontData, (rtl ? oldWidth + lastRoundingWidth : width)); lastRoundingWidth = width - oldWidth; if (m_accountForGlyphBounds) { m_maxGlyphBoundingBoxY = max(m_maxGlyphBoundingBoxY, bounds.bottom()); m_minGlyphBoundingBoxY = min(m_minGlyphBoundingBoxY, bounds.y()); m_lastGlyphOverflow = max<float>(0, bounds.right() - width); } } m_currentCharacter = currentCharacter; m_runWidthSoFar += widthSinceLastRounding; m_finalRoundingWidth = lastRoundingWidth; }
/* * Match each code point in a string against each code point in the matchSet. * Return the index of the first string code point that * is (polarity==TRUE) or is not (FALSE) contained in the matchSet. * Return -(string length)-1 if there is no such code point. */ static int32_t _matchFromSet(const UChar* string, const UChar* matchSet, UBool polarity) { int32_t matchLen, matchBMPLen, strItr, matchItr; UChar32 stringCh, matchCh; UChar c, c2; /* first part of matchSet contains only BMP code points */ matchBMPLen = 0; while ((c = matchSet[matchBMPLen]) != 0 && U16_IS_SINGLE(c)) { ++matchBMPLen; } /* second part of matchSet contains BMP and supplementary code points */ matchLen = matchBMPLen; while (matchSet[matchLen] != 0) { ++matchLen; } for (strItr = 0; (c = string[strItr]) != 0;) { ++strItr; if (U16_IS_SINGLE(c)) { if (polarity) { for (matchItr = 0; matchItr < matchLen; ++matchItr) { if (c == matchSet[matchItr]) { return strItr - 1; /* one matches */ } } } else { for (matchItr = 0; matchItr < matchLen; ++matchItr) { if (c == matchSet[matchItr]) { goto endloop; } } return strItr - 1; /* none matches */ } } else { /* * No need to check for string length before U16_IS_TRAIL * because c2 could at worst be the terminating NUL. */ if (U16_IS_SURROGATE_LEAD(c) && U16_IS_TRAIL(c2 = string[strItr])) { ++strItr; stringCh = U16_GET_SUPPLEMENTARY(c, c2); } else { stringCh = c; /* unpaired trail surrogate */ } if (polarity) { for (matchItr = matchBMPLen; matchItr < matchLen;) { U16_NEXT(matchSet, matchItr, matchLen, matchCh); if (stringCh == matchCh) { return strItr - U16_LENGTH(stringCh); /* one matches */ } } } else { for (matchItr = matchBMPLen; matchItr < matchLen;) { U16_NEXT(matchSet, matchItr, matchLen, matchCh); if (stringCh == matchCh) { goto endloop; } } return strItr - U16_LENGTH(stringCh); /* none matches */ } } endloop: /* wish C had continue with labels like Java... */; } /* Didn't find it. */ return -strItr - 1; }
static void UConverter_fromUnicode_CompoundText_OFFSETS(UConverterFromUnicodeArgs* args, UErrorCode* err){ UConverter *cnv = args->converter; uint8_t *target = (uint8_t *) args->target; const uint8_t *targetLimit = (const uint8_t *) args->targetLimit; const UChar* source = args->source; const UChar* sourceLimit = args->sourceLimit; /* int32_t* offsets = args->offsets; */ UChar32 sourceChar; UBool useFallback = cnv->useFallback; uint8_t tmpTargetBuffer[7]; int32_t tmpTargetBufferLength = 0; COMPOUND_TEXT_CONVERTERS currentState, tmpState; uint32_t pValue; int32_t pValueLength = 0; int32_t i, n, j; UConverterDataCompoundText *myConverterData = (UConverterDataCompoundText *) cnv->extraInfo; currentState = myConverterData->state; /* check if the last codepoint of previous buffer was a lead surrogate*/ if((sourceChar = cnv->fromUChar32)!=0 && target< targetLimit) { goto getTrail; } while( source < sourceLimit){ if(target < targetLimit){ sourceChar = *(source++); /*check if the char is a First surrogate*/ if(U16_IS_SURROGATE(sourceChar)) { if(U16_IS_SURROGATE_LEAD(sourceChar)) { getTrail: /*look ahead to find the trail surrogate*/ if(source < sourceLimit) { /* test the following code unit */ UChar trail=(UChar) *source; if(U16_IS_TRAIL(trail)) { source++; sourceChar=U16_GET_SUPPLEMENTARY(sourceChar, trail); cnv->fromUChar32=0x00; /* convert this supplementary code point */ /* exit this condition tree */ } else { /* this is an unmatched lead code unit (1st surrogate) */ /* callback(illegal) */ *err=U_ILLEGAL_CHAR_FOUND; cnv->fromUChar32=sourceChar; break; } } else { /* no more input */ cnv->fromUChar32=sourceChar; break; } } else { /* this is an unmatched trail code unit (2nd surrogate) */ /* callback(illegal) */ *err=U_ILLEGAL_CHAR_FOUND; cnv->fromUChar32=sourceChar; break; } } tmpTargetBufferLength = 0; tmpState = getState(sourceChar); if (tmpState != DO_SEARCH && currentState != tmpState) { /* Get escape sequence if necessary */ currentState = tmpState; for (i = 0; escSeqCompoundText[currentState][i] != 0; i++) { tmpTargetBuffer[tmpTargetBufferLength++] = escSeqCompoundText[currentState][i]; } } if (tmpState == DO_SEARCH) { /* Test all available converters */ for (i = 1; i < SEARCH_LENGTH; i++) { pValueLength = ucnv_MBCSFromUChar32(myConverterData->myConverterArray[i], sourceChar, &pValue, useFallback); if (pValueLength > 0) { tmpState = (COMPOUND_TEXT_CONVERTERS)i; if (currentState != tmpState) { currentState = tmpState; for (j = 0; escSeqCompoundText[currentState][j] != 0; j++) { tmpTargetBuffer[tmpTargetBufferLength++] = escSeqCompoundText[currentState][j]; } } for (n = (pValueLength - 1); n >= 0; n--) { tmpTargetBuffer[tmpTargetBufferLength++] = (uint8_t)(pValue >> (n * 8)); } break; } } } else if (tmpState == COMPOUND_TEXT_SINGLE_0) {
void WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer) { if (offset > m_end) offset = m_end; int currentCharacter = m_currentCharacter; const UChar* cp = m_run.data(currentCharacter); bool rtl = m_run.rtl(); bool hasExtraSpacing = m_font->letterSpacing() || m_font->wordSpacing() || m_padding; float runWidthSoFar = m_runWidthSoFar; float lastRoundingWidth = m_finalRoundingWidth; while (currentCharacter < offset) { UChar32 c = *cp; unsigned clusterLength = 1; if (c >= 0x3041) { if (c <= 0x30FE) { // Deal with Hiragana and Katakana voiced and semi-voiced syllables. // Normalize into composed form, and then look for glyph with base + combined mark. // Check above for character range to minimize performance impact. UChar32 normalized = normalizeVoicingMarks(currentCharacter); if (normalized) { c = normalized; clusterLength = 2; } } else if (U16_IS_SURROGATE(c)) { if (!U16_IS_SURROGATE_LEAD(c)) break; // Do we have a surrogate pair? If so, determine the full Unicode (32 bit) // code point before glyph lookup. // Make sure we have another character and it's a low surrogate. if (currentCharacter + 1 >= m_run.length()) break; UChar low = cp[1]; if (!U16_IS_TRAIL(low)) break; c = U16_GET_SUPPLEMENTARY(c, low); clusterLength = 2; } } const GlyphData& glyphData = m_font->glyphDataForCharacter(c, rtl); Glyph glyph = glyphData.glyph; const SimpleFontData* fontData = glyphData.fontData; ASSERT(fontData); // Now that we have a glyph and font data, get its width. float width; if (c == '\t' && m_run.allowTabs()) { float tabWidth = m_font->tabWidth(); width = tabWidth - fmodf(m_run.xPos() + runWidthSoFar, tabWidth); } else { width = fontData->widthForGlyph(glyph); // We special case spaces in two ways when applying word rounding. // First, we round spaces to an adjusted width in all fonts. // Second, in fixed-pitch fonts we ensure that all characters that // match the width of the space character have the same width as the space character. if (width == fontData->m_spaceWidth && (fontData->m_treatAsFixedPitch || glyph == fontData->m_spaceGlyph) && m_run.applyWordRounding()) width = fontData->m_adjustedSpaceWidth; } if (hasExtraSpacing && !m_run.spacingDisabled()) { // Account for letter-spacing. if (width && m_font->letterSpacing()) width += m_font->letterSpacing(); if (Font::treatAsSpace(c)) { // Account for padding. WebCore uses space padding to justify text. // We distribute the specified padding over the available spaces in the run. if (m_padding) { // Use left over padding if not evenly divisible by number of spaces. if (m_padding < m_padPerSpace) { width += m_padding; m_padding = 0; } else { width += m_padPerSpace; m_padding -= m_padPerSpace; } } // Account for word spacing. // We apply additional space between "words" by adding width to the space character. if (currentCharacter != 0 && !Font::treatAsSpace(cp[-1]) && m_font->wordSpacing()) width += m_font->wordSpacing(); } } // Advance past the character we just dealt with. cp += clusterLength; currentCharacter += clusterLength; // Account for float/integer impedance mismatch between CG and KHTML. "Words" (characters // followed by a character defined by isRoundingHackCharacter()) are always an integer width. // We adjust the width of the last character of a "word" to ensure an integer width. // If we move KHTML to floats we can remove this (and related) hacks. float oldWidth = width; // Force characters that are used to determine word boundaries for the rounding hack // to be integer width, so following words will start on an integer boundary. if (m_run.applyWordRounding() && Font::isRoundingHackCharacter(c)) width = ceilf(width); // Check to see if the next character is a "rounding hack character", if so, adjust // width so that the total run width will be on an integer boundary. if ((m_run.applyWordRounding() && currentCharacter < m_run.length() && Font::isRoundingHackCharacter(*cp)) || (m_run.applyRunRounding() && currentCharacter >= m_end)) { float totalWidth = runWidthSoFar + width; width += ceilf(totalWidth) - totalWidth; } runWidthSoFar += width; if (glyphBuffer) glyphBuffer->add(glyph, fontData, (rtl ? oldWidth + lastRoundingWidth : width)); lastRoundingWidth = width - oldWidth; } m_currentCharacter = currentCharacter; m_runWidthSoFar = runWidthSoFar; m_finalRoundingWidth = lastRoundingWidth; }
bool FontConfigDirect::Match(std::string* result_family, unsigned* result_filefaceid, bool filefaceid_valid, unsigned filefaceid, const std::string& family, const void* data, size_t characters_bytes, bool* is_bold, bool* is_italic) { if (family.length() > kMaxFontFamilyLength) return false; SkAutoMutexAcquire ac(mutex_); // Given |family|, |is_bold| and |is_italic| but not |data|, the result will // be a function of these three parameters, and thus eligible for caching. // This is the fast path for |SkTypeface::CreateFromName()|. bool eligible_for_cache = !family.empty() && is_bold && is_italic && !data; if (eligible_for_cache) { int style = (*is_bold ? SkTypeface::kBold : 0 ) | (*is_italic ? SkTypeface::kItalic : 0); FontMatchKey key = FontMatchKey(family, style); const std::map<FontMatchKey, FontMatch>::const_iterator i = font_match_cache_.find(key); if (i != font_match_cache_.end()) { *is_bold = i->second.is_bold; *is_italic = i->second.is_italic; if (result_family) *result_family = i->second.family; if (result_filefaceid) *result_filefaceid = i->second.filefaceid; return true; } } FcPattern* pattern = FcPatternCreate(); if (filefaceid_valid) { const std::map<unsigned, std::string>::const_iterator i = fileid_to_filename_.find(FileFaceIdToFileId(filefaceid)); if (i == fileid_to_filename_.end()) { FcPatternDestroy(pattern); return false; } int face_index = filefaceid & 0xfu; FcPatternAddString(pattern, FC_FILE, reinterpret_cast<const FcChar8*>(i->second.c_str())); // face_index is added only when family is empty because it is not // necessary to uniquiely identify a font if both file and // family are given. if (family.empty()) FcPatternAddInteger(pattern, FC_INDEX, face_index); } if (!family.empty()) { FcPatternAddString(pattern, FC_FAMILY, (FcChar8*) family.c_str()); } FcCharSet* charset = NULL; if (data) { charset = FcCharSetCreate(); const uint16_t* chars = (const uint16_t*) data; size_t num_chars = characters_bytes / 2; for (size_t i = 0; i < num_chars; ++i) { if (U16_IS_SURROGATE(chars[i]) && U16_IS_SURROGATE_LEAD(chars[i]) && i != num_chars - 1 && U16_IS_TRAIL(chars[i + 1])) { FcCharSetAddChar(charset, U16_GET_SUPPLEMENTARY(chars[i], chars[i+1])); i++; } else { FcCharSetAddChar(charset, chars[i]); } } FcPatternAddCharSet(pattern, FC_CHARSET, charset); FcCharSetDestroy(charset); // pattern now owns it. } FcPatternAddInteger(pattern, FC_WEIGHT, is_bold && *is_bold ? FC_WEIGHT_BOLD : FC_WEIGHT_NORMAL); FcPatternAddInteger(pattern, FC_SLANT, is_italic && *is_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN); FcPatternAddBool(pattern, FC_SCALABLE, FcTrue); FcConfigSubstitute(NULL, pattern, FcMatchPattern); FcDefaultSubstitute(pattern); // Font matching: // CSS often specifies a fallback list of families: // font-family: a, b, c, serif; // However, fontconfig will always do its best to find *a* font when asked // for something so we need a way to tell if the match which it has found is // "good enough" for us. Otherwise, we can return NULL which gets piped up // and lets WebKit know to try the next CSS family name. However, fontconfig // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we // wish to support that. // // Thus, if a specific family is requested we set @family_requested. Then we // record two strings: the family name after config processing and the // family name after resolving. If the two are equal, it's a good match. // // So consider the case where a user has mapped Arial to Helvetica in their // config. // requested family: "Arial" // post_config_family: "Helvetica" // post_match_family: "Helvetica" // -> good match // // and for a missing font: // requested family: "Monaco" // post_config_family: "Monaco" // post_match_family: "Times New Roman" // -> BAD match // // However, we special-case fallback fonts; see IsFallbackFontAllowed(). FcChar8* post_config_family; FcPatternGetString(pattern, FC_FAMILY, 0, &post_config_family); FcResult result; FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result); if (!font_set) { FcPatternDestroy(pattern); return false; } FcPattern* match = MatchFont(font_set, post_config_family, family); if (!match) { FcPatternDestroy(pattern); FcFontSetDestroy(font_set); return false; } FcPatternDestroy(pattern); FcChar8* c_filename; if (FcPatternGetString(match, FC_FILE, 0, &c_filename) != FcResultMatch) { FcFontSetDestroy(font_set); return false; } int face_index; if (FcPatternGetInteger(match, FC_INDEX, 0, &face_index) != FcResultMatch) { FcFontSetDestroy(font_set); return false; } FontMatch font_match; if (filefaceid_valid) { font_match.filefaceid = filefaceid; } else { unsigned out_fileid; const std::string filename(reinterpret_cast<char*>(c_filename)); const std::map<std::string, unsigned>::const_iterator i = filename_to_fileid_.find(filename); if (i == filename_to_fileid_.end()) { out_fileid = next_file_id_++; filename_to_fileid_[filename] = out_fileid; fileid_to_filename_[out_fileid] = filename; } else { out_fileid = i->second; } // fileid stored in filename_to_fileid_ and fileid_to_filename_ is // unique only up to the font file. We have to encode face_index for // the out param. font_match.filefaceid = FileIdAndFaceIndexToFileFaceId(out_fileid, face_index); } bool success = GetFontProperties(match, &font_match.family, &font_match.is_bold, &font_match.is_italic); FcFontSetDestroy(font_set); if (success) { // If eligible, cache the result of the matching. if (eligible_for_cache) { int style = (*is_bold ? SkTypeface::kBold : 0 ) | (*is_italic ? SkTypeface::kItalic : 0); font_match_cache_[FontMatchKey(family, style)] = font_match; } if (result_family) *result_family = font_match.family; if (result_filefaceid) *result_filefaceid = font_match.filefaceid; if (is_bold) *is_bold = font_match.is_bold; if (is_italic) *is_italic = font_match.is_italic; } return success; }
void CoreTextController::collectCoreTextRuns() { if (!m_end) return; // We break up glyph run generation for the string by FontData and (if needed) the use of small caps. const UChar* cp = m_run.characters(); bool hasTrailingSoftHyphen = m_run[m_end - 1] == softHyphen; if (m_font.isSmallCaps() || hasTrailingSoftHyphen) m_smallCapsBuffer.resize(m_end); unsigned indexOfFontTransition = m_run.rtl() ? m_end - 1 : 0; const UChar* curr = m_run.rtl() ? cp + m_end - 1 : cp; const UChar* end = m_run.rtl() ? cp - 1 : cp + m_end; // FIXME: Using HYPHEN-MINUS rather than HYPHEN because Times has a HYPHEN-MINUS glyph that looks like its // SOFT-HYPHEN glyph, and has no HYPHEN glyph. static const UChar hyphen = '-'; if (hasTrailingSoftHyphen && m_run.rtl()) { collectCoreTextRunsForCharacters(&hyphen, 1, m_end - 1, m_font.glyphDataForCharacter(hyphen, false).fontData); indexOfFontTransition--; curr--; } GlyphData glyphData; GlyphData nextGlyphData; bool isSurrogate = U16_IS_SURROGATE(*curr); if (isSurrogate) { if (m_run.ltr()) { if (!U16_IS_SURROGATE_LEAD(curr[0]) || curr + 1 == end || !U16_IS_TRAIL(curr[1])) return; nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[0], curr[1]), false); } else { if (!U16_IS_TRAIL(curr[0]) || curr -1 == end || !U16_IS_SURROGATE_LEAD(curr[-1])) return; nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[-1], curr[0]), false); } } else nextGlyphData = m_font.glyphDataForCharacter(*curr, false); UChar newC = 0; bool isSmallCaps; bool nextIsSmallCaps = !isSurrogate && m_font.isSmallCaps() && !(U_GET_GC_MASK(*curr) & U_GC_M_MASK) && (newC = u_toupper(*curr)) != *curr; if (nextIsSmallCaps) m_smallCapsBuffer[curr - cp] = newC; while (true) { curr = m_run.rtl() ? curr - (isSurrogate ? 2 : 1) : curr + (isSurrogate ? 2 : 1); if (curr == end) break; glyphData = nextGlyphData; isSmallCaps = nextIsSmallCaps; int index = curr - cp; isSurrogate = U16_IS_SURROGATE(*curr); UChar c = *curr; bool forceSmallCaps = !isSurrogate && isSmallCaps && (U_GET_GC_MASK(c) & U_GC_M_MASK); if (isSurrogate) { if (m_run.ltr()) { if (!U16_IS_SURROGATE_LEAD(curr[0]) || curr + 1 == end || !U16_IS_TRAIL(curr[1])) return; nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[0], curr[1]), false); } else { if (!U16_IS_TRAIL(curr[0]) || curr -1 == end || !U16_IS_SURROGATE_LEAD(curr[-1])) return; nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[-1], curr[0]), false); } } else nextGlyphData = m_font.glyphDataForCharacter(*curr, false, forceSmallCaps); if (!isSurrogate && m_font.isSmallCaps()) { nextIsSmallCaps = forceSmallCaps || (newC = u_toupper(c)) != c; if (nextIsSmallCaps) m_smallCapsBuffer[index] = forceSmallCaps ? c : newC; } if (nextGlyphData.fontData != glyphData.fontData || nextIsSmallCaps != isSmallCaps || !nextGlyphData.glyph != !glyphData.glyph) { int itemStart = m_run.rtl() ? index + 1 : indexOfFontTransition; int itemLength = m_run.rtl() ? indexOfFontTransition - index : index - indexOfFontTransition; collectCoreTextRunsForCharacters((isSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, glyphData.glyph ? glyphData.fontData : 0); indexOfFontTransition = index; } } int itemLength = m_run.rtl() ? indexOfFontTransition + 1 : m_end - indexOfFontTransition - (hasTrailingSoftHyphen ? 1 : 0); if (itemLength) { int itemStart = m_run.rtl() ? 0 : indexOfFontTransition; collectCoreTextRunsForCharacters((nextIsSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, nextGlyphData.glyph ? nextGlyphData.fontData : 0); } if (hasTrailingSoftHyphen && m_run.ltr()) collectCoreTextRunsForCharacters(&hyphen, 1, m_end - 1, m_font.glyphDataForCharacter(hyphen, false).fontData); }