File indexing completed on 2024-05-19 16:09:58

0001 /* This file is part of the KDE project
0002  * Copyright (C) 2006, 2008, 2010 Thomas Zander <zander@kde.org>
0003  * Copyright (C) 2008 Pierre Ducroquet <pinaraf@pinaraf.info>
0004  * Copyright (C) 2008 Sebastian Sauer <mail@dipe.org>
0005  *
0006  * This library is free software; you can redistribute it and/or
0007  * modify it under the terms of the GNU Library General Public
0008  * License as published by the Free Software Foundation; either
0009  * version 2 of the License, or (at your option) any later version.
0010  *
0011  * This library is distributed in the hope that it will be useful,
0012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0014  * Library General Public License for more details.
0015  *
0016  * You should have received a copy of the GNU Library General Public License
0017  * along with this library; see the file COPYING.LIB.  If not, write to
0018  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0019  * Boston, MA 02110-1301, USA.
0020  */
0021 
0022 #include "KWPageStyle.h"
0023 #include "KWPageStyle_p.h"
0024 
0025 #include <KoXmlWriter.h>
0026 #include <KoXmlNS.h>
0027 #include <KoUnit.h>
0028 #include <KoColorBackground.h>
0029 #include <KoPatternBackground.h>
0030 #include <KoImageCollection.h>
0031 #include <KoOdfLoadingContext.h>
0032 
0033 #include <QBuffer>
0034 #include <QColor>
0035 
0036 KWPageStylePrivate::~KWPageStylePrivate()
0037 {
0038 }
0039 
0040 void KWPageStylePrivate::clear()
0041 {
0042     displayName.clear();
0043     headerDistance = 10; // ~3mm
0044     footerDistance = 10;
0045     headerMinimumHeight = 10; // includes spacing
0046     footerMinimumHeight = 10; // includes spacing
0047     headers = Words::HFTypeNone;
0048     footers = Words::HFTypeNone;
0049     pageUsage = KWPageStyle::AllPages;
0050     columns.reset();
0051     direction = KoText::AutoDirection;
0052     headerDynamicSpacing = false;
0053     footerDynamicSpacing = false;
0054     fullPageBackground.clear();
0055     nextStyleName.clear();
0056 }
0057 
0058 ///////////
0059 
0060 KWPageStyle::KWPageStyle(const QString &name, const QString &displayname)
0061     : d (new KWPageStylePrivate())
0062 {
0063     d->name = name;
0064     if (!displayname.isEmpty() && displayname != name)
0065         d->displayName = displayname;
0066 }
0067 
0068 KWPageStyle::KWPageStyle(const KWPageStyle &ps)
0069     : d(ps.d)
0070 {
0071 }
0072 
0073 KWPageStyle::KWPageStyle()
0074 {
0075 }
0076 
0077 bool KWPageStyle::isValid() const
0078 {
0079     return d && !d->name.isEmpty();
0080 }
0081 
0082 
0083 KWPageStyle &KWPageStyle::operator=(const KWPageStyle &ps)
0084 {
0085     d = ps.d;
0086     return *this;
0087 }
0088 
0089 KWPageStyle::~KWPageStyle()
0090 {
0091 }
0092 
0093 KWPageStyle::PageUsageType KWPageStyle::pageUsage() const
0094 {
0095     return d ? d->pageUsage : KWPageStyle::AllPages;
0096 }
0097 
0098 void KWPageStyle::setPageUsage(KWPageStyle::PageUsageType pageusage) const
0099 {
0100     d->pageUsage = pageusage;
0101 }
0102 
0103 void KWPageStyle::setFooterPolicy(Words::HeaderFooterType p)
0104 {
0105     d->footers = p;
0106 }
0107 
0108 void KWPageStyle::setHeaderPolicy(Words::HeaderFooterType p)
0109 {
0110     d->headers = p;
0111 }
0112 
0113 KoPageLayout KWPageStyle::pageLayout() const
0114 {
0115     return d->pageLayout;
0116 }
0117 
0118 void KWPageStyle::setPageLayout(const KoPageLayout &pageLayout)
0119 {
0120     d->pageLayout = pageLayout;
0121 }
0122 
0123 KoColumns KWPageStyle::columns() const
0124 {
0125     return d->columns;
0126 }
0127 
0128 void KWPageStyle::setColumns(const KoColumns &columns)
0129 {
0130     d->columns = columns;
0131 }
0132 
0133 Words::HeaderFooterType KWPageStyle::headerPolicy() const
0134 {
0135     return d->headers;
0136 }
0137 
0138 Words::HeaderFooterType KWPageStyle::footerPolicy() const
0139 {
0140     return d->footers;
0141 }
0142 
0143 qreal KWPageStyle::headerDistance() const
0144 {
0145     return d->headerDistance;
0146 }
0147 
0148 void KWPageStyle::setHeaderDistance(qreal distance)
0149 {
0150     d->headerDistance = distance;
0151 }
0152 
0153 bool KWPageStyle::headerDynamicSpacing() const
0154 {
0155     return d->headerDynamicSpacing;
0156 }
0157 
0158 void KWPageStyle::setHeaderDynamicSpacing(bool dynamic)
0159 {
0160     d->headerDynamicSpacing = dynamic;
0161 }
0162 
0163 qreal KWPageStyle::headerMinimumHeight() const
0164 {
0165     return d->headerMinimumHeight;
0166 }
0167 
0168 void KWPageStyle::setHeaderMinimumHeight(qreal height)
0169 {
0170     d->headerMinimumHeight = height;
0171 }
0172 
0173 qreal KWPageStyle::footerMinimumHeight() const
0174 {
0175     return d->footerMinimumHeight;
0176 }
0177 
0178 void KWPageStyle::setFooterMinimumHeight(qreal height)
0179 {
0180     d->footerMinimumHeight = height;
0181 }
0182 
0183 qreal KWPageStyle::footerDistance() const
0184 {
0185     return d->footerDistance;
0186 }
0187 
0188 void KWPageStyle::setFooterDistance(qreal distance)
0189 {
0190     d->footerDistance = distance;
0191 }
0192 
0193 bool KWPageStyle::footerDynamicSpacing() const
0194 {
0195     return d->footerDynamicSpacing;
0196 }
0197 
0198 void KWPageStyle::setFooterDynamicSpacing(bool dynamic)
0199 {
0200     d->footerDynamicSpacing = dynamic;
0201 }
0202 
0203 void KWPageStyle::clear()
0204 {
0205     d->clear();
0206 }
0207 
0208 QString KWPageStyle::name() const
0209 {
0210     return d->name;
0211 }
0212 
0213 QString KWPageStyle::displayName() const
0214 {
0215     return d->displayName;
0216 }
0217 
0218 QSharedPointer<KoShapeBackground> KWPageStyle::background() const
0219 {
0220     return d->fullPageBackground;
0221 }
0222 
0223 void KWPageStyle::setBackground(QSharedPointer<KoShapeBackground> background)
0224 {
0225     d->fullPageBackground = background;
0226 }
0227 
0228 KoGenStyle KWPageStyle::saveOdf() const
0229 {
0230     KoGenStyle pageLayout = d->pageLayout.saveOdf();
0231     pageLayout.setAutoStyleInStylesDotXml(true);
0232 
0233     switch (d->pageUsage) {
0234         case LeftPages:
0235             pageLayout.addAttribute("style:page-usage", "left");
0236             break;
0237         case MirroredPages:
0238             pageLayout.addAttribute("style:page-usage", "mirrored");
0239             break;
0240         case RightPages:
0241             pageLayout.addAttribute("style:page-usage", "right");
0242             break;
0243         default:
0244             pageLayout.addAttribute("style:page-usage", "all");
0245             break;
0246     }
0247 
0248     // Save background color if it is set
0249     if (d->fullPageBackground)
0250     {
0251         KoColorBackground *colorBackground = dynamic_cast<KoColorBackground*>(d->fullPageBackground.data());
0252         if (colorBackground)
0253             pageLayout.addProperty("fo:background-color", colorBackground->color().name());
0254     }
0255 
0256     // save column data
0257     d->columns.saveOdf(pageLayout);
0258 
0259     //<style:footnote-sep style:adjustment="left" style:width="0.5pt" style:rel-width="20%" style:line-style="solid"/>
0260     //writer.startElement("style:footnote-sep");
0261     // TODO
0262     //writer.addAttribute("style:adjustment",)
0263     //writer.addAttribute("style:width",)
0264     //writer.addAttribute("style:rel-width",)
0265     //writer.addAttribute("style:line-style",)
0266     //writer.endElement();
0267 
0268 
0269 
0270     if (headerPolicy() != Words::HFTypeNone) {
0271         QBuffer buffer;
0272         buffer.open(QIODevice::WriteOnly);
0273         KoXmlWriter writer(&buffer);
0274 
0275         writer.startElement("style:header-style");
0276         writer.startElement("style:header-footer-properties");
0277         writer.addAttributePt("fo:min-height", headerMinimumHeight());
0278         writer.addAttributePt("fo:margin-bottom", headerDistance());
0279         writer.addAttribute("style:dynamic-spacing", headerDynamicSpacing());
0280         // TODO there are quite some more properties we want to at least preserve between load and save
0281         writer.endElement();
0282         writer.endElement();
0283 
0284         QString contentElement = QString::fromUtf8(buffer.buffer(), buffer.buffer().size());
0285         // the 1_ 2_ is needed to get the correct order
0286         pageLayout.addStyleChildElement("1_headerStyle", contentElement);
0287     }
0288     if (footerPolicy() != Words::HFTypeNone) {
0289         QBuffer buffer;
0290         buffer.open(QIODevice::WriteOnly);
0291         KoXmlWriter writer(&buffer);
0292 
0293         writer.startElement("style:footer-style");
0294         writer.startElement("style:header-footer-properties");
0295         writer.addAttributePt("fo:min-height", footerMinimumHeight());
0296         writer.addAttributePt("fo:margin-top", footerDistance());
0297         writer.addAttribute("style:dynamic-spacing", footerDynamicSpacing());
0298         // TODO there are quite some more properties we want to at least preserve between load and save
0299         writer.endElement();
0300         writer.endElement();
0301 
0302         QString contentElement = QString::fromUtf8(buffer.buffer(), buffer.buffer().size());
0303         pageLayout.addStyleChildElement("2_footerStyle", contentElement);
0304     }
0305 
0306     // TODO see how we should save margins if we use the 'closest to binding' stuff.
0307 
0308     return pageLayout;
0309 }
0310 
0311 void KWPageStyle::loadOdf(KoOdfLoadingContext &context, const KoXmlElement &masterNode, const KoXmlElement &style, KoDocumentResourceManager *documentResources)
0312 {
0313     d->pageLayout.loadOdf(style);
0314     KoXmlElement props = KoXml::namedItemNS(style, KoXmlNS::style, "page-layout-properties");
0315     if (props.isNull())
0316         return;
0317     QString direction = props.attributeNS(KoXmlNS::style, "writing-mode", "lr-tb");
0318     d->direction = KoText::directionFromString(direction);
0319 
0320     QString pageUsage = style.attributeNS(KoXmlNS::style, "page-usage", "all");
0321     if (pageUsage == "left") {
0322         d->pageUsage = LeftPages;
0323     } else if (pageUsage == "mirrored") {
0324         d->pageUsage = MirroredPages;
0325     } else if (pageUsage == "right") {
0326         d->pageUsage = RightPages;
0327     } else { // "all"
0328         d->pageUsage = AllPages;
0329     }
0330 
0331     d->columns.loadOdf(props);
0332 
0333     KoXmlElement header = KoXml::namedItemNS(style, KoXmlNS::style, "header-style");
0334     if (! header.isNull()) {
0335         KoXmlElement hfprops = KoXml::namedItemNS(header, KoXmlNS::style, "header-footer-properties");
0336         if (! hfprops.isNull()) {
0337             d->headerDistance = KoUnit::parseValue(hfprops.attributeNS(KoXmlNS::fo, "margin-bottom"));
0338             d->headerMinimumHeight = KoUnit::parseValue(hfprops.attributeNS(KoXmlNS::fo, "min-height"));
0339             const QString dynamicSpacing(hfprops.attributeNS(KoXmlNS::style, "dynamic-spacing"));
0340             d->headerDynamicSpacing = dynamicSpacing == "true";
0341         }
0342         // TODO there are quite some more properties we want to at least preserve between load and save
0343     }
0344 
0345     KoXmlElement footer = KoXml::namedItemNS(style, KoXmlNS::style, "footer-style");
0346     if (! footer.isNull()) {
0347         KoXmlElement hfprops = KoXml::namedItemNS(footer, KoXmlNS::style, "header-footer-properties");
0348         if (! hfprops.isNull()) {
0349             d->footerDistance = KoUnit::parseValue(hfprops.attributeNS(KoXmlNS::fo, "margin-top"));
0350             d->footerMinimumHeight = KoUnit::parseValue(hfprops.attributeNS(KoXmlNS::fo, "min-height"));
0351             const QString dynamicSpacing(hfprops.attributeNS(KoXmlNS::style, "dynamic-spacing"));
0352             d->footerDynamicSpacing = dynamicSpacing == "true";
0353         }
0354         // TODO there are quite some more properties we want to at least preserve between load and save
0355     }
0356 
0357     // Load background picture
0358     KoXmlElement propBackgroundImage = KoXml::namedItemNS(props, KoXmlNS::style, "background-image");
0359     if (!propBackgroundImage.isNull()) {
0360         const QString href = propBackgroundImage.attributeNS(KoXmlNS::xlink, "href", QString());
0361         if (!href.isEmpty()) {
0362             QSharedPointer<KoPatternBackground> background =  QSharedPointer<KoPatternBackground>(new KoPatternBackground(documentResources->imageCollection()));
0363             d->fullPageBackground = background;
0364 
0365             KoImageCollection *imageCollection = documentResources->imageCollection();
0366             if (imageCollection != 0) {
0367                 KoImageData *imageData = imageCollection->createImageData(href,context.store());
0368                 background->setPattern(imageData);
0369             }
0370         }
0371         // TODO load another possible attributes
0372     }
0373 
0374     // Load background color
0375     QString backgroundColor = props.attributeNS(KoXmlNS::fo, "background-color", QString());
0376     if (!backgroundColor.isNull() && d->fullPageBackground == 0) {
0377 
0378         if (backgroundColor == "transparent") {
0379             d->fullPageBackground.clear();
0380         }
0381         else {
0382             d->fullPageBackground = QSharedPointer<KoShapeBackground>(new KoColorBackground(QColor(backgroundColor)));
0383         }
0384     }
0385 
0386     // Load next master-page style name
0387     d->nextStyleName = masterNode.attributeNS(KoXmlNS::style, "next-style-name", QString());
0388 }
0389 
0390 QString KWPageStyle::nextStyleName() const
0391 {
0392     return d->nextStyleName;
0393 }
0394 
0395 void KWPageStyle::setNextStyleName(const QString &nextStyleName)
0396 {
0397     d->nextStyleName = nextStyleName;
0398 }
0399 
0400 KoText::Direction KWPageStyle::direction() const
0401 {
0402     return d->direction;
0403 }
0404 
0405 void KWPageStyle::setDirection(KoText::Direction direction)
0406 {
0407     d->direction = direction;
0408 }
0409 
0410 bool KWPageStyle::operator==(const KWPageStyle &other) const
0411 {
0412     bool equals = d == other.d;
0413     return equals;
0414 }
0415 
0416 KWPageStylePrivate *KWPageStyle::priv()
0417 {
0418     return d.data();
0419 }
0420 
0421 const KWPageStylePrivate *KWPageStyle::priv() const
0422 {
0423     return d.data();
0424 }
0425 
0426 uint KWPageStyle::hash() const
0427 {
0428     return ((uint) d) + 1;
0429 }
0430 
0431 bool KWPageStyle::isPageSpread() const
0432 {
0433     return d->pageLayout.leftMargin < 0;
0434 }
0435 
0436 uint qHash(const KWPageStyle &style)
0437 {
0438     return style.hash();
0439 }
0440 
0441 void KWPageStyle::detach(const QString &newName, const QString &displayName)
0442 {
0443     d->fullPageBackground.clear();
0444     d.detach();
0445     d->name = newName;
0446     d->displayName = displayName;
0447 }