diff --git a/src/plugins/texteditor/highlighter.cpp b/src/plugins/texteditor/highlighter.cpp index bbeff2a4844..1a12d968822 100644 --- a/src/plugins/texteditor/highlighter.cpp +++ b/src/plugins/texteditor/highlighter.cpp @@ -44,6 +44,8 @@ #include #include +#include + using namespace TextEditor; static const char kDefinitionForMimeType[] = "definitionForMimeType"; @@ -322,9 +324,77 @@ void Highlighter::highlightBlock(const QString &text) formatSpaces(text); } +static double luminance(const QColor &color) +{ + // calculate the luminance based on + // https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef + auto val = [](const double &colorVal) { + return colorVal < 0.03928 ? colorVal / 12.92 : std::pow((colorVal + 0.055) / 1.055, 2.4); + }; + + static QHash cache; + QHash::iterator it = cache.find(color.rgb()); + if (it == cache.end()) { + it = cache.insert(color.rgb(), 0.2126 * val(color.redF()) + + 0.7152 * val(color.greenF()) + + 0.0722 * val(color.blueF())); + } + return it.value(); +} + +static double contrastRatio(const QColor &color1, const QColor &color2) +{ + // calculate the contrast ratio based on + // https://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef + auto contrast = (luminance(color1) + 0.05) / (luminance(color2) + 0.05); + if (contrast < 1) + return 1 / contrast; + return contrast; +} + +static bool isReadableOn(const QColor &background, const QColor &foreground) +{ + // following the W3C Recommendation on contrast for large Text + // https://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef + return contrastRatio(background, foreground) > 3; +} + void Highlighter::applyFormat(int offset, int length, const KSyntaxHighlighting::Format &format) { - setFormat(offset, length, formatForCategory(format.textStyle())); + const KSyntaxHighlighting::Theme defaultTheme; + QTextCharFormat qformat = formatForCategory(format.textStyle()); + + if (format.hasTextColor(defaultTheme)) { + const QColor textColor = format.textColor(defaultTheme); + if (format.hasBackgroundColor(defaultTheme)) { + const QColor backgroundColor = format.hasBackgroundColor(defaultTheme); + if (isReadableOn(backgroundColor, textColor)) { + qformat.setForeground(textColor); + qformat.setBackground(backgroundColor); + } else if (isReadableOn(qformat.background().color(), textColor)) { + qformat.setForeground(textColor); + } + } else if (isReadableOn(qformat.background().color(), textColor)) { + qformat.setForeground(textColor); + } + } else if (format.hasBackgroundColor(defaultTheme)) { + const QColor backgroundColor = format.hasBackgroundColor(defaultTheme); + if (isReadableOn(backgroundColor, qformat.foreground().color())) + qformat.setBackground(backgroundColor); + } + + if (format.isBold(defaultTheme)) + qformat.setFontWeight(QFont::Bold); + + if (format.isItalic(defaultTheme)) + qformat.setFontItalic(true); + + if (format.isUnderline(defaultTheme)) + qformat.setFontUnderline(true); + + if (format.isStrikeThrough(defaultTheme)) + qformat.setFontStrikeOut(true); + setFormat(offset, length, qformat); } void Highlighter::applyFolding(int offset,