1 /*
2  * This file is part of Office 2007 Filters for Calligra
3  * Copyright (C) 2002 Laurent Montel <lmontel@mandrakesoft.com>
4  * Copyright (c) 2003 Lukas Tinkl <lukas@kde.org>
5  * Copyright (C) 2003 David Faure <faure@kde.org>
6  * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
7  *
8  * Contact: Suresh Chande suresh.chande@nokia.com
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public License
12  * version 2.1 as published by the Free Software Foundation.
13  *
14  * This library is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22  * 02110-1301 USA
23  *
24  */
25 
26 #ifndef MSOOXML_UTILS_H
27 #define MSOOXML_UTILS_H
28 
29 #include "komsooxml_export.h"
30 
31 #include <QSize>
32 #include <QColor>
33 #include <QBuffer>
34 #include <KoFilterChain.h>
35 #include <KoXmlReader.h>
36 #include "MsooXmlDebug.h"
37 #include <KoGenStyle.h>
38 
39 class KZip;
40 struct KoOdfWriters;
41 class KoCharacterStyle;
42 class KoXmlWriter;
43 class KoGenStyles;
44 
45 //! Returns from the current block if the result of @a call is not equal to KoFilter::OK
46 #define RETURN_IF_ERROR( call ) \
47     { \
48         const KoFilter::ConversionStatus result = call; \
49         if (result != KoFilter::OK) \
50             return result; \
51     }
52 
53 //! Common utilities for handling MSOOXML formats
54 namespace MSOOXML
55 {
56 
57 class MsooXmlReader;
58 class MsooXmlReaderContext;
59 
60 namespace Utils {
61 
62 enum autoFitStatus {
63     autoFitUnUsed, autoFitOn, autoFitOff
64 };
65 
66 enum MSOOXMLFilter {
67     DocxFilter, PptxFilter, XlsxFilter
68 };
69 
70 class KOMSOOXML_EXPORT ParagraphBulletProperties
71 {
72 public:
73 
74     ParagraphBulletProperties();
75 
76     void clear();
77 
78     QString convertToListProperties(KoGenStyles& mainStyles, MSOOXMLFilter filter = XlsxFilter);
79 
80     bool isEmpty() const;
81 
82     void setBulletChar(const QString& bulletChar);
83 
84     void setPrefix(const QString& prefixChar);
85 
86     void setSuffix(const QString& suffixChar);
87 
88     void setAlign(const QString& align);
89 
90     void setNumFormat(const QString& numFormat);
91 
92     void setMargin(const qreal margin);
93 
94     void setIndent(const qreal indent);
95 
96     void setPicturePath(const QString& picturePath);
97 
98     void setBulletFont(const QString& font);
99 
100     void setBulletColor(const QString& bulletColor);
101 
102     void setStartValue(const QString& value);
103 
104     void setBulletRelativeSize(const int size);
105 
106     void setBulletSizePt(const qreal size);
107 
108     void setFollowingChar(const QString& value);
109 
110     void setTextStyle(const KoGenStyle& textStyle);
111 
112     void setStartOverride(const bool startOverride);
113 
114     QString startValue() const;
115 
116     QString bulletRelativeSize() const;
117 
118     QString bulletSizePt() const;
119 
120     QString bulletColor() const;
121 
122     QString bulletChar() const;
123 
124     QString bulletFont() const;
125 
126     QString indent() const;
127 
128     QString margin() const;
129 
130     QString followingChar() const;
131 
132     KoGenStyle textStyle() const;
133 
134     bool startOverride() const;
135 
136     void addInheritedValues(const ParagraphBulletProperties& properties);
137 
138     int m_level;
139 
140     enum ParagraphBulletType {BulletType, NumberType, PictureType, DefaultType};
141     ParagraphBulletType m_type;
142 
143 private:
144 
145     QString m_startValue;
146     QString m_bulletFont;
147     QString m_bulletChar;
148     QString m_numFormat;
149     QString m_prefix;
150     QString m_suffix;
151     QString m_align;
152     QString m_indent;
153     QString m_margin;
154     QString m_picturePath;
155     QString m_bulletColor;
156     QString m_followingChar;
157     QString m_bulletRelativeSize;
158     QString m_bulletSize;
159 
160     KoGenStyle m_textStyle;
161 
162     // MSWord specific: Restart the numbering when this list style is
163     // used for the 1st time.  Otherwise don't restart in case any of the
164     // styles inheriting from the same abstract numbering definition was
165     // already used.  Let's ignore presence of this attribute in
166     // addInheritedValues.
167     bool m_startOverride;
168 };
169 
170 //! Container autodeleter. Works for QList, QHash and QMap.
171 //! @todo move to more generic place
172 template <typename T>
173 class ContainerDeleter
174 {
175 public:
ContainerDeleter(T & container)176     ContainerDeleter(T& container) : m_container(&container) {}
~ContainerDeleter()177     ~ContainerDeleter() {
178         qDeleteAll(*m_container); m_container->clear();
179     }
180 private:
181     T* const m_container;
182 };
183 
184 //! Helper that sets given variable to specified value on destruction
185 //! Object of type Setter are supposed to be created on the stack.
186 //! @todo Copied from calligra/kexi/kexiutils/utils.h; replace with a shared code
187 template <typename T>
188 class Setter
189 {
190 public:
191     //! Creates a new setter object for variable @a var,
192     //! which will be set to value @a val on setter's destruction.
Setter(T * var,const T & val)193     Setter(T* var, const T& val)
194             : m_var(var), m_value(val) {
195     }
~Setter()196     ~Setter() {
197         if (m_var)
198             *m_var = m_value;
199     }
200     //! Clears the assignment, so the setter
201     //! will not alter the variable on destruction
clear()202     void clear() {
203         m_var = 0;
204     }
205 private:
206     T* m_var;
207     const T m_value;
208 };
209 
210 //! Helper that works like the @ref Setter class but also has behaviour of std::auto_ptr.
211 //! When std::auto_ptr is used, the pointer of type T* is not set back to 0 on destruction.
212 //! @todo replace with a shared code
213 template <typename T>
214 class AutoPtrSetter
215 {
216 public:
217     //! Creates a new auto-ptr setter object for variable pointer of type T* @a ptr,
218     //! which will be set to 0 setter's destruction, unless release() was called.
AutoPtrSetter(T * ptr)219     explicit AutoPtrSetter(T* ptr)
220             : m_pptr(&ptr) {
221     }
~AutoPtrSetter()222     ~AutoPtrSetter() {
223         if (m_pptr && *m_pptr) {
224             delete *m_pptr;
225             *m_pptr = 0;
226         }
227     }
228     //! Bypasses the smart pointer, and returns it, so on destruction
229     //! of the AutoPtrSetter object the pointed object will not be deleted
230     //! (so it is the behaviour like std::auto__ptr::release())
231     //! but also the pointer of type T* will not be cleared.
release()232     T* release() {
233         T* p = m_pptr ? *m_pptr : 0;
234         m_pptr = 0;
235         return p;
236     }
237 private:
238     T** m_pptr;
239 };
240 
241 //! Decodes boolean attribute @a value. If unspecified returns @a defaultValue.
242 //! @return true unless @a value is equal to "false", "off" or "0".
243 KOMSOOXML_EXPORT bool convertBooleanAttr(const QString& value, bool defaultValue = false);
244 
245 //! Loads content types from "[Content_Types].xml"
246 /*! Based on information from ECMA-376, Part 1: "11.2 Package Structure".
247  @return status: KoFilter::OK on success or KoFilter::WrongFormat when any unexpected and critical incompatibility occurs.
248 */
249 //! @todo create whole class keeping the data
250 KOMSOOXML_EXPORT KoFilter::ConversionStatus loadContentTypes(const KoXmlDocument& contentTypesXML,
251         QMultiHash<QByteArray, QByteArray>& contentTypes);
252 
253 //! Loads content types from "docProps/app.xml"
254 KOMSOOXML_EXPORT KoFilter::ConversionStatus loadDocumentProperties(const KoXmlDocument& appXML, QMap<QString, QVariant>& properties);
255 
256 //! @return device for file @a fileName of @a zip archive. Status @a status is written on error.
257 //! The device is already opened for reading and should be deleted after use.
258 KOMSOOXML_EXPORT QIODevice* openDeviceForFile(const KZip* zip,
259         QString& errorMessage,
260         const QString& fileName,
261         KoFilter::ConversionStatus& status);
262 
263 //! QXmlStreamReader-based generic loading/parsing into @a doc KoXmlDocument
264 KOMSOOXML_EXPORT KoFilter::ConversionStatus loadAndParse(QIODevice* io, KoXmlDocument& doc,
265         QString& errorMessage, const QString & fileName);
266 
267 //! @see KoOdfReadStore::loadAndParse(QIODevice* fileDevice, KoXmlDocument& doc, QString& errorMessage, const QString& fileName)
268 KOMSOOXML_EXPORT KoFilter::ConversionStatus loadAndParse(KoXmlDocument& doc,
269         const KZip* zip,
270         QString& errorMessage,
271         const QString& fileName);
272 
273 //! QXmlStreamReader-based loading/parsing for document.xml
274 KOMSOOXML_EXPORT KoFilter::ConversionStatus loadAndParseDocument(MsooXmlReader* reader,
275         const KZip* zip,
276         KoOdfWriters* writers,
277         QString& errorMessage,
278         const QString& fileName,
279         MsooXmlReaderContext* context = 0);
280 
281 /*! Copies file @a sourceName from zip archive @a zip to @a outputStore store
282  under @a destinationName name. If @a size is not 0, *size is set to size of the image
283  @return KoFilter::OK on success.
284  On failure @a errorMessage is set. */
285 KoFilter::ConversionStatus copyFile(const KZip* zip, QString& errorMessage,
286                                     const QString& sourceName, KoStore *outputStore,
287                                     const QString& destinationName, bool oleType=false);
288 
289 /*! Creates a file */
290 KoFilter::ConversionStatus createImage(QString& errorMessage,
291                                        const QImage& source, KoStore *outputStore,
292                                        const QString& destinationName);
293 
294 /*! @return size of image file @a sourceName read from zip archive @a zip.
295  Size of the image is returned in @a size.
296  @return KoFilter::OK on success.
297  On failure @a errorMessage is set. */
298 KoFilter::ConversionStatus imageSize(const KZip* zip, QString& errorMessage,
299                                      const QString& sourceName, QSize* size);
300 
301 //! Loads a thumbnail.
302 /*! @return conversion status
303     @todo Thumbnails are apparently used only by PowerPoint or templates for now.
304           Implement it, for now this feature is not needed for docx. */
305 KOMSOOXML_EXPORT KoFilter::ConversionStatus loadThumbnail(QImage& thumbnail, KZip* zip);
306 
307 // -- conversions ---
308 
309 //! Handles ST_Lang value @a value (Language Reference) (SharedML, 22.9.2.6)
310 /*! The value specifies that its contents contains a language identifier as defined by RFC 4646/BCP 47.
311     Sets up @a language and @a country based on @a value that is of format {langugage}-{country}
312     @return true on success. */
313 KOMSOOXML_EXPORT bool ST_Lang_to_languageAndCountry(const QString& value, QString& language, QString& country);
314 
315 //! @return QColor value for ST_HexColorRGB (Hexadecimal Color Value) (SharedML, 22.9.2.5)
316 //!         or invalid QColor if @a color is not in the expected format.
317 //! @par val color value in RRGGBB hexadecimal format
ST_HexColorRGB_to_QColor(const QString & color)318 inline QColor ST_HexColorRGB_to_QColor(const QString& color)
319 {
320     if (color.length() != 6)
321         return QColor();
322     bool ok;
323     const uint rgb = color.toUInt(&ok, 16);
324     return ok ? QColor(QRgb(rgb)) : QColor(); // alpha ignored
325 //    return QColor(QRgb(0xff000000 | color.toInt(0, 16)));
326 }
327 
328 //! @return QBrush value for ST_HighlightColor
329 //! The brush is built out of solid color.
330 //! If colorName is not supported by the standard, QBrush() is returned.
331 //! @par colorName named text highlight color like "black", "blue" (17.18.40)
332 KOMSOOXML_EXPORT QBrush ST_HighlightColor_to_QColor(const QString& colorName);
333 
334 //! Converts value for 22.9.2.9 ST_Percentage (Percentage Value with Sign) from string
335 //! Sets @arg ok to true on success.
336 KOMSOOXML_EXPORT qreal ST_Percentage_to_double(const QString& val, bool& ok);
337 
338 //! Converts value for 22.9.2.9 ST_Percentage (Percentage Value with Sign) from string
339 //! If "%" suffix is not present (MSOOXML violation of OOXML), the format is expected to be int({ST_Percentage}*1000).
340 //! Sets @arg ok to true on success.
341 KOMSOOXML_EXPORT qreal ST_Percentage_withMsooxmlFix_to_double(const QString& val, bool& ok);
342 
343 struct KOMSOOXML_EXPORT DoubleModifier {
DoubleModifierDoubleModifier344     DoubleModifier(qreal v) : value(v), valid(true) {}
DoubleModifierDoubleModifier345     DoubleModifier() : value(0.0), valid(false) {}
346     qreal value;
347     bool valid;
348 };
349 
350 KOMSOOXML_EXPORT QColor colorForLuminance(const QColor& color,
351     const DoubleModifier& modulation, const DoubleModifier& offset);
352 
353 KOMSOOXML_EXPORT void modifyColor(QColor& color, qreal tint, qreal shade, qreal satMod);
354 
355 //! Converts shape types from ECMA-376 to ODF.
356 /*! @return "Common Presentation Shape Attribute" value (ODF 1.1., 9.6.1)
357             for presentation shape converted from ECMA-376 19.7.10
358             ST_PlaceholderType (Placeholder ID) value, p. 2987.
359     @param ecmaType ECMA-376 shape type
360     The conversion is useful e.g. for presentation:class attribute of draw:frame
361     out of ECMA-376's ph\@type attribute.
362     By default (and for empty argument), "outline" is returned.
363 */
364 //! @todo or "object"?  ST_PlaceholderType docs day the default is "obj".
365 //! CASE \#P500
366 KOMSOOXML_EXPORT QString ST_PlaceholderType_to_ODF(const QString& ecmaType);
367 
368 //! Sets up @p textStyleProperties with underline style matching MSOOXML name @p msooxmlName.
369 //! Based on 17.18.99 ST_Underline (Underline Patterns), WML ECMA-376 p.1681
370 //! and on 20.1.10.82 ST_TextUnderlineType (Text Underline Types), DrawingML ECMA-376 p.3450 (merged)
371 KOMSOOXML_EXPORT void setupUnderLineStyle(const QString& msooxmlName, KoCharacterStyle* textStyleProperties);
372 
373 //! @return the symbolic name of column @p column (counted from 0)
374 //! This is similar to the notation of spreadsheet's column, e.g. 0th column is "A", 1st is "B", 26th is "AA".
375 KOMSOOXML_EXPORT QString columnName(uint column);
376 
377 //! Splits @a pathAndFile into path and file parts. Path does not end with '/'.
378 KOMSOOXML_EXPORT void splitPathAndFile(const QString& pathAndFile, QString* path, QString* file);
379 
380 //! Returns calculated angle and xDiff, yDiff, caller has to apply these to style
381 KOMSOOXML_EXPORT void rotateString(const qreal rotation, const qreal width, const qreal height, qreal& angle, qreal& xDiff, qreal& yDiff);
382 
383 //! Marker related utils
384 KOMSOOXML_EXPORT QString defineMarkerStyle(KoGenStyles& mainStyles, const QString& markerType);
385 
386 KOMSOOXML_EXPORT qreal defineMarkerWidth(const QString &markerWidth, const qreal lineWidth);
387 
388 //! A helper allowing to buffer xml streams and writing them back later
389 /*! This class is useful when information that has to be written in advance is
390     based on XML elements parsed later.  In such case the information cannot be
391     saved in one pass.  Example of this is paragraphs style name: is should be
392     written to style:name attribute but relevant XML elements (that we use for
393     building the style) are appearing later.  So we first output created XML to
394     a buffer, then save the parent element with the style name and use
395     KoXmlWriter::addCompleteElement() to redirect the buffer contents as a
396     subelement.
397 
398      Example use:
399      @code
400      KoXmlWriter *body = ...;
401      XmlWriteBuffer buf;
402      body = buf.setWriter(body);
403      // ...
404      // buf.originalWriter() can be used here ...
405      // ...
406      // Use the new buffered body writer here, e.g.:
407      body->startElement("text:span", false);
408      body->addAttribute("text:style-name", currentTextStyleName);
409      body->addTextSpan(text);
410      body->endElement();
411 
412      // We are done with the buffered body writer, now release it and restore
413      // the original body writer.  This inserts all the XML buffered by buf
414      // into the original body writer (using KoXmlWriter::addCompleteElement()).
415 
416      body = buf.releaseWriter();
417      @endcode */
418 class KOMSOOXML_EXPORT XmlWriteBuffer
419 {
420 public:
421     //! Constructor; no writer is set initially.
422     XmlWriteBuffer();
423 
424     //! Destructor, releases writer if there is any set.
425     ~XmlWriteBuffer();
426 
427     //! Assigns writer @a writer to this buffer.
428     /*! From now any output directed to @a writer is written to a buffer instead.
429      Use releaseWriter() to write the changes back through the original writer.
430      @return the newly created writer, which usually should be assigned
431              to the variable passed as @a writer. */
432     KoXmlWriter* setWriter(KoXmlWriter* writer);
433 
434     //! Releases the original writer set before using setWriter(KoXmlWriter*&).
435     /*! This inserts all the XML buffered by buffer into the original body writer passed in setWriter()
436      (internally using KoXmlWriter::addCompleteElement()).
437      @return the original writer set in setWriter();
438              this writer usually should be assigned back to the variable
439              altered by the recent use of setWriter(). */
440     KoXmlWriter* releaseWriter();
441 
442     //! Releases the original writer set before using setWriter(KoXmlWriter*&).
443     /*! This inserts all the XML buffered by buffer into @a bkpXmlSnippet
444      @return the original writer set in setWriter();
445              this writer usually should be assigned back to the variable
446              altered by the recent use of setWriter(). */
447     KoXmlWriter* releaseWriter(QString& bkpXmlSnippet);
448 
449     //! @return the original writer set in setWriter(). Does not change the state of the buffer.
450     /*! Use this method when you need to access the remembered writer without releasing it. */
originalWriter()451     KoXmlWriter* originalWriter() const {
452         return m_origWriter;
453     }
454 
455     //! Clears this buffer without performing any output to the writer.
456     void clear();
457 
458     //! Returns true if the buffer is empty; otherwise returns false.
isEmpty()459     bool isEmpty() const {
460         return m_buffer.buffer().isEmpty();
461     }
462 
463 private:
464     //! Internal, used in releaseWriter() and the destructor; Does not assert when there's nothing to release.
465     KoXmlWriter* releaseWriterInternal();
466 
467     QBuffer m_buffer;
468     KoXmlWriter* m_origWriter;
469     KoXmlWriter* m_newWriter;
470 };
471 
472 //! The purpose of this class is to make sure the this->body variable is proper
473 //! set back to what it was before even if one of the TRY_READ calls lead to
474 //! us skipping out of this method. In that case we need to make sure to restore
475 //! the body variable else things may later crash.
476 //!
477 //! FIXME refactor the XmlWriteBuffer and merge this hack in so we don't
478 //! need to work-around at any place where it's used.
479 template <typename T>
480 class AutoRestore
481 {
482 public:
AutoRestore(T ** originalPtr)483     explicit AutoRestore(T** originalPtr)
484             : m_originalPtr(originalPtr), m_prevValue(*originalPtr) {
485     }
~AutoRestore()486     ~AutoRestore() {
487         if (m_originalPtr) {
488             *m_originalPtr = m_prevValue;
489         }
490     }
491 private:
492     T** m_originalPtr;
493     T* m_prevValue;
494 };
495 
496 } // Utils namespace
497 
498 } // MSOOXML namespace
499 
500 #endif /* MSOOXML_UTILS_H */
501