Warning, file /office/calligra/libs/flake/KoHatchBackground.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /* This file is part of the KDE project
0002  *
0003  * Copyright (C) 2012 Thorsten Zachmann <zachmann@kde.org>
0004  *
0005  * This library is free software; you can redistribute it and/or
0006  * modify it under the terms of the GNU Library General Public
0007  * License as published by the Free Software Foundation; either
0008  * version 2 of the License, or (at your option) any later version.
0009  *
0010  * This library is distributed in the hope that it will be useful,
0011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013  * Library General Public License for more details.
0014  *
0015  * You should have received a copy of the GNU Library General Public License
0016  * along with this library; see the file COPYING.LIB.  If not, write to
0017  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018  * Boston, MA 02110-1301, USA.
0019  */
0020 
0021 #include "KoHatchBackground.h"
0022 #include "KoColorBackground_p.h"
0023 
0024 #include <KoOdfLoadingContext.h>
0025 #include <KoStyleStack.h>
0026 #include <KoShapeSavingContext.h>
0027 #include <KoGenStyles.h>
0028 #include <KoGenStyle.h>
0029 #include <KoXmlNS.h>
0030 #include <KoUnit.h>
0031 
0032 #include <FlakeDebug.h>
0033 
0034 #include <QColor>
0035 #include <QString>
0036 #include <QPainter>
0037 #include <QPainterPath>
0038 
0039 class KoHatchBackgroundPrivate : public KoColorBackgroundPrivate
0040 {
0041 public:
0042     KoHatchBackgroundPrivate()
0043     : angle(0.0)
0044     , distance(1.0)
0045     , style(KoHatchBackground::Single)
0046     {}
0047 
0048     QColor lineColor;
0049     int angle;
0050     qreal distance;
0051     KoHatchBackground::HatchStyle style;
0052     QString name;
0053 };
0054 
0055 KoHatchBackground::KoHatchBackground()
0056     : KoColorBackground(*(new KoHatchBackgroundPrivate()))
0057 {
0058 }
0059 
0060 void KoHatchBackground::paint(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &context, const QPainterPath &fillPath) const
0061 {
0062     Q_D(const KoHatchBackground);
0063     if (d->color.isValid()) {
0064         // paint background color if set by using the color background
0065         KoColorBackground::paint(painter, converter, context, fillPath);
0066     }
0067 
0068     const QRectF targetRect = fillPath.boundingRect();
0069     painter.save();
0070     painter.setClipPath(fillPath);
0071     QPen pen(d->lineColor);
0072     // we set the pen width to 0.5 pt for the hatch. This is not defined in the spec.
0073     pen.setWidthF(0.5);
0074     painter.setPen(pen);
0075     QVector<QLineF> lines;
0076 
0077     // The different styles are handled by painting the lines multiple times with a different
0078     // angel offset as basically it just means we paint the lines also at a different angle.
0079     // This are the angle offsets we need to apply to the different lines of a style.
0080     // -90 is for single, 0 for the 2nd line in double and -45 for the 3th line in triple.
0081     const int angleOffset[] = {-90, 0, -45 };
0082     // The number of loops is defined by the style.
0083     int loops = (d->style == Single) ? 1 : (d->style == Double) ? 2 : 3;
0084 
0085     for (int i = 0; i < loops; ++i) {
0086         int angle = d->angle - angleOffset[i];
0087         qreal cosAngle = ::cos(angle/180.0*M_PI);
0088         // if cos is nearly 0 the lines are horizontal. Use a special case for that
0089         if (qAbs(cosAngle) > 0.00001) {
0090             qreal xDiff = tan(angle/180.0*M_PI) * targetRect.height();
0091             // calculate the distance we need to increase x when creating the lines so that the
0092             // distance between the lines is also correct for rotated lines.
0093             qreal xOffset = qAbs(d->distance / cosAngle);
0094 
0095             // if the lines go to the right we need to start more to the left. Get the correct start.
0096             qreal xStart = 0;
0097             while (-xDiff < xStart) {
0098                 xStart -= xOffset;
0099             }
0100 
0101             // if the lines go to the left we need to stop more at the right. Get the correct end offset
0102             qreal xEndOffset = 0;
0103             if (xDiff < 0) {
0104                 while (xDiff < -xEndOffset) {
0105                     xEndOffset += xOffset;
0106                 }
0107             }
0108             // create line objects.
0109             lines.reserve(lines.size() + int((targetRect.width() + xEndOffset - xStart) / xOffset) + 1);
0110             for (qreal x = xStart; x < targetRect.width() + xEndOffset; x += xOffset) {
0111                 lines.append(QLineF(x, 0, x + xDiff, targetRect.height()));
0112             }
0113         }
0114         else {
0115             // horizontal lines
0116             lines.reserve(lines.size() + int(targetRect.height()/d->distance) + 1);
0117             for (qreal y = 0; y < targetRect.height(); y += d->distance) {
0118                 lines.append(QLineF(0, y, targetRect.width(), y));
0119             }
0120         }
0121     }
0122 
0123     painter.drawLines(lines);
0124     painter.restore();
0125 }
0126 
0127 void KoHatchBackground::fillStyle(KoGenStyle &style, KoShapeSavingContext &context)
0128 {
0129     Q_D(KoHatchBackground);
0130 
0131     KoGenStyle::Type type = style.type();
0132     KoGenStyle::PropertyType propertyType = (type == KoGenStyle::GraphicStyle || type == KoGenStyle::GraphicAutoStyle ||
0133                                              type == KoGenStyle::DrawingPageStyle || type == KoGenStyle::DrawingPageAutoStyle )
0134                                             ? KoGenStyle::DefaultType : KoGenStyle::GraphicType;
0135 
0136     style.addProperty("draw:fill", "hatch", propertyType);
0137     style.addProperty("draw:fill-hatch-name", saveHatchStyle(context), propertyType);
0138     bool fillHatchSolid = d->color.isValid();
0139     style.addProperty("draw:fill-hatch-solid", fillHatchSolid, propertyType);
0140     if (fillHatchSolid) {
0141         style.addProperty("draw:fill-color", d->color.name(), propertyType);
0142     }
0143 }
0144 
0145 QString KoHatchBackground::saveHatchStyle(KoShapeSavingContext &context) const
0146 {
0147     Q_D(const KoHatchBackground);
0148     KoGenStyle hatchStyle(KoGenStyle::HatchStyle /*no family name*/);
0149     hatchStyle.addAttribute("draw:display-name", d->name);
0150     hatchStyle.addAttribute("draw:color", d->lineColor.name());
0151 
0152     hatchStyle.addAttributePt("draw:distance", d->distance);
0153 
0154     hatchStyle.addAttribute("draw:rotation", QString("%1").arg(d->angle * 10));
0155 
0156     switch (d->style) {
0157     case Single:
0158         hatchStyle.addAttribute("draw:style", "single");
0159         break;
0160     case Double:
0161         hatchStyle.addAttribute("draw:style", "double");
0162         break;
0163     case Triple:
0164         hatchStyle.addAttribute("draw:style", "triple");
0165         break;
0166     }
0167 
0168     return context.mainStyles().insert(hatchStyle, "hatch");
0169 }
0170 
0171 bool KoHatchBackground::loadStyle(KoOdfLoadingContext &context, const QSizeF &shapeSize)
0172 {
0173     // <draw:hatch draw:name="hatchStyle3" draw:color="#000000" draw:display-name="#000000 Vertical" draw:distance="0.102cm" draw:rotation="900" draw:style="single"/>
0174     Q_D(KoHatchBackground);
0175     Q_UNUSED(shapeSize);
0176 
0177     KoStyleStack &styleStack = context.styleStack();
0178     QString fillStyle = styleStack.property(KoXmlNS::draw, "fill");
0179     if (fillStyle == "hatch") {
0180         QString style = styleStack.property(KoXmlNS::draw, "fill-hatch-name");
0181         debugFlake << " hatch style is  :" << style;
0182 
0183         KoXmlElement* draw = context.stylesReader().drawStyles("hatch").value(style);
0184         if (draw) {
0185             debugFlake << "Hatch style found for:" << style;
0186 
0187             QString angle = draw->attributeNS(KoXmlNS::draw, "rotation", QString("0"));
0188             if (angle.at(angle.size()-1).isLetter()) {
0189                 d->angle = KoUnit::parseAngle(angle);
0190             }
0191             else {
0192                 // OO saves the angle value without unit and multiplied by a factor of 10
0193                 d->angle = int(angle.toInt() / 10);
0194             }
0195 
0196             debugFlake << "angle :" << d->angle;
0197 
0198             d->name = draw->attributeNS(KoXmlNS::draw, "display-name");
0199 
0200             // use 2mm as default, just in case it is not given in a document so we show something sensible. 
0201             d->distance = KoUnit::parseValue(draw->attributeNS(KoXmlNS::draw, "distance", "2mm"));
0202 
0203             bool fillHatchSolid = styleStack.property(KoXmlNS::draw, "fill-hatch-solid") == QLatin1String("true");
0204             if (fillHatchSolid) {
0205                 QString fillColor = styleStack.property(KoXmlNS::draw, "fill-color");
0206                 if (!fillColor.isEmpty()) {
0207                     d->color.setNamedColor(fillColor);
0208                 }
0209                 else {
0210                     d->color =QColor();
0211                 }
0212             }
0213             else {
0214                 d->color = QColor();
0215             }
0216             d->lineColor.setNamedColor(draw->attributeNS(KoXmlNS::draw, "color", QString("#000000")));
0217 
0218             QString style = draw->attributeNS(KoXmlNS::draw, "style", QString());
0219             if (style == "double") {
0220                 d->style = Double;
0221             }
0222             else if (style == "triple") {
0223                 d->style = Triple;
0224             }
0225             else {
0226                 d->style = Single;
0227             }
0228         }
0229 
0230         return true;
0231     }
0232 
0233     return false;
0234 }