File indexing completed on 2024-12-08 12:19:29

0001 /*
0002  * This file is part of the CSS implementation for KDE.
0003  *
0004  * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
0005  *           (C) David Carson  <dacarson@gmail.com>
0006  *
0007  * This library is free software; you can redistribute it and/or
0008  * modify it under the terms of the GNU Library General Public
0009  * License as published by the Free Software Foundation; either
0010  * version 2 of the License, or (at your option) any later version.
0011  *
0012  * This library is distributed in the hope that it will be useful,
0013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0015  * Library General Public License for more details.
0016  *
0017  * You should have received a copy of the GNU Library General Public License
0018  * along with this library; see the file COPYING.LIB.  If not, write to
0019  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0020  * Boston, MA 02110-1301, USA.
0021  *
0022  */
0023 #include "helper.h"
0024 #include "khtmllayout.h"
0025 #include <QMap>
0026 #include <QPainter>
0027 #include <dom/dom_string.h>
0028 #include <xml/dom_stringimpl.h>
0029 #include <rendering/render_object.h>
0030 #include <kconfig.h>
0031 #include <kcolorscheme.h>
0032 #include <ksharedconfig.h>
0033 #include <kconfiggroup.h>
0034 #include <QToolTip>
0035 #include "css/cssvalues.h"
0036 
0037 using namespace DOM;
0038 using namespace khtml;
0039 
0040 namespace khtml
0041 {
0042 
0043 QPainter *printpainter;
0044 
0045 void setPrintPainter(QPainter *printer)
0046 {
0047     printpainter = printer;
0048 }
0049 
0050 void findWordBoundary(QChar *chars, int len, int position, int *start, int *end)
0051 {
0052     if (chars[position].isSpace()) {
0053         int pos = position;
0054         while (pos >= 0 && chars[pos].isSpace()) {
0055             pos--;
0056         }
0057         *start = pos + 1;
0058         pos = position;
0059         while (pos < (int)len && chars[pos].isSpace()) {
0060             pos++;
0061         }
0062         *end = pos;
0063     } else if (chars[position].isPunct()) {
0064         int pos = position;
0065         while (pos >= 0 && chars[pos].isPunct()) {
0066             pos--;
0067         }
0068         *start = pos + 1;
0069         pos = position;
0070         while (pos < (int)len && chars[pos].isPunct()) {
0071             pos++;
0072         }
0073         *end = pos;
0074     } else {
0075         int pos = position;
0076         while (pos >= 0 && !chars[pos].isSpace() && !chars[pos].isPunct()) {
0077             pos--;
0078         }
0079         *start = pos + 1;
0080         pos = position;
0081         while (pos < (int)len && !chars[pos].isSpace() && !chars[pos].isPunct()) {
0082             pos++;
0083         }
0084         *end = pos;
0085     }
0086 }
0087 
0088 }
0089 
0090 // color mapping code
0091 struct colorMap {
0092     int css_value;
0093     QRgb color;
0094 };
0095 
0096 static const colorMap cmap[] = {
0097     { CSS_VAL_AQUA, 0xFF00FFFF },
0098     { CSS_VAL_BLACK, 0xFF000000 },
0099     { CSS_VAL_BLUE, 0xFF0000FF },
0100     { CSS_VAL_CRIMSON, 0xFFDC143C },
0101     { CSS_VAL_FUCHSIA, 0xFFFF00FF },
0102     { CSS_VAL_GRAY, 0xFF808080 },
0103     { CSS_VAL_GREEN, 0xFF008000  },
0104     { CSS_VAL_INDIGO, 0xFF4B0082 },
0105     { CSS_VAL_LIME, 0xFF00FF00 },
0106     { CSS_VAL_MAROON, 0xFF800000 },
0107     { CSS_VAL_NAVY, 0xFF000080 },
0108     { CSS_VAL_OLIVE, 0xFF808000  },
0109     { CSS_VAL_ORANGE, 0xFFFFA500 },
0110     { CSS_VAL_PURPLE, 0xFF800080 },
0111     { CSS_VAL_RED, 0xFFFF0000 },
0112     { CSS_VAL_SILVER, 0xFFC0C0C0 },
0113     { CSS_VAL_TEAL, 0xFF008080  },
0114     { CSS_VAL_WHITE, 0xFFFFFFFF },
0115     { CSS_VAL_YELLOW, 0xFFFFFF00 },
0116     { CSS_VAL_TRANSPARENT, transparentColor },
0117     { CSS_VAL_GREY, 0xff808080 },
0118     { 0, 0 }
0119 };
0120 
0121 struct uiColors {
0122     int css_value;
0123     QPalette::ColorGroup group;
0124     QPalette::ColorRole role;
0125 };
0126 
0127 // CSS 2.1 system color mapping
0128 static const uiColors uimap[] = {
0129     // MDI background color
0130     { CSS_VAL_APPWORKSPACE, QPalette::Normal, QPalette::Mid },
0131     // Button colors
0132     { CSS_VAL_BUTTONFACE, QPalette::Normal, QPalette::Button },
0133     { CSS_VAL_BUTTONHIGHLIGHT, QPalette::Normal, QPalette::Light },
0134     { CSS_VAL_BUTTONSHADOW, QPalette::Normal, QPalette::Dark },
0135     { CSS_VAL_BUTTONTEXT, QPalette::Normal, QPalette::ButtonText },
0136     // Disabled text
0137     { CSS_VAL_GRAYTEXT, QPalette::Disabled, QPalette::Text },
0138     // Selected items
0139     { CSS_VAL_HIGHLIGHTTEXT, QPalette::Normal, QPalette::HighlightedText },
0140     { CSS_VAL_HIGHLIGHT, QPalette::Normal, QPalette::Highlight },
0141     // Tooltips
0142     { CSS_VAL_INFOBACKGROUND, QPalette::Normal, QPalette::ToolTipBase },
0143     { CSS_VAL_INFOTEXT, QPalette::Normal, QPalette::ToolTipText },
0144     // Menu colors
0145     { CSS_VAL_MENU, QPalette::Normal, QPalette::Window },
0146     { CSS_VAL_MENUTEXT, QPalette::Normal, QPalette::Text },
0147     // Scroll bar color
0148     { CSS_VAL_SCROLLBAR, QPalette::Normal, QPalette::Window },
0149     // 3D elements
0150     { CSS_VAL_THREEDDARKSHADOW, QPalette::Normal, QPalette::Dark },
0151     { CSS_VAL_THREEDFACE, QPalette::Normal, QPalette::Button },
0152     { CSS_VAL_THREEDHIGHLIGHT, QPalette::Normal, QPalette::Light },
0153     { CSS_VAL_THREEDLIGHTSHADOW, QPalette::Normal, QPalette::Midlight },
0154     { CSS_VAL_THREEDSHADOW, QPalette::Normal, QPalette::Mid },
0155     // Window background
0156     { CSS_VAL_WINDOW, QPalette::Normal, QPalette::Base },
0157     // Window frame
0158     { CSS_VAL_WINDOWFRAME, QPalette::Normal, QPalette::Window },
0159     // WindowText
0160     { CSS_VAL_WINDOWTEXT, QPalette::Normal, QPalette::Text },
0161     { CSS_VAL_TEXT, QPalette::Normal, QPalette::Text },
0162     { 0, QPalette::NColorGroups, QPalette::NColorRoles }
0163 };
0164 
0165 QColor khtml::colorForCSSValue(int css_value)
0166 {
0167     // try the regular ones first
0168     const colorMap *col = cmap;
0169     while (col->css_value && col->css_value != css_value) {
0170         ++col;
0171     }
0172     if (col->css_value) {
0173         return QColor::fromRgba(col->color);
0174     } else if (css_value == CSS_VAL_INVERT) {
0175         return QColor();
0176     }
0177 
0178     const uiColors *uicol = uimap;
0179     while (uicol->css_value && uicol->css_value != css_value) {
0180         ++uicol;
0181     }
0182 #ifndef APPLE_CHANGES
0183     if (!uicol->css_value) {
0184         switch (css_value) {
0185         case CSS_VAL_ACTIVEBORDER:
0186             return qApp->palette().color(QPalette::Normal, QPalette::Window);
0187         case CSS_VAL_ACTIVECAPTION:
0188             return KColorScheme(QPalette::Active, KColorScheme::Window).background(KColorScheme::ActiveBackground).color();
0189         case CSS_VAL_CAPTIONTEXT:
0190             return KColorScheme(QPalette::Active, KColorScheme::Window).foreground(KColorScheme::ActiveText).color();
0191         case CSS_VAL_INACTIVEBORDER:
0192             return qApp->palette().color(QPalette::Inactive, QPalette::Window);
0193         case CSS_VAL_INACTIVECAPTION:
0194             return KColorScheme(QPalette::Inactive, KColorScheme::Window).background().color();
0195         case CSS_VAL_INACTIVECAPTIONTEXT:
0196             return KColorScheme(QPalette::Inactive, KColorScheme::Window).foreground().color();
0197         case CSS_VAL_BACKGROUND: // Desktop background - no way to get this information from Plasma
0198             return qApp->palette().color(QPalette::Normal, QPalette::Highlight);
0199         default:
0200             return QColor();
0201         }
0202     }
0203 #endif
0204 
0205     const QPalette &pal = qApp->palette();
0206     return pal.color(uicol->group, uicol->role);
0207 }
0208 
0209 double calcHue(double temp1, double temp2, double hueVal)
0210 {
0211     if (hueVal < 0) {
0212         hueVal++;
0213     } else if (hueVal > 1) {
0214         hueVal--;
0215     }
0216     if (hueVal * 6 < 1) {
0217         return temp1 + (temp2 - temp1) * hueVal * 6;
0218     }
0219     if (hueVal * 2 < 1) {
0220         return temp2;
0221     }
0222     if (hueVal * 3 < 2) {
0223         return temp1 + (temp2 - temp1) * (2.0 / 3.0 - hueVal) * 6;
0224     }
0225     return temp1;
0226 }
0227 
0228 // Explanation of this algorithm can be found in the CSS3 Color Module
0229 // specification at https://www.w3.org/TR/css3-color/#hsl-color with further
0230 // explanation available at https://en.wikipedia.org/wiki/HSL_and_HSV
0231 
0232 // all values are in the range of 0 to 1.0
0233 QRgb khtml::qRgbaFromHsla(double h, double s, double l, double a)
0234 {
0235     double temp2 = l < 0.5 ? l * (1.0 + s) : l + s - l * s;
0236     double temp1 = 2.0 * l - temp2;
0237 
0238     return qRgba(static_cast<int>(calcHue(temp1, temp2, h + 1.0 / 3.0) * 255),
0239                  static_cast<int>(calcHue(temp1, temp2, h) * 255),
0240                  static_cast<int>(calcHue(temp1, temp2, h - 1.0 / 3.0) * 255),
0241                  static_cast<int>(a * 255));
0242 }
0243 
0244 /** finds out the background color of an element
0245  * @param obj render object
0246  * @return the background color. It is guaranteed that a valid color is returned.
0247  */
0248 QColor khtml::retrieveBackgroundColor(const RenderObject *obj)
0249 {
0250     QColor result;
0251     while (!obj->isCanvas()) {
0252         result = obj->style()->backgroundColor();
0253         if (result.isValid()) {
0254             return result;
0255         }
0256 
0257         obj = obj->container();
0258     }/*wend*/
0259 
0260     // everything transparent? Use base then.
0261     return obj->style()->palette().color(QPalette::Active, QPalette::Base);
0262 }
0263 
0264 /** checks whether the given colors have enough contrast
0265  * @returns @p true if contrast is ok.
0266  */
0267 bool khtml::hasSufficientContrast(const QColor &c1, const QColor &c2)
0268 {
0269 // New version from Germain Garand, better suited for contrast measurement
0270 #if 1
0271 
0272 #define HUE_DISTANCE 40
0273 #define CONTRAST_DISTANCE 10
0274 
0275     int h1, s1, v1, h2, s2, v2;
0276     int hdist = -CONTRAST_DISTANCE;
0277     c1.getHsv(&h1, &s1, &v1);
0278     c2.getHsv(&h2, &s2, &v2);
0279     if (h1 != -1 && h2 != -1) { // grey values have no hue
0280         hdist = qAbs(h1 - h2);
0281         if (hdist > 180) {
0282             hdist = 360 - hdist;
0283         }
0284         if (hdist < HUE_DISTANCE) {
0285             hdist -= HUE_DISTANCE;
0286             // see if they are high key or low key colours
0287             bool hk1 = h1 >= 45 && h1 <= 225;
0288             bool hk2 = h2 >= 45 && h2 <= 225;
0289             if (hk1 && hk2) {
0290                 hdist = (5 * hdist) / 3;
0291             } else if (!hk1 && !hk2) {
0292                 hdist = (7 * hdist) / 4;
0293             }
0294         }
0295         hdist = qMin(hdist, HUE_DISTANCE * 2);
0296     }
0297     return hdist + (qAbs(s1 - s2) * 128) / (160 + qMin(s1, s2)) + qAbs(v1 - v2) > CONTRAST_DISTANCE;
0298 
0299 #undef CONTRAST_DISTANCE
0300 #undef HUE_DISTANCE
0301 
0302 #else   // orginal fast but primitive version by me (LS)
0303 
0304 // ### arbitrary value, to be adapted if necessary (LS)
0305 #define CONTRAST_DISTANCE 32
0306 
0307     if (qAbs(c1.Qt::red() - c2.Qt::red()) > CONTRAST_DISTANCE) {
0308         return true;
0309     }
0310     if (qAbs(c1.Qt::green() - c2.Qt::green()) > CONTRAST_DISTANCE) {
0311         return true;
0312     }
0313     if (qAbs(c1.Qt::blue() - c2.Qt::blue()) > CONTRAST_DISTANCE) {
0314         return true;
0315     }
0316 
0317     return false;
0318 
0319 #undef CONTRAST_DISTANCE
0320 
0321 #endif
0322 }