📄 fontmacatsui.mm
字号:
}void ATSULayoutParameters::initialize(const Font* font, const GraphicsContext* graphicsContext){ m_font = font; const SimpleFontData* fontData = font->primaryFont(); m_fonts.set(new const SimpleFontData*[m_run.length()]); if (font->isSmallCaps()) m_charBuffer.set(new UChar[m_run.length()]); ATSUTextLayout layout; OSStatus status; ATSULayoutOperationOverrideSpecifier overrideSpecifier; initializeATSUStyle(fontData); // FIXME: This is currently missing the following required features that the CoreGraphics code path has: // - \n, \t, and nonbreaking space render as a space. UniCharCount runLength = m_run.length(); if (m_charBuffer) memcpy(m_charBuffer.get(), m_run.characters(), runLength * sizeof(UChar)); status = ATSUCreateTextLayoutWithTextPtr( (m_charBuffer ? m_charBuffer.get() : m_run.characters()), 0, // offset runLength, // length runLength, // total length 1, // styleRunCount &runLength, // length of style run &fontData->m_ATSUStyle, &layout); if (status != noErr) LOG_ERROR("ATSUCreateTextLayoutWithTextPtr failed(%d)", status); m_layout = layout; ATSUSetTextLayoutRefCon(m_layout, (URefCon)this); // FIXME: There are certain times when this method is called, when we don't have access to a GraphicsContext // measuring text runs with floatWidthForComplexText is one example. // ATSUI requires that we pass a valid CGContextRef to it when specifying kATSUCGContextTag (crashes when passed 0) // ATSUI disables sub-pixel rendering if kATSUCGContextTag is not specified! So we're in a bind. // Sometimes [[NSGraphicsContext currentContext] graphicsPort] may return the wrong (or no!) context. Nothing we can do about it (yet). CGContextRef cgContext = graphicsContext ? graphicsContext->platformContext() : (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; ATSLineLayoutOptions lineLayoutOptions = kATSLineKeepSpacesOutOfMargin | kATSLineHasNoHangers; Boolean rtl = m_run.rtl(); overrideSpecifier.operationSelector = kATSULayoutOperationPostLayoutAdjustment; overrideSpecifier.overrideUPP = overrideLayoutOperation; ATSUAttributeTag tags[] = { kATSUCGContextTag, kATSULineLayoutOptionsTag, kATSULineDirectionTag, kATSULayoutOperationOverrideTag }; ByteCount sizes[] = { sizeof(CGContextRef), sizeof(ATSLineLayoutOptions), sizeof(Boolean), sizeof(ATSULayoutOperationOverrideSpecifier) }; ATSUAttributeValuePtr values[] = { &cgContext, &lineLayoutOptions, &rtl, &overrideSpecifier }; status = ATSUSetLayoutControls(layout, (m_run.applyWordRounding() ? 4 : 3), tags, sizes, values); if (status != noErr) LOG_ERROR("ATSUSetLayoutControls failed(%d)", status); status = ATSUSetTransientFontMatching(layout, YES); if (status != noErr) LOG_ERROR("ATSUSetTransientFontMatching failed(%d)", status); m_hasSyntheticBold = false; ATSUFontID ATSUSubstituteFont; UniCharArrayOffset substituteOffset = 0; UniCharCount substituteLength; UniCharArrayOffset lastOffset; const SimpleFontData* substituteFontData = 0; while (substituteOffset < runLength) { // FIXME: Using ATSUMatchFontsToText() here results in several problems: the CSS font family list is not necessarily followed for the 2nd // and onwards unmatched characters; segmented fonts do not work correctly; behavior does not match the simple text and Uniscribe code // paths. Change this function to use Font::glyphDataForCharacter() for each character instead. lastOffset = substituteOffset; status = ATSUMatchFontsToText(layout, substituteOffset, kATSUToTextEnd, &ATSUSubstituteFont, &substituteOffset, &substituteLength); if (status == kATSUFontsMatched || status == kATSUFontsNotMatched) { const FontData* fallbackFontData = m_font->fontDataForCharacters(m_run.characters() + substituteOffset, substituteLength); substituteFontData = fallbackFontData ? fallbackFontData->fontDataForCharacter(m_run[0]) : 0; if (substituteFontData) { initializeATSUStyle(substituteFontData); if (substituteFontData->m_ATSUStyle) ATSUSetRunStyle(layout, substituteFontData->m_ATSUStyle, substituteOffset, substituteLength); } else substituteFontData = fontData; } else { substituteOffset = runLength; substituteLength = 0; } bool shapedArabic = false; bool isSmallCap = false; UniCharArrayOffset firstSmallCap = 0; const SimpleFontData *r = fontData; UniCharArrayOffset i; for (i = lastOffset; ; i++) { if (i == substituteOffset || i == substituteOffset + substituteLength) { if (isSmallCap) { isSmallCap = false; initializeATSUStyle(r->smallCapsFontData(m_font->fontDescription())); ATSUSetRunStyle(layout, r->smallCapsFontData(m_font->fontDescription())->m_ATSUStyle, firstSmallCap, i - firstSmallCap); } if (i == substituteOffset && substituteLength > 0) r = substituteFontData; else break; } if (!shapedArabic && WTF::Unicode::isArabicChar(m_run[i]) && !r->shapesArabic()) { shapedArabic = true; if (!m_charBuffer) { m_charBuffer.set(new UChar[runLength]); memcpy(m_charBuffer.get(), m_run.characters(), i * sizeof(UChar)); ATSUTextMoved(layout, m_charBuffer.get()); } shapeArabic(m_run.characters(), m_charBuffer.get(), runLength, i); } if (m_run.rtl() && !r->m_ATSUMirrors) { UChar mirroredChar = u_charMirror(m_run[i]); if (mirroredChar != m_run[i]) { if (!m_charBuffer) { m_charBuffer.set(new UChar[runLength]); memcpy(m_charBuffer.get(), m_run.characters(), runLength * sizeof(UChar)); ATSUTextMoved(layout, m_charBuffer.get()); } m_charBuffer[i] = mirroredChar; } } if (m_font->isSmallCaps()) { const SimpleFontData* smallCapsData = r->smallCapsFontData(m_font->fontDescription()); UChar c = m_charBuffer[i]; UChar newC; if (U_GET_GC_MASK(c) & U_GC_M_MASK) m_fonts[i] = isSmallCap ? smallCapsData : r; else if (!u_isUUppercase(c) && (newC = u_toupper(c)) != c) { m_charBuffer[i] = newC; if (!isSmallCap) { isSmallCap = true; firstSmallCap = i; } m_fonts[i] = smallCapsData; } else { if (isSmallCap) { isSmallCap = false; initializeATSUStyle(smallCapsData); ATSUSetRunStyle(layout, smallCapsData->m_ATSUStyle, firstSmallCap, i - firstSmallCap); } m_fonts[i] = r; } } else m_fonts[i] = r; if (m_fonts[i]->m_syntheticBoldOffset) m_hasSyntheticBold = true; } substituteOffset += substituteLength; } if (m_run.padding()) { float numSpaces = 0; unsigned k; for (k = 0; k < runLength; k++) if (Font::treatAsSpace(m_run[k])) numSpaces++; if (numSpaces == 0) m_padPerSpace = 0; else m_padPerSpace = ceilf(m_run.padding() / numSpaces); } else m_padPerSpace = 0;}FloatRect Font::selectionRectForComplexText(const TextRun& run, const IntPoint& point, int h, int from, int to) const{ OwnArrayPtr<UChar> charactersWithOverride; TextRun adjustedRun = copyRunForDirectionalOverrideIfNecessary(run, charactersWithOverride); if (run.directionalOverride()) { from++; to++; } ATSULayoutParameters params(adjustedRun); params.initialize(this); ATSTrapezoid firstGlyphBounds; ItemCount actualNumBounds; OSStatus status = ATSUGetGlyphBounds(params.m_layout, 0, 0, from, to - from, kATSUseFractionalOrigins, 1, &firstGlyphBounds, &actualNumBounds); if (status != noErr || actualNumBounds != 1) { static ATSTrapezoid zeroTrapezoid = { {0, 0}, {0, 0}, {0, 0}, {0, 0} }; firstGlyphBounds = zeroTrapezoid; } float beforeWidth = MIN(FixedToFloat(firstGlyphBounds.lowerLeft.x), FixedToFloat(firstGlyphBounds.upperLeft.x)); float afterWidth = MAX(FixedToFloat(firstGlyphBounds.lowerRight.x), FixedToFloat(firstGlyphBounds.upperRight.x)); FloatRect rect(point.x() + floorf(beforeWidth), point.y(), roundf(afterWidth) - floorf(beforeWidth), h); return rect;}void Font::drawComplexText(GraphicsContext* graphicsContext, const TextRun& run, const FloatPoint& point, int from, int to) const{ OSStatus status; int drawPortionLength = to - from; OwnArrayPtr<UChar> charactersWithOverride; TextRun adjustedRun = copyRunForDirectionalOverrideIfNecessary(run, charactersWithOverride); if (run.directionalOverride()) from++; ATSULayoutParameters params(adjustedRun); params.initialize(this, graphicsContext); // ATSUI can't draw beyond -32768 to +32767 so we translate the CTM and tell ATSUI to draw at (0, 0). CGContextRef context = graphicsContext->platformContext(); CGContextTranslateCTM(context, point.x(), point.y()); IntSize shadowSize; int shadowBlur; Color shadowColor; graphicsContext->getShadow(shadowSize, shadowBlur, shadowColor); bool hasSimpleShadow = graphicsContext->textDrawingMode() == cTextFill && shadowColor.isValid() && !shadowBlur; if (hasSimpleShadow) { // Paint simple shadows ourselves instead of relying on CG shadows, to avoid losing subpixel antialiasing. graphicsContext->clearShadow(); Color fillColor = graphicsContext->fillColor(); Color shadowFillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), shadowColor.alpha() * fillColor.alpha() / 255); graphicsContext->setFillColor(shadowFillColor); CGContextTranslateCTM(context, shadowSize.width(), shadowSize.height()); status = ATSUDrawText(params.m_layout, from, drawPortionLength, 0, 0); if (status == noErr && params.m_hasSyntheticBold) { // Force relayout for the bold pass ATSUClearLayoutCache(params.m_layout, 0); params.m_syntheticBoldPass = true; status = ATSUDrawText(params.m_layout, from, drawPortionLength, 0, 0); // Force relayout for the next pass ATSUClearLayoutCache(params.m_layout, 0); params.m_syntheticBoldPass = false; } CGContextTranslateCTM(context, -shadowSize.width(), -shadowSize.height()); graphicsContext->setFillColor(fillColor); } status = ATSUDrawText(params.m_layout, from, drawPortionLength, 0, 0); if (status == noErr && params.m_hasSyntheticBold) { // Force relayout for the bold pass ATSUClearLayoutCache(params.m_layout, 0); params.m_syntheticBoldPass = true; status = ATSUDrawText(params.m_layout, from, drawPortionLength, 0, 0); } CGContextTranslateCTM(context, -point.x(), -point.y()); if (status != noErr) // Nothing to do but report the error (dev build only). LOG_ERROR("ATSUDrawText() failed(%d)", status); if (hasSimpleShadow) graphicsContext->setShadow(shadowSize, shadowBlur, shadowColor);}float Font::floatWidthForComplexText(const TextRun& run) const{ if (run.length() == 0) return 0; ATSULayoutParameters params(run); params.initialize(this); OSStatus status; ATSTrapezoid firstGlyphBounds; ItemCount actualNumBounds; status = ATSUGetGlyphBounds(params.m_layout, 0, 0, 0, run.length(), kATSUseFractionalOrigins, 1, &firstGlyphBounds, &actualNumBounds); if (status != noErr) LOG_ERROR("ATSUGetGlyphBounds() failed(%d)", status); if (actualNumBounds != 1) LOG_ERROR("unexpected result from ATSUGetGlyphBounds(): actualNumBounds(%d) != 1", actualNumBounds); return MAX(FixedToFloat(firstGlyphBounds.upperRight.x), FixedToFloat(firstGlyphBounds.lowerRight.x)) - MIN(FixedToFloat(firstGlyphBounds.upperLeft.x), FixedToFloat(firstGlyphBounds.lowerLeft.x));}int Font::offsetForPositionForComplexText(const TextRun& run, int x, bool /*includePartialGlyphs*/) const{ OwnArrayPtr<UChar> charactersWithOverride; TextRun adjustedRun = copyRunForDirectionalOverrideIfNecessary(run, charactersWithOverride); ATSULayoutParameters params(adjustedRun); params.initialize(this); UniCharArrayOffset primaryOffset = 0; // FIXME: No idea how to avoid including partial glyphs. // Not even sure if that's the behavior this yields now. Boolean isLeading; UniCharArrayOffset secondaryOffset = 0; OSStatus status = ATSUPositionToOffset(params.m_layout, FloatToFixed(x), FloatToFixed(-1), &primaryOffset, &isLeading, &secondaryOffset); unsigned offset; if (status == noErr) { offset = (unsigned)primaryOffset; if (run.directionalOverride() && offset > 0) offset--; } else // Failed to find offset! Return 0 offset. offset = 0; return offset;}}#endif // USE(ATSUI)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -