1 /* This file is part of the KDE project
2  * Copyright (C) 2006, 2008, 2010 Thomas Zander <zander@kde.org>
3  * Copyright (C) 2008 Pierre Ducroquet <pinaraf@pinaraf.info>
4  * Copyright (C) 2008 Sebastian Sauer <mail@dipe.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * along with this library; see the file COPYING.LIB.  If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21 
22 #include "KWPageStyle.h"
23 #include "KWPageStyle_p.h"
24 
25 #include <KoXmlWriter.h>
26 #include <KoXmlNS.h>
27 #include <KoUnit.h>
28 #include <KoColorBackground.h>
29 #include <KoPatternBackground.h>
30 #include <KoImageCollection.h>
31 #include <KoOdfLoadingContext.h>
32 
33 #include <QBuffer>
34 #include <QColor>
35 
~KWPageStylePrivate()36 KWPageStylePrivate::~KWPageStylePrivate()
37 {
38 }
39 
clear()40 void KWPageStylePrivate::clear()
41 {
42     displayName.clear();
43     headerDistance = 10; // ~3mm
44     footerDistance = 10;
45     headerMinimumHeight = 10; // includes spacing
46     footerMinimumHeight = 10; // includes spacing
47     headers = Words::HFTypeNone;
48     footers = Words::HFTypeNone;
49     pageUsage = KWPageStyle::AllPages;
50     columns.reset();
51     direction = KoText::AutoDirection;
52     headerDynamicSpacing = false;
53     footerDynamicSpacing = false;
54     fullPageBackground.clear();
55     nextStyleName.clear();
56 }
57 
58 ///////////
59 
KWPageStyle(const QString & name,const QString & displayname)60 KWPageStyle::KWPageStyle(const QString &name, const QString &displayname)
61     : d (new KWPageStylePrivate())
62 {
63     d->name = name;
64     if (!displayname.isEmpty() && displayname != name)
65         d->displayName = displayname;
66 }
67 
KWPageStyle(const KWPageStyle & ps)68 KWPageStyle::KWPageStyle(const KWPageStyle &ps)
69     : d(ps.d)
70 {
71 }
72 
KWPageStyle()73 KWPageStyle::KWPageStyle()
74 {
75 }
76 
isValid() const77 bool KWPageStyle::isValid() const
78 {
79     return d && !d->name.isEmpty();
80 }
81 
82 
operator =(const KWPageStyle & ps)83 KWPageStyle &KWPageStyle::operator=(const KWPageStyle &ps)
84 {
85     d = ps.d;
86     return *this;
87 }
88 
~KWPageStyle()89 KWPageStyle::~KWPageStyle()
90 {
91 }
92 
pageUsage() const93 KWPageStyle::PageUsageType KWPageStyle::pageUsage() const
94 {
95     return d ? d->pageUsage : KWPageStyle::AllPages;
96 }
97 
setPageUsage(KWPageStyle::PageUsageType pageusage) const98 void KWPageStyle::setPageUsage(KWPageStyle::PageUsageType pageusage) const
99 {
100     d->pageUsage = pageusage;
101 }
102 
setFooterPolicy(Words::HeaderFooterType p)103 void KWPageStyle::setFooterPolicy(Words::HeaderFooterType p)
104 {
105     d->footers = p;
106 }
107 
setHeaderPolicy(Words::HeaderFooterType p)108 void KWPageStyle::setHeaderPolicy(Words::HeaderFooterType p)
109 {
110     d->headers = p;
111 }
112 
pageLayout() const113 KoPageLayout KWPageStyle::pageLayout() const
114 {
115     return d->pageLayout;
116 }
117 
setPageLayout(const KoPageLayout & pageLayout)118 void KWPageStyle::setPageLayout(const KoPageLayout &pageLayout)
119 {
120     d->pageLayout = pageLayout;
121 }
122 
columns() const123 KoColumns KWPageStyle::columns() const
124 {
125     return d->columns;
126 }
127 
setColumns(const KoColumns & columns)128 void KWPageStyle::setColumns(const KoColumns &columns)
129 {
130     d->columns = columns;
131 }
132 
headerPolicy() const133 Words::HeaderFooterType KWPageStyle::headerPolicy() const
134 {
135     return d->headers;
136 }
137 
footerPolicy() const138 Words::HeaderFooterType KWPageStyle::footerPolicy() const
139 {
140     return d->footers;
141 }
142 
headerDistance() const143 qreal KWPageStyle::headerDistance() const
144 {
145     return d->headerDistance;
146 }
147 
setHeaderDistance(qreal distance)148 void KWPageStyle::setHeaderDistance(qreal distance)
149 {
150     d->headerDistance = distance;
151 }
152 
headerDynamicSpacing() const153 bool KWPageStyle::headerDynamicSpacing() const
154 {
155     return d->headerDynamicSpacing;
156 }
157 
setHeaderDynamicSpacing(bool dynamic)158 void KWPageStyle::setHeaderDynamicSpacing(bool dynamic)
159 {
160     d->headerDynamicSpacing = dynamic;
161 }
162 
headerMinimumHeight() const163 qreal KWPageStyle::headerMinimumHeight() const
164 {
165     return d->headerMinimumHeight;
166 }
167 
setHeaderMinimumHeight(qreal height)168 void KWPageStyle::setHeaderMinimumHeight(qreal height)
169 {
170     d->headerMinimumHeight = height;
171 }
172 
footerMinimumHeight() const173 qreal KWPageStyle::footerMinimumHeight() const
174 {
175     return d->footerMinimumHeight;
176 }
177 
setFooterMinimumHeight(qreal height)178 void KWPageStyle::setFooterMinimumHeight(qreal height)
179 {
180     d->footerMinimumHeight = height;
181 }
182 
footerDistance() const183 qreal KWPageStyle::footerDistance() const
184 {
185     return d->footerDistance;
186 }
187 
setFooterDistance(qreal distance)188 void KWPageStyle::setFooterDistance(qreal distance)
189 {
190     d->footerDistance = distance;
191 }
192 
footerDynamicSpacing() const193 bool KWPageStyle::footerDynamicSpacing() const
194 {
195     return d->footerDynamicSpacing;
196 }
197 
setFooterDynamicSpacing(bool dynamic)198 void KWPageStyle::setFooterDynamicSpacing(bool dynamic)
199 {
200     d->footerDynamicSpacing = dynamic;
201 }
202 
clear()203 void KWPageStyle::clear()
204 {
205     d->clear();
206 }
207 
name() const208 QString KWPageStyle::name() const
209 {
210     return d->name;
211 }
212 
displayName() const213 QString KWPageStyle::displayName() const
214 {
215     return d->displayName;
216 }
217 
background() const218 QSharedPointer<KoShapeBackground> KWPageStyle::background() const
219 {
220     return d->fullPageBackground;
221 }
222 
setBackground(QSharedPointer<KoShapeBackground> background)223 void KWPageStyle::setBackground(QSharedPointer<KoShapeBackground> background)
224 {
225     d->fullPageBackground = background;
226 }
227 
saveOdf() const228 KoGenStyle KWPageStyle::saveOdf() const
229 {
230     KoGenStyle pageLayout = d->pageLayout.saveOdf();
231     pageLayout.setAutoStyleInStylesDotXml(true);
232 
233     switch (d->pageUsage) {
234         case LeftPages:
235             pageLayout.addAttribute("style:page-usage", "left");
236             break;
237         case MirroredPages:
238             pageLayout.addAttribute("style:page-usage", "mirrored");
239             break;
240         case RightPages:
241             pageLayout.addAttribute("style:page-usage", "right");
242             break;
243         default:
244             pageLayout.addAttribute("style:page-usage", "all");
245             break;
246     }
247 
248     // Save background color if it is set
249     if (d->fullPageBackground)
250     {
251         KoColorBackground *colorBackground = dynamic_cast<KoColorBackground*>(d->fullPageBackground.data());
252         if (colorBackground)
253             pageLayout.addProperty("fo:background-color", colorBackground->color().name());
254     }
255 
256     // save column data
257     d->columns.saveOdf(pageLayout);
258 
259     //<style:footnote-sep style:adjustment="left" style:width="0.5pt" style:rel-width="20%" style:line-style="solid"/>
260     //writer.startElement("style:footnote-sep");
261     // TODO
262     //writer.addAttribute("style:adjustment",)
263     //writer.addAttribute("style:width",)
264     //writer.addAttribute("style:rel-width",)
265     //writer.addAttribute("style:line-style",)
266     //writer.endElement();
267 
268 
269 
270     if (headerPolicy() != Words::HFTypeNone) {
271         QBuffer buffer;
272         buffer.open(QIODevice::WriteOnly);
273         KoXmlWriter writer(&buffer);
274 
275         writer.startElement("style:header-style");
276         writer.startElement("style:header-footer-properties");
277         writer.addAttributePt("fo:min-height", headerMinimumHeight());
278         writer.addAttributePt("fo:margin-bottom", headerDistance());
279         writer.addAttribute("style:dynamic-spacing", headerDynamicSpacing());
280         // TODO there are quite some more properties we want to at least preserve between load and save
281         writer.endElement();
282         writer.endElement();
283 
284         QString contentElement = QString::fromUtf8(buffer.buffer(), buffer.buffer().size());
285         // the 1_ 2_ is needed to get the correct order
286         pageLayout.addStyleChildElement("1_headerStyle", contentElement);
287     }
288     if (footerPolicy() != Words::HFTypeNone) {
289         QBuffer buffer;
290         buffer.open(QIODevice::WriteOnly);
291         KoXmlWriter writer(&buffer);
292 
293         writer.startElement("style:footer-style");
294         writer.startElement("style:header-footer-properties");
295         writer.addAttributePt("fo:min-height", footerMinimumHeight());
296         writer.addAttributePt("fo:margin-top", footerDistance());
297         writer.addAttribute("style:dynamic-spacing", footerDynamicSpacing());
298         // TODO there are quite some more properties we want to at least preserve between load and save
299         writer.endElement();
300         writer.endElement();
301 
302         QString contentElement = QString::fromUtf8(buffer.buffer(), buffer.buffer().size());
303         pageLayout.addStyleChildElement("2_footerStyle", contentElement);
304     }
305 
306     // TODO see how we should save margins if we use the 'closest to binding' stuff.
307 
308     return pageLayout;
309 }
310 
loadOdf(KoOdfLoadingContext & context,const KoXmlElement & masterNode,const KoXmlElement & style,KoDocumentResourceManager * documentResources)311 void KWPageStyle::loadOdf(KoOdfLoadingContext &context, const KoXmlElement &masterNode, const KoXmlElement &style, KoDocumentResourceManager *documentResources)
312 {
313     d->pageLayout.loadOdf(style);
314     KoXmlElement props = KoXml::namedItemNS(style, KoXmlNS::style, "page-layout-properties");
315     if (props.isNull())
316         return;
317     QString direction = props.attributeNS(KoXmlNS::style, "writing-mode", "lr-tb");
318     d->direction = KoText::directionFromString(direction);
319 
320     QString pageUsage = style.attributeNS(KoXmlNS::style, "page-usage", "all");
321     if (pageUsage == "left") {
322         d->pageUsage = LeftPages;
323     } else if (pageUsage == "mirrored") {
324         d->pageUsage = MirroredPages;
325     } else if (pageUsage == "right") {
326         d->pageUsage = RightPages;
327     } else { // "all"
328         d->pageUsage = AllPages;
329     }
330 
331     d->columns.loadOdf(props);
332 
333     KoXmlElement header = KoXml::namedItemNS(style, KoXmlNS::style, "header-style");
334     if (! header.isNull()) {
335         KoXmlElement hfprops = KoXml::namedItemNS(header, KoXmlNS::style, "header-footer-properties");
336         if (! hfprops.isNull()) {
337             d->headerDistance = KoUnit::parseValue(hfprops.attributeNS(KoXmlNS::fo, "margin-bottom"));
338             d->headerMinimumHeight = KoUnit::parseValue(hfprops.attributeNS(KoXmlNS::fo, "min-height"));
339             const QString dynamicSpacing(hfprops.attributeNS(KoXmlNS::style, "dynamic-spacing"));
340             d->headerDynamicSpacing = dynamicSpacing == "true";
341         }
342         // TODO there are quite some more properties we want to at least preserve between load and save
343     }
344 
345     KoXmlElement footer = KoXml::namedItemNS(style, KoXmlNS::style, "footer-style");
346     if (! footer.isNull()) {
347         KoXmlElement hfprops = KoXml::namedItemNS(footer, KoXmlNS::style, "header-footer-properties");
348         if (! hfprops.isNull()) {
349             d->footerDistance = KoUnit::parseValue(hfprops.attributeNS(KoXmlNS::fo, "margin-top"));
350             d->footerMinimumHeight = KoUnit::parseValue(hfprops.attributeNS(KoXmlNS::fo, "min-height"));
351             const QString dynamicSpacing(hfprops.attributeNS(KoXmlNS::style, "dynamic-spacing"));
352             d->footerDynamicSpacing = dynamicSpacing == "true";
353         }
354         // TODO there are quite some more properties we want to at least preserve between load and save
355     }
356 
357     // Load background picture
358     KoXmlElement propBackgroundImage = KoXml::namedItemNS(props, KoXmlNS::style, "background-image");
359     if (!propBackgroundImage.isNull()) {
360         const QString href = propBackgroundImage.attributeNS(KoXmlNS::xlink, "href", QString());
361         if (!href.isEmpty()) {
362             QSharedPointer<KoPatternBackground> background =  QSharedPointer<KoPatternBackground>(new KoPatternBackground(documentResources->imageCollection()));
363             d->fullPageBackground = background;
364 
365             KoImageCollection *imageCollection = documentResources->imageCollection();
366             if (imageCollection != 0) {
367                 KoImageData *imageData = imageCollection->createImageData(href,context.store());
368                 background->setPattern(imageData);
369             }
370         }
371         // TODO load another possible attributes
372     }
373 
374     // Load background color
375     QString backgroundColor = props.attributeNS(KoXmlNS::fo, "background-color", QString());
376     if (!backgroundColor.isNull() && d->fullPageBackground == 0) {
377 
378         if (backgroundColor == "transparent") {
379             d->fullPageBackground.clear();
380         }
381         else {
382             d->fullPageBackground = QSharedPointer<KoShapeBackground>(new KoColorBackground(QColor(backgroundColor)));
383         }
384     }
385 
386     // Load next master-page style name
387     d->nextStyleName = masterNode.attributeNS(KoXmlNS::style, "next-style-name", QString());
388 }
389 
nextStyleName() const390 QString KWPageStyle::nextStyleName() const
391 {
392     return d->nextStyleName;
393 }
394 
setNextStyleName(const QString & nextStyleName)395 void KWPageStyle::setNextStyleName(const QString &nextStyleName)
396 {
397     d->nextStyleName = nextStyleName;
398 }
399 
direction() const400 KoText::Direction KWPageStyle::direction() const
401 {
402     return d->direction;
403 }
404 
setDirection(KoText::Direction direction)405 void KWPageStyle::setDirection(KoText::Direction direction)
406 {
407     d->direction = direction;
408 }
409 
operator ==(const KWPageStyle & other) const410 bool KWPageStyle::operator==(const KWPageStyle &other) const
411 {
412     bool equals = d == other.d;
413     return equals;
414 }
415 
priv()416 KWPageStylePrivate *KWPageStyle::priv()
417 {
418     return d.data();
419 }
420 
priv() const421 const KWPageStylePrivate *KWPageStyle::priv() const
422 {
423     return d.data();
424 }
425 
hash() const426 uint KWPageStyle::hash() const
427 {
428     return ((uint) d) + 1;
429 }
430 
isPageSpread() const431 bool KWPageStyle::isPageSpread() const
432 {
433     return d->pageLayout.leftMargin < 0;
434 }
435 
qHash(const KWPageStyle & style)436 uint qHash(const KWPageStyle &style)
437 {
438     return style.hash();
439 }
440 
detach(const QString & newName,const QString & displayName)441 void KWPageStyle::detach(const QString &newName, const QString &displayName)
442 {
443     d->fullPageBackground.clear();
444     d.detach();
445     d->name = newName;
446     d->displayName = displayName;
447 }
448