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