float Font::width(const TextRun& run, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const { CodePath codePathToUse = codePath(run); if (codePathToUse != ComplexPath) { // The complex path is more restrictive about returning fallback fonts than the simple path, so we need an explicit test to make their behaviors match. if (!FontPlatformFeatures::canReturnFallbackFontsForComplexText()) fallbackFonts = 0; // The simple path can optimize the case where glyph overflow is not observable. if (codePathToUse != SimpleWithGlyphOverflowPath && (glyphOverflow && !glyphOverflow->computeBounds)) glyphOverflow = 0; } bool hasKerningOrLigatures = fontDescription().typesettingFeatures() & (Kerning | Ligatures); bool hasWordSpacingOrLetterSpacing = fontDescription().wordSpacing() || fontDescription().letterSpacing(); float* cacheEntry = m_fontFallbackList->widthCache().add(run, std::numeric_limits<float>::quiet_NaN(), hasKerningOrLigatures, hasWordSpacingOrLetterSpacing, glyphOverflow); if (cacheEntry && !std::isnan(*cacheEntry)) return *cacheEntry; float result; if (codePathToUse == ComplexPath) result = floatWidthForComplexText(run, fallbackFonts, glyphOverflow); else result = floatWidthForSimpleText(run, fallbackFonts, glyphOverflow); if (cacheEntry && (!fallbackFonts || fallbackFonts->isEmpty())) *cacheEntry = result; return result; }
void RenderSVGInlineText::computeNewScaledFontForStyle(RenderObject* renderer, const RenderStyle* style, float& scalingFactor, Font& scaledFont) { ASSERT(style); ASSERT(renderer); Document* document = renderer->document(); ASSERT(document); StyleResolver* styleResolver = document->styleResolver(); ASSERT(styleResolver); // Alter font-size to the right on-screen value to avoid scaling the glyphs themselves, except when GeometricPrecision is specified scalingFactor = SVGRenderingContext::calculateScreenFontSizeScalingFactor(renderer); if (scalingFactor == 1 || !scalingFactor || style->fontDescription().textRenderingMode() == GeometricPrecision) { scalingFactor = 1; scaledFont = style->font(); return; } FontDescription fontDescription(style->fontDescription()); // FIXME: We need to better handle the case when we compute very small fonts below (below 1pt). fontDescription.setComputedSize(StyleResolver::getComputedSizeFromSpecifiedSize(document, scalingFactor, fontDescription.isAbsoluteSize(), fontDescription.computedSize(), DoNotUseSmartMinimumForFontSize)); scaledFont = Font(fontDescription, 0, 0); scaledFont.update(styleResolver->fontSelector()); }
void LayoutSVGInlineText::computeNewScaledFontForStyle(LayoutObject* layoutObject, const ComputedStyle* style, float& scalingFactor, Font& scaledFont) { ASSERT(style); ASSERT(layoutObject); // Alter font-size to the right on-screen value to avoid scaling the glyphs themselves, except when GeometricPrecision is specified. scalingFactor = SVGLayoutSupport::calculateScreenFontSizeScalingFactor(layoutObject); if (style->effectiveZoom() == 1 && (scalingFactor == 1 || !scalingFactor)) { scalingFactor = 1; scaledFont = style->font(); return; } if (style->fontDescription().textRendering() == GeometricPrecision) scalingFactor = 1; FontDescription fontDescription(style->fontDescription()); Document& document = layoutObject->document(); // FIXME: We need to better handle the case when we compute very small fonts below (below 1pt). fontDescription.setComputedSize(FontSize::getComputedSizeFromSpecifiedSize(&document, scalingFactor, fontDescription.isAbsoluteSize(), fontDescription.specifiedSize(), DoNotUseSmartMinimumForFontSize)); scaledFont = Font(fontDescription); scaledFont.update(document.styleEngine().fontSelector()); }
int Font::offsetForPosition(const TextRun& run, float x, bool includePartialGlyphs) const { // FIXME: Use the fast code path once it handles partial runs with kerning and ligatures. See http://webkit.org/b/100050 if (codePath(run) != ComplexPath && !fontDescription().typesettingFeatures()) return offsetForPositionForSimpleText(run, x, includePartialGlyphs); return offsetForPositionForComplexText(run, x, includePartialGlyphs); }
void floatWidthMissingGlyphCallback(const TextRun& run, SVGTextRunWalkerMeasuredLengthData& data) { // Handle system font fallback FontDescription fontDescription(data.font->fontDescription()); fontDescription.setFamily(FontFamily()); Font font(fontDescription, 0, 0); // spacing handled by SVG text code. font.update(data.font->fontSelector()); data.length += font.floatWidth(run); }
Font::Font(const FontPlatformData& fontData, bool isPrinterFont, FontSmoothingMode fontSmoothingMode) : m_fontList(FontFallbackList::create()) , m_letterSpacing(0) , m_wordSpacing(0) , m_isPlatformFont(true) { m_fontDescription.setUsePrinterFont(isPrinterFont); m_fontDescription.setFontSmoothing(fontSmoothingMode); m_needsTranscoding = fontTranscoder().needsTranscoding(fontDescription()); m_fontList->setPlatformFont(fontData); }
FloatRect Font::selectionRectForText(const TextRun& run, const FloatPoint& point, int h, int from, int to) const { to = (to == -1 ? run.length() : to); CodePath codePathToUse = codePath(run); // FIXME: Use the fast code path once it handles partial runs with kerning and ligatures. See http://webkit.org/b/100050 if (codePathToUse != ComplexPath && fontDescription().typesettingFeatures() && (from || to != run.length())) codePathToUse = ComplexPath; if (codePathToUse != ComplexPath) return selectionRectForSimpleText(run, point, h, from, to); return selectionRectForComplexText(run, point, h, from, to); }
void Font::drawEmphasisMarks(GraphicsContext* context, const TextRunPaintInfo& runInfo, const AtomicString& mark, const FloatPoint& point) const { if (loadingCustomFonts()) return; CodePath codePathToUse = codePath(runInfo.run); // FIXME: Use the fast code path once it handles partial runs with kerning and ligatures. See http://webkit.org/b/100050 if (codePathToUse != ComplexPath && fontDescription().typesettingFeatures() && (runInfo.from || runInfo.to != runInfo.run.length())) codePathToUse = ComplexPath; if (codePathToUse != ComplexPath) drawEmphasisMarksForSimpleText(context, runInfo, mark, point); else drawEmphasisMarksForComplexText(context, runInfo, mark, point); }
float Font::floatWidthForSimpleText(const TextRun& run, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const { WidthIterator it(this, run, fallbackFonts, glyphOverflow); GlyphBuffer glyphBuffer; it.advance(run.length(), (fontDescription().typesettingFeatures() & (Kerning | Ligatures)) ? &glyphBuffer : 0); if (glyphOverflow) { glyphOverflow->top = max<int>(glyphOverflow->top, ceilf(-it.minGlyphBoundingBoxY()) - (glyphOverflow->computeBounds ? 0 : fontMetrics().ascent())); glyphOverflow->bottom = max<int>(glyphOverflow->bottom, ceilf(it.maxGlyphBoundingBoxY()) - (glyphOverflow->computeBounds ? 0 : fontMetrics().descent())); glyphOverflow->left = ceilf(it.firstGlyphOverflow()); glyphOverflow->right = ceilf(it.lastGlyphOverflow()); } return it.m_runWidthSoFar; }
void Font::drawText(GraphicsContext* context, const TextRunPaintInfo& runInfo, const FloatPoint& point, CustomFontNotReadyAction customFontNotReadyAction) const { // Don't draw anything while we are using custom fonts that are in the process of loading, // except if the 'force' argument is set to true (in which case it will use a fallback // font). if (loadingCustomFonts() && customFontNotReadyAction == DoNotPaintIfFontNotReady) return; CodePath codePathToUse = codePath(runInfo.run); // FIXME: Use the fast code path once it handles partial runs with kerning and ligatures. See http://webkit.org/b/100050 if (codePathToUse != ComplexPath && fontDescription().typesettingFeatures() && (runInfo.from || runInfo.to != runInfo.run.length())) codePathToUse = ComplexPath; if (codePathToUse != ComplexPath) return drawSimpleText(context, runInfo, point); return drawComplexText(context, runInfo, point); }
void RenderSVGInlineText::computeNewScaledFontForStyle(const RenderObject& renderer, const RenderStyle& style, float& scalingFactor, FontCascade& scaledFont) { // Alter font-size to the right on-screen value to avoid scaling the glyphs themselves, except when GeometricPrecision is specified scalingFactor = SVGRenderingContext::calculateScreenFontSizeScalingFactor(renderer); if (scalingFactor == 1 || !scalingFactor || style.fontDescription().textRenderingMode() == GeometricPrecision) { scalingFactor = 1; scaledFont = style.fontCascade(); return; } FontDescription fontDescription(style.fontDescription()); // FIXME: We need to better handle the case when we compute very small fonts below (below 1pt). fontDescription.setComputedSize(Style::computedFontSizeFromSpecifiedSizeForSVGInlineText(fontDescription.computedSize(), fontDescription.isAbsoluteSize(), scalingFactor, renderer.document())); scaledFont = FontCascade(fontDescription, 0, 0); scaledFont.update(&renderer.document().fontSelector()); }
const SimpleFontData* FontCache::getFontDataForCharacters(const Font& font, const UChar* characters, int length) { String familyName; WCHAR name[LF_FACESIZE]; UChar character = characters[0]; const FontPlatformData& origFont = font.primaryFont()->fontDataForCharacter(character)->platformData(); unsigned unicodeRange = findCharUnicodeRange(character); #if defined(IMLANG_FONT_LINK) && (IMLANG_FONT_LINK == 2) if (IMLangFontLink2* langFontLink = getFontLinkInterface()) { #else if (IMLangFontLink* langFontLink = getFontLinkInterface()) { #endif HGDIOBJ oldFont = GetCurrentObject(g_screenDC, OBJ_FONT); HFONT hfont = 0; DWORD codePages = 0; UINT codePage = 0; // Try MLang font linking first. langFontLink->GetCharCodePages(character, &codePages); if (codePages && unicodeRange == cRangeSetCJK) { // The CJK character may belong to multiple code pages. We want to // do font linking against a single one of them, preferring the default // code page for the user's locale. const Vector<DWORD, 4>& CJKCodePageMasks = getCJKCodePageMasks(); unsigned numCodePages = CJKCodePageMasks.size(); for (unsigned i = 0; i < numCodePages; ++i) { #if defined(IMLANG_FONT_LINK) && (IMLANG_FONT_LINK == 2) hfont = createMLangFont(langFontLink, g_screenDC, CJKCodePageMasks[i]); #else hfont = createMLangFont(langFontLink, g_screenDC, origFont, CJKCodePageMasks[i]); #endif if (!hfont) continue; SelectObject(g_screenDC, hfont); GetTextFace(g_screenDC, LF_FACESIZE, name); if (hfont && !(codePages & CJKCodePageMasks[i])) { // We asked about a code page that is not one of the code pages // returned by MLang, so the font might not contain the character. #if defined(IMLANG_FONT_LINK) && (IMLANG_FONT_LINK == 2) if (!currentFontContainsCharacter(langFontLink, g_screenDC, character)) { #else if (!currentFontContainsCharacter(langFontLink, g_screenDC, hfont, character, name)) { #endif SelectObject(g_screenDC, oldFont); langFontLink->ReleaseFont(hfont); hfont = 0; continue; } } break; } } else { #if defined(IMLANG_FONT_LINK) && (IMLANG_FONT_LINK == 2) hfont = createMLangFont(langFontLink, g_screenDC, codePages, character); #else hfont = createMLangFont(langFontLink, g_screenDC, origFont, codePages); #endif SelectObject(g_screenDC, hfont); GetTextFace(g_screenDC, LF_FACESIZE, name); } SelectObject(g_screenDC, oldFont); if (hfont) { familyName = name; langFontLink->ReleaseFont(hfont); } else FontPlatformData::mapKnownFont(codePages, familyName); } if (familyName.isEmpty()) familyName = FontPlatformData::defaultFontFamily(); if (!familyName.isEmpty()) { // FIXME: temporary workaround for Thai font problem FontDescription fontDescription(font.fontDescription()); if (unicodeRange == cRangeThai && fontDescription.weight() > FontWeightNormal) fontDescription.setWeight(FontWeightNormal); FontPlatformData* result = getCachedFontPlatformData(fontDescription, familyName); if (result && result->hash() != origFont.hash()) { if (SimpleFontData* fontData = getCachedFontData(result, DoNotRetain)) return fontData; } } return 0; }
void QtUiStyle::generateSettingsQss() const { QFile settingsQss(Quassel::configDirPath() + "settings.qss"); if(!settingsQss.open(QFile::WriteOnly|QFile::Truncate)) { qWarning() << "Could not open" << settingsQss.fileName() << "for writing!"; return; } QTextStream out(&settingsQss); out << "// Style settings made in Quassel's configuration dialog\n" << "// This file is automatically generated, do not edit\n"; // ChatView /////////// QtUiStyleSettings fs("Fonts"); if(fs.value("UseCustomChatViewFont").toBool()) out << "\n// ChatView Font\n" << "ChatLine { " << fontDescription(fs.value("ChatView").value<QFont>()) << "; }\n"; QtUiStyleSettings s("Colors"); if(s.value("UseChatViewColors").toBool()) { out << "\n// Custom ChatView Colors\n" // markerline is special in that it always used to use a gradient, so we keep this behavior even with the new implementation << "Palette { marker-line: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 " << color("MarkerLine", s) << ", stop: 0.1 transparent); }\n" << "ChatView { background: " << color("ChatViewBackground", s) << "; }\n\n" << "ChatLine[label=\"highlight\"] {\n" << " foreground: " << color("Highlight",s) << ";\n" << " background: " << color("HighlightBackground", s) << ";\n" << "}\n\n" << "ChatLine::timestamp { foreground: " << color("Timestamp", s) << "; }\n\n" << msgTypeQss("plain", "ChannelMsg", s) << msgTypeQss("notice", "ServerMsg", s) << msgTypeQss("action", "ActionMsg", s) << msgTypeQss("nick", "CommandMsg", s) << msgTypeQss("mode", "CommandMsg", s) << msgTypeQss("join", "CommandMsg", s) << msgTypeQss("part", "CommandMsg", s) << msgTypeQss("quit", "CommandMsg", s) << msgTypeQss("kick", "CommandMsg", s) << msgTypeQss("kill", "CommandMsg", s) << msgTypeQss("server", "ServerMsg", s) << msgTypeQss("info", "ServerMsg", s) << msgTypeQss("error", "ErrorMsg", s) << msgTypeQss("daychange", "ServerMsg", s) << msgTypeQss("topic", "CommandMsg", s) << msgTypeQss("netsplit-join", "CommandMsg", s) << msgTypeQss("netsplit-quit", "CommandMsg", s) << msgTypeQss("invite", "CommandMsg", s) << "\n"; } if(s.value("UseSenderColors").toBool()) { out << "\n// Sender Colors\n" << "ChatLine::sender#plain[sender=\"self\"] { foreground: " << color("SenderSelf", s) << "; }\n\n"; for(int i = 0; i < 16; i++) out << senderQss(i, s); } // ItemViews //////////// UiStyleSettings uiFonts("Fonts"); if(uiFonts.value("UseCustomItemViewFont").toBool()) { QString fontDesc = fontDescription(uiFonts.value("ItemView").value<QFont>()); out << "\n// ItemView Font\n" << "ChatListItem { " << fontDesc << "; }\n" << "NickListItem { " << fontDesc << "; }\n\n"; } UiStyleSettings uiColors("Colors"); if(uiColors.value("UseBufferViewColors").toBool()) { out << "\n// BufferView Colors\n" << "ChatListItem { foreground: " << color("DefaultBuffer", uiColors) << "; }\n" << chatListItemQss("inactive", "InactiveBuffer", uiColors) << chatListItemQss("channel-event", "ActiveBuffer", uiColors) << chatListItemQss("unread-message", "UnreadBuffer", uiColors) << chatListItemQss("highlighted", "HighlightedBuffer", uiColors); } if(uiColors.value("UseNickViewColors").toBool()) { out << "\n// NickView Colors\n" << "NickListItem[type=\"category\"] { foreground: " << color("DefaultBuffer", uiColors) << "; }\n" << "NickListItem[type=\"user\"] { foreground: " << color("OnlineNick", uiColors) << "; }\n" << "NickListItem[type=\"user\", state=\"away\"] { foreground: " << color("AwayNick", uiColors) << "; }\n"; } settingsQss.close(); }
// TODO: This needs to be split into helper functions to better scope the // inputs/outputs, and reduce duplicate code. // This issue is tracked in https://bugs.webkit.org/show_bug.cgi?id=62989 void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { COMPILE_ASSERT(sizeof(GlyphBufferGlyph) == sizeof(uint16_t), GlyphBufferGlyphSize_equals_uint16_t); bool shouldSmoothFonts = true; bool shouldAntialias = true; switch (fontDescription().fontSmoothing()) { case Antialiased: shouldSmoothFonts = false; break; case SubpixelAntialiased: break; case NoSmoothing: shouldAntialias = false; shouldSmoothFonts = false; break; case AutoSmoothing: // For the AutoSmooth case, don't do anything! Keep the default settings. break; } if (!shouldUseSmoothing() || PlatformSupport::layoutTestMode()) shouldSmoothFonts = false; const GlyphBufferGlyph* glyphs = glyphBuffer.glyphs(from); SkScalar x = SkFloatToScalar(point.x()); SkScalar y = SkFloatToScalar(point.y()); if (font->platformData().orientation() == Vertical) y += SkFloatToScalar(font->fontMetrics().floatAscent(IdeographicBaseline) - font->fontMetrics().floatAscent()); // FIXME: text rendering speed: // Android has code in their WebCore fork to special case when the // GlyphBuffer has no advances other than the defaults. In that case the // text drawing can proceed faster. However, it's unclear when those // patches may be upstreamed to WebKit so we always use the slower path // here. const GlyphBufferAdvance* adv = glyphBuffer.advances(from); SkAutoSTMalloc<32, SkPoint> storage(numGlyphs); SkPoint* pos = storage.get(); for (int i = 0; i < numGlyphs; i++) { pos[i].set(x, y); x += SkFloatToScalar(adv[i].width); y += SkFloatToScalar(adv[i].height); } SkCanvas* canvas = gc->platformContext()->canvas(); if (font->platformData().orientation() == Vertical) { canvas->save(); canvas->rotate(-90); SkMatrix rotator; rotator.reset(); rotator.setRotate(90); rotator.mapPoints(pos, numGlyphs); } TextDrawingModeFlags textMode = gc->platformContext()->getTextDrawingMode(); // We draw text up to two times (once for fill, once for stroke). if (textMode & TextModeFill) { SkPaint paint; gc->platformContext()->setupPaintForFilling(&paint); setupPaint(&paint, font, this, shouldAntialias, shouldSmoothFonts); gc->platformContext()->adjustTextRenderMode(&paint); paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); canvas->drawPosText(glyphs, numGlyphs * sizeof(uint16_t), pos, paint); } if ((textMode & TextModeStroke) && gc->platformContext()->getStrokeStyle() != NoStroke && gc->platformContext()->getStrokeThickness() > 0) { SkPaint paint; gc->platformContext()->setupPaintForStroking(&paint, 0, 0); setupPaint(&paint, font, this, shouldAntialias, shouldSmoothFonts); gc->platformContext()->adjustTextRenderMode(&paint); paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); if (textMode & TextModeFill) { // If we also filled, we don't want to draw shadows twice. // See comment in FontChromiumWin.cpp::paintSkiaText() for more details. paint.setLooper(0); } canvas->drawPosText(glyphs, numGlyphs * sizeof(uint16_t), pos, paint); } if (font->platformData().orientation() == Vertical) canvas->restore(); }
void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { CGContextRef cgContext = graphicsContext->platformContext(); bool shouldUseFontSmoothing = WebCoreShouldUseFontSmoothing(); switch(fontDescription().fontSmoothing()) { case Antialiased: { graphicsContext->setShouldAntialias(true); shouldUseFontSmoothing = false; break; } case SubpixelAntialiased: { graphicsContext->setShouldAntialias(true); shouldUseFontSmoothing = true; break; } case NoSmoothing: { graphicsContext->setShouldAntialias(false); shouldUseFontSmoothing = false; break; } case AutoSmoothing: { // For the AutoSmooth case, don't do anything! Keep the default settings. break; } default: ASSERT_NOT_REACHED(); } if (font->platformData().useGDI() && !shouldUseFontSmoothing) { drawGDIGlyphs(graphicsContext, font, glyphBuffer, from, numGlyphs, point); return; } uint32_t oldFontSmoothingStyle = wkSetFontSmoothingStyle(cgContext, shouldUseFontSmoothing); const FontPlatformData& platformData = font->platformData(); CGContextSetFont(cgContext, platformData.cgFont()); CGAffineTransform matrix = CGAffineTransformIdentity; matrix.b = -matrix.b; matrix.d = -matrix.d; if (platformData.syntheticOblique()) { static float skew = -tanf(syntheticObliqueAngle * piFloat / 180.0f); matrix = CGAffineTransformConcat(matrix, CGAffineTransformMake(1, 0, skew, 1, 0, 0)); } CGContextSetTextMatrix(cgContext, matrix); // Uniscribe gives us offsets to help refine the positioning of combining glyphs. FloatSize translation = glyphBuffer.offsetAt(from); CGContextSetFontSize(cgContext, platformData.size()); wkSetCGContextFontRenderingStyle(cgContext, font->isSystemFont(), false, font->platformData().useGDI()); FloatSize shadowOffset; float shadowBlur; Color shadowColor; ColorSpace shadowColorSpace; graphicsContext->getShadow(shadowOffset, shadowBlur, shadowColor, shadowColorSpace); bool hasSimpleShadow = graphicsContext->textDrawingMode() == TextModeFill && shadowColor.isValid() && !shadowBlur && (!graphicsContext->shadowsIgnoreTransforms() || graphicsContext->getCTM().isIdentityOrTranslationOrFlipped()); 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, ColorSpaceDeviceRGB); float shadowTextX = point.x() + translation.width() + shadowOffset.width(); // If shadows are ignoring transforms, then we haven't applied the Y coordinate flip yet, so down is negative. float shadowTextY = point.y() + translation.height() + shadowOffset.height() * (graphicsContext->shadowsIgnoreTransforms() ? -1 : 1); CGContextSetTextPosition(cgContext, shadowTextX, shadowTextY); CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); if (font->syntheticBoldOffset()) { CGContextSetTextPosition(cgContext, point.x() + translation.width() + shadowOffset.width() + font->syntheticBoldOffset(), point.y() + translation.height() + shadowOffset.height()); CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); } graphicsContext->setFillColor(fillColor, ColorSpaceDeviceRGB); } CGContextSetTextPosition(cgContext, point.x() + translation.width(), point.y() + translation.height()); CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); if (font->syntheticBoldOffset()) { CGContextSetTextPosition(cgContext, point.x() + translation.width() + font->syntheticBoldOffset(), point.y() + translation.height()); CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); } if (hasSimpleShadow) graphicsContext->setShadow(shadowOffset, shadowBlur, shadowColor, ColorSpaceDeviceRGB); wkRestoreFontSmoothingStyle(cgContext, oldFontSmoothingStyle); }
int RenderStyle::fontSize() const { return fontDescription().computedPixelSize(); }
PassRefPtr<SimpleFontData> FontCache::systemFallbackForCharacters(const FontDescription& description, const SimpleFontData* originalFontData, bool, const UChar* characters, int length) { String familyName; WCHAR name[LF_FACESIZE]; UChar character = characters[0]; const FontPlatformData& origFont = originalFontData->platformData(); if (IMLangFontLinkType* langFontLink = getFontLinkInterface()) { HGDIOBJ oldFont = GetCurrentObject(g_screenDC, OBJ_FONT); HFONT hfont = 0; DWORD codePages = 0; UINT codePage = 0; // Try MLang font linking first. langFontLink->GetCharCodePages(character, &codePages); if (codePages && u_getIntPropertyValue(character, UCHAR_UNIFIED_IDEOGRAPH)) { // The CJK character may belong to multiple code pages. We want to // do font linking against a single one of them, preferring the default // code page for the user's locale. const Vector<DWORD, 4>& CJKCodePageMasks = getCJKCodePageMasks(); unsigned numCodePages = CJKCodePageMasks.size(); for (unsigned i = 0; i < numCodePages; ++i) { hfont = createMLangFont(langFontLink, g_screenDC, origFont, CJKCodePageMasks[i]); if (!hfont) continue; SelectObject(g_screenDC, hfont); GetTextFace(g_screenDC, LF_FACESIZE, name); if (hfont && !(codePages & CJKCodePageMasks[i])) { // We asked about a code page that is not one of the code pages // returned by MLang, so the font might not contain the character. if (!currentFontContainsCharacter(langFontLink, g_screenDC, hfont, character, name)) { SelectObject(g_screenDC, oldFont); langFontLink->ReleaseFont(hfont); hfont = 0; continue; } } break; } } else { hfont = createMLangFont(langFontLink, g_screenDC, origFont, codePages, character); SelectObject(g_screenDC, hfont); GetTextFace(g_screenDC, LF_FACESIZE, name); } SelectObject(g_screenDC, oldFont); if (hfont) { familyName = name; langFontLink->ReleaseFont(hfont); } else FontPlatformData::mapKnownFont(codePages, familyName); } if (familyName.isEmpty()) familyName = FontPlatformData::defaultFontFamily(); if (!familyName.isEmpty()) { // FIXME: temporary workaround for Thai font problem FontDescription fontDescription(description); if (ublock_getCode(c) == UBLOCK_THAI && fontDescription.weight() > FontWeightNormal) fontDescription.setWeight(FontWeightNormal); FontPlatformData* result = getCachedFontPlatformData(fontDescription, familyName); if (result && result->hash() != origFont.hash()) { if (RefPtr<SimpleFontData> fontData = getCachedFontData(result, DoNotRetain)) return fontData.release(); } } return 0; }
FontWeight RenderStyle::fontWeight() const { return fontDescription().weight(); }
void QtUiStyle::generateSettingsQss() const { QFile settingsQss(Quassel::configDirPath() + "settings.qss"); if (!settingsQss.open(QFile::WriteOnly | QFile::Truncate)) { qWarning() << "Could not open" << settingsQss.fileName() << "for writing!"; return; } QTextStream out(&settingsQss); out << "// Style settings made in Quassel's configuration dialog\n" << "// This file is automatically generated, do not edit\n"; // ChatView /////////// QtUiStyleSettings fs("Fonts"); if (fs.value("UseCustomChatViewFont").toBool()) out << "\n// ChatView Font\n" << "ChatLine { " << fontDescription(fs.value("ChatView").value<QFont>()) << "; }\n"; QtUiStyleSettings s("Colors"); if (s.value("UseChatViewColors").toBool()) { out << "\n// Custom ChatView Colors\n" // markerline is special in that it always used to use a gradient, so we keep this behavior even with the new implementation << "Palette { marker-line: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 " << color("MarkerLine", s) << ", stop: 0.1 transparent); }\n" << "ChatView { background: " << color("ChatViewBackground", s) << "; }\n\n" << "ChatLine[label=\"highlight\"] {\n" << " foreground: " << color("Highlight", s) << ";\n" << " background: " << color("HighlightBackground", s) << ";\n" << "}\n\n" << "ChatLine::timestamp { foreground: " << color("Timestamp", s) << "; }\n\n" << msgTypeQss("plain", "ChannelMsg", s) << msgTypeQss("notice", "ServerMsg", s) << msgTypeQss("action", "ActionMsg", s) << msgTypeQss("nick", "CommandMsg", s) << msgTypeQss("mode", "CommandMsg", s) << msgTypeQss("join", "CommandMsg", s) << msgTypeQss("part", "CommandMsg", s) << msgTypeQss("quit", "CommandMsg", s) << msgTypeQss("kick", "CommandMsg", s) << msgTypeQss("kill", "CommandMsg", s) << msgTypeQss("server", "ServerMsg", s) << msgTypeQss("info", "ServerMsg", s) << msgTypeQss("error", "ErrorMsg", s) << msgTypeQss("daychange", "ServerMsg", s) << msgTypeQss("topic", "CommandMsg", s) << msgTypeQss("netsplit-join", "CommandMsg", s) << msgTypeQss("netsplit-quit", "CommandMsg", s) << msgTypeQss("invite", "CommandMsg", s) << "\n"; } if (s.value("UseSenderColors", true).toBool()) { out << "\n// Sender Colors\n"; // Generate a color palette for easy reuse elsewhere // NOTE: A color palette is not a complete replacement for specifying the colors below, as // specifying the colors one-by-one instead of with QtUi::style()->brush(...) makes it easy // to toggle the specific coloring of sender/nick at the cost of regenerating this file. // See UiStyle::ColorRole out << senderPaletteQss(s); out << "ChatLine::sender#plain[sender=\"self\"] { foreground: palette(sender-color-self); }\n\n"; // Matches qssparser.cpp for UiStyle::PlainMsg for (int i = 0; i < defaultSenderColors.count(); i++) out << senderQss(i, "plain"); // Only color the nicks in CTCP ACTIONs if sender colors are enabled if (s.value("UseSenderActionColors", true).toBool()) { // For action messages, color the 'sender' column -and- the nick itself out << "\n// Sender Nickname Colors for action messages\n" << "ChatLine::sender#action[sender=\"self\"] { foreground: palette(sender-color-self); }\n" << "ChatLine::nick#action[sender=\"self\"] { foreground: palette(sender-color-self); }\n\n"; // Matches qssparser.cpp for UiStyle::ActionMsg for (int i = 0; i < defaultSenderColors.count(); i++) out << senderQss(i, "action", true); } // Only color the nicks in CTCP ACTIONs if sender colors are enabled if (s.value("UseNickGeneralColors", true).toBool()) { // For action messages, color the 'sender' column -and- the nick itself out << "\n// Nickname colors for all messages\n" << "ChatLine::nick[sender=\"self\"] { foreground: palette(sender-color-self); }\n\n"; // Matches qssparser.cpp for any style of message (UiStyle::...) for (int i = 0; i < defaultSenderColors.count(); i++) out << nickQss(i); } } // ItemViews //////////// UiStyleSettings uiFonts("Fonts"); if (uiFonts.value("UseCustomItemViewFont").toBool()) { QString fontDesc = fontDescription(uiFonts.value("ItemView").value<QFont>()); out << "\n// ItemView Font\n" << "ChatListItem { " << fontDesc << "; }\n" << "NickListItem { " << fontDesc << "; }\n\n"; } UiStyleSettings uiColors("Colors"); if (uiColors.value("UseBufferViewColors").toBool()) { out << "\n// BufferView Colors\n" << "ChatListItem { foreground: " << color("DefaultBuffer", uiColors) << "; }\n" << chatListItemQss("inactive", "InactiveBuffer", uiColors) << chatListItemQss("channel-event", "ActiveBuffer", uiColors) << chatListItemQss("unread-message", "UnreadBuffer", uiColors) << chatListItemQss("highlighted", "HighlightedBuffer", uiColors); } if (uiColors.value("UseNickViewColors").toBool()) { out << "\n// NickView Colors\n" << "NickListItem[type=\"category\"] { foreground: " << color("DefaultBuffer", uiColors) << "; }\n" << "NickListItem[type=\"user\"] { foreground: " << color("OnlineNick", uiColors) << "; }\n" << R"(NickListItem[type="user", state="away"] { foreground: )" << color("AwayNick", uiColors) << "; }\n"; }
float RenderStyle::computedFontSize() const { return fontDescription().computedSize(); }
void Font::drawTextUsingSVGFont(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int from, int to) const { SVGFontElement* fontElement = 0; SVGFontFaceElement* fontFaceElement = 0; if (const SVGFontData* fontData = svgFontAndFontFaceElementForFontData(primaryFont(), fontFaceElement, fontElement)) { if (!fontElement) return; SVGTextRunWalkerDrawTextData data; FloatPoint currentPoint = point; float scale = convertEmUnitToPixel(size(), fontFaceElement->unitsPerEm(), 1.0f); SVGPaintServer* activePaintServer = run.activePaintServer(); // If renderObject is not set, we're dealing for HTML text rendered using SVG Fonts. if (!run.referencingRenderObject()) { ASSERT(!activePaintServer); // TODO: We're only supporting simple filled HTML text so far. SVGPaintServerSolid* solidPaintServer = SVGPaintServer::sharedSolidPaintServer(); solidPaintServer->setColor(context->fillColor()); activePaintServer = solidPaintServer; } ASSERT(activePaintServer); int charsConsumed; String glyphName; bool isVerticalText = false; float xStartOffset = floatWidthOfSubStringUsingSVGFont(this, run, 0, run.rtl() ? to : 0, run.rtl() ? run.length() : from, charsConsumed, glyphName); FloatPoint glyphOrigin; String language; // TODO: language matching & svg glyphs should be possible for HTML text, too. if (run.referencingRenderObject()) { isVerticalText = isVerticalWritingMode(run.referencingRenderObject()->style()->svgStyle()); if (SVGElement* element = static_cast<SVGElement*>(run.referencingRenderObject()->element())) language = element->getAttribute(XMLNames::langAttr); } if (!isVerticalText) { glyphOrigin.setX(fontData->horizontalOriginX() * scale); glyphOrigin.setY(fontData->horizontalOriginY() * scale); } data.extraCharsAvailable = 0; data.charsConsumed = 0; SVGTextRunWalker<SVGTextRunWalkerDrawTextData> runWalker(fontData, fontElement, data, drawTextUsingSVGFontCallback, drawTextMissingGlyphCallback); runWalker.walk(run, isVerticalText, language, from, to); SVGPaintTargetType targetType = context->textDrawingMode() == cTextStroke ? ApplyToStrokeTargetType : ApplyToFillTargetType; unsigned numGlyphs = data.glyphIdentifiers.size(); unsigned fallbackCharacterIndex = 0; for (unsigned i = 0; i < numGlyphs; ++i) { const SVGGlyphIdentifier& identifier = data.glyphIdentifiers[run.rtl() ? numGlyphs - i - 1 : i]; if (identifier.isValid) { // FIXME: Support arbitary SVG content as glyph (currently limited to <glyph d="..."> situations). if (!identifier.pathData.isEmpty()) { context->save(); if (isVerticalText) { glyphOrigin.setX(identifier.verticalOriginX * scale); glyphOrigin.setY(identifier.verticalOriginY * scale); } context->translate(xStartOffset + currentPoint.x() + glyphOrigin.x(), currentPoint.y() + glyphOrigin.y()); context->scale(FloatSize(scale, -scale)); context->beginPath(); context->addPath(identifier.pathData); if (activePaintServer->setup(context, run.referencingRenderObject(), targetType)) { // Spec: Any properties specified on a text elements which represents a length, such as the // 'stroke-width' property, might produce surprising results since the length value will be // processed in the coordinate system of the glyph. (TODO: What other lengths? miter-limit? dash-offset?) if (targetType == ApplyToStrokeTargetType && scale != 0.0f) context->setStrokeThickness(context->strokeThickness() / scale); activePaintServer->renderPath(context, run.referencingRenderObject(), targetType); activePaintServer->teardown(context, run.referencingRenderObject(), targetType); } context->restore(); } if (isVerticalText) currentPoint.move(0.0f, identifier.verticalAdvanceY * scale); else currentPoint.move(identifier.horizontalAdvanceX * scale, 0.0f); } else { // Handle system font fallback FontDescription fontDescription(context->font().fontDescription()); fontDescription.setFamily(FontFamily()); Font font(fontDescription, 0, 0); // spacing handled by SVG text code. font.update(context->font().fontSelector()); TextRun fallbackCharacterRun(run); fallbackCharacterRun.setText(&data.fallbackCharacters[run.rtl() ? data.fallbackCharacters.size() - fallbackCharacterIndex - 1 : fallbackCharacterIndex], 1); font.drawText(context, fallbackCharacterRun, currentPoint); if (isVerticalText) currentPoint.move(0.0f, font.floatWidth(fallbackCharacterRun)); else currentPoint.move(font.floatWidth(fallbackCharacterRun), 0.0f); fallbackCharacterIndex++; } } } }
FontStretch RenderStyle::fontStretch() const { return fontDescription().stretch(); }
void Font::willUseFontData() const { const FontFamily& family = fontDescription().family(); if (m_fontFallbackList && m_fontFallbackList->fontSelector() && !family.familyIsEmpty()) m_fontFallbackList->fontSelector()->willUseFontData(fontDescription(), family.family()); }
float RenderStyle::specifiedFontSize() const { return fontDescription().specifiedSize(); }