From 493c5396ff1d1938b8614f42129355c37379a4ef Mon Sep 17 00:00:00 2001 From: David Schulz Date: Tue, 14 Aug 2018 06:55:56 +0200 Subject: [PATCH] GenericHighlighter: check text color against background The kate syntax highlighter format allows to directly assign a color for a specific item. This could result in a bad contrast ratio between text and background. Check the contrast ratio according to W3C Recommendation and apply if it exceeds the minimum contrast ratio for large text. (https://www.w3.org/TR/2008/REC-WCAG20-20081211/#visual-audio-contrast- contrast) Task-number: QTCREATORBUG-20919 Change-Id: If5a5d09224446df72f31027cd30e50088179d6d7 Reviewed-by: Christian Stenger --- .../generichighlighter/highlighter.cpp | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/plugins/texteditor/generichighlighter/highlighter.cpp b/src/plugins/texteditor/generichighlighter/highlighter.cpp index fd875eae28c..055f3902614 100644 --- a/src/plugins/texteditor/generichighlighter/highlighter.cpp +++ b/src/plugins/texteditor/generichighlighter/highlighter.cpp @@ -498,6 +498,43 @@ void Highlighter::handleContextChange(const QString &contextName, changeContext(contextName, definition, setCurrent); } + +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 float 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 count, const QString &itemDataName, @@ -526,7 +563,10 @@ void Highlighter::applyFormat(int offset, // strategy). This is because the highlighter does not really know on which // definition(s) it is working. Since not many item data specify customizations I // think this approach would fit better. If there are other ideas... - if (itemData->color().isValid()) + QBrush bg = format.background(); + if (bg.style() == Qt::NoBrush) + bg = formatForCategory(C_TEXT).background(); + if (itemData->color().isValid() && isReadableOn(bg.color(), itemData->color())) format.setForeground(itemData->color()); if (itemData->isItalicSpecified()) format.setFontItalic(itemData->isItalic());