1 /***************************************************************************
2 * Copyright (C) 2015 by Jens Nissen jens-chessx@gmx.net *
3 * Copyright (C) 2006 by Tobias Koenig <tokoe@kde.org> *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 ***************************************************************************/
10
11 #include "styleparser.h"
12 #include "document.h"
13 #include "styleinformation.h"
14
15 #include <QDateTime>
16 #include <QFont>
17 #include <QDomDocument>
18 #include <QDomElement>
19 #include <QXmlSimpleReader>
20 #include <QDebug>
21
22 #if defined(_MSC_VER) && defined(_DEBUG)
23 #define DEBUG_NEW new( _NORMAL_BLOCK, __FILE__, __LINE__ )
24 #define new DEBUG_NEW
25 #endif // _MSC_VER
26
27 using namespace OOO;
28
StyleParser(const Document * document,const QDomDocument & domDocument,StyleInformation * styleInformation)29 StyleParser::StyleParser( const Document *document, const QDomDocument &domDocument, StyleInformation *styleInformation )
30 : m_pDocument( document ), m_DomDocument( domDocument ),
31 m_StyleInformation( styleInformation ), m_MasterPageNameSet( false )
32 {
33 }
34
parse()35 bool StyleParser::parse()
36 {
37 int foundStyles = 0;
38 if ( !parseContentFile() )
39 foundStyles++;
40
41 if ( !parseStyleFile() )
42 foundStyles++;
43
44 if ( !parseMetaFile() )
45 foundStyles++;
46
47 return (foundStyles>0);
48 }
49
parseContentFile()50 bool StyleParser::parseContentFile()
51 {
52 const QDomElement documentElement = m_DomDocument.documentElement();
53 QDomElement element = documentElement.firstChildElement();
54 while ( !element.isNull() ) {
55 if ( element.tagName() == QLatin1String( "document-common-attrs" ) ) {
56 if ( !parseDocumentCommonAttrs( element ) )
57 return false;
58 } else if ( element.tagName() == QLatin1String( "font-face-decls" ) ) {
59 if ( !parseFontFaceDecls( element ) )
60 return false;
61 } else if ( element.tagName() == QLatin1String( "styles" ) ) {
62 if ( !parseStyles( element ) )
63 return false;
64 } else if ( element.tagName() == QLatin1String( "automatic-styles" ) ) {
65 if ( !parseAutomaticStyles( element ) )
66 return false;
67 }
68
69 element = element.nextSiblingElement();
70 }
71
72 return true;
73 }
74
parseStyleFile()75 bool StyleParser::parseStyleFile()
76 {
77 QXmlSimpleReader reader;
78
79 QXmlInputSource source;
80 source.setData( m_pDocument->styles() );
81
82 QString errorMsg;
83 int errorLine, errorCol;
84
85 QDomDocument document;
86 if ( !document.setContent( &source, &reader, &errorMsg, &errorLine, &errorCol ) ) {
87 qDebug( "%s at (%d,%d)", qPrintable( errorMsg ), errorLine, errorCol );
88 return false;
89 }
90
91 const QDomElement documentElement = document.documentElement();
92 QDomElement element = documentElement.firstChildElement();
93 while ( !element.isNull() ) {
94 if ( element.tagName() == QLatin1String( "styles" ) ) {
95 if ( !parseAutomaticStyles( element ) )
96 return false;
97 } else if ( element.tagName() == QLatin1String( "automatic-styles" ) ) {
98 if ( !parseAutomaticStyles( element ) )
99 return false;
100 } else if ( element.tagName() == QLatin1String( "master-styles" ) ) {
101 if ( !parseMasterStyles( element ) )
102 return false;
103 }
104
105 element = element.nextSiblingElement();
106 }
107
108 return true;
109 }
110
parseMetaFile()111 bool StyleParser::parseMetaFile()
112 {
113 QXmlSimpleReader reader;
114
115 QXmlInputSource source;
116 source.setData( m_pDocument->meta() );
117
118 QString errorMsg;
119 int errorLine, errorCol;
120
121 QDomDocument document;
122 if ( !document.setContent( &source, &reader, &errorMsg, &errorLine, &errorCol ) ) {
123 qDebug( "%s at (%d,%d)", qPrintable( errorMsg ), errorLine, errorCol );
124 return false;
125 }
126
127 const QDomElement documentElement = document.documentElement();
128 QDomElement element = documentElement.firstChildElement();
129 while ( !element.isNull() ) {
130 if ( element.tagName() == QLatin1String( "meta" ) ) {
131 QDomElement child = element.firstChildElement();
132 while ( !child.isNull() ) {
133 if ( child.tagName() == QLatin1String( "generator" ) ) {
134 m_StyleInformation->addMetaInformation( "producer", child.text(), "Producer");
135 } else if ( child.tagName() == QLatin1String( "creation-date" ) ) {
136 const QDateTime dateTime = QDateTime::fromString( child.text(), Qt::ISODate );
137 qDebug() << "### dateTime ... " << dateTime << " file" << __FILE__ << ":" << __LINE__;
138 //// mStyleInformation->addMetaInformation( "creationDate", KGlobal::locale()->formatDateTime( dateTime, KLocale::LongDate, true ),i18n( "Created" ) );
139 } else if ( child.tagName() == QLatin1String( "initial-creator" ) ) {
140 m_StyleInformation->addMetaInformation( "creator", child.text(), "Creator");
141 } else if ( child.tagName() == QLatin1String( "creator" ) ) {
142 m_StyleInformation->addMetaInformation( "author", child.text(), "Author");
143 } else if ( child.tagName() == QLatin1String( "date" ) ) {
144 const QDateTime dateTime = QDateTime::fromString( child.text(), Qt::ISODate );
145 qDebug() << "### dateTime ... " << dateTime << " file" << __FILE__ << ":" << __LINE__;
146 //// mStyleInformation->addMetaInformation( "modificationDate", KGlobal::locale()->formatDateTime( dateTime, KLocale::LongDate, true ), i18n( "Modified" ) );
147 }
148
149 child = child.nextSiblingElement();
150 }
151 }
152
153 element = element.nextSiblingElement();
154 }
155
156 return true;
157 }
158
parseDocumentCommonAttrs(QDomElement &)159 bool StyleParser::parseDocumentCommonAttrs( QDomElement& )
160 {
161 return true;
162 }
163
parseFontFaceDecls(QDomElement & parent)164 bool StyleParser::parseFontFaceDecls( QDomElement &parent )
165 {
166 QDomElement element = parent.firstChildElement();
167 while ( !element.isNull() ) {
168 if ( element.tagName() == QLatin1String( "font-face" ) ) {
169 FontFormatProperty property;
170 property.setFamily( element.attribute( "font-family" ) );
171
172 m_StyleInformation->addFontProperty( element.attribute( "name" ), property );
173 } else {
174 qDebug( "unknown tag %s", qPrintable( element.tagName() ) );
175 }
176
177 element = element.nextSiblingElement();
178 }
179
180 return true;
181 }
182
parseStyles(QDomElement &)183 bool StyleParser::parseStyles( QDomElement& )
184 {
185 return true;
186 }
187
parseMasterStyles(QDomElement & parent)188 bool StyleParser::parseMasterStyles( QDomElement &parent )
189 {
190 QDomElement element = parent.firstChildElement();
191 while ( !element.isNull() ) {
192 if ( element.tagName() == QLatin1String( "master-page" ) ) {
193 m_StyleInformation->addMasterLayout( element.attribute( "name" ), element.attribute( "page-layout-name" ) );
194 if ( !m_MasterPageNameSet ) {
195 m_StyleInformation->setMasterPageName( element.attribute( "name" ) );
196 m_MasterPageNameSet = true;
197 }
198 } else {
199 qDebug( "unknown tag %s", qPrintable( element.tagName() ) );
200 }
201
202 element = element.nextSiblingElement();
203 }
204
205 return true;
206 }
207
parseAutomaticStyles(QDomElement & parent)208 bool StyleParser::parseAutomaticStyles( QDomElement &parent )
209 {
210 QDomElement element = parent.firstChildElement();
211 while ( !element.isNull() ) {
212 if ( element.tagName() == QLatin1String( "style" ) )
213 {
214 const StyleFormatProperty property = parseStyleProperty( element );
215 m_StyleInformation->addStyleProperty( element.attribute( "name" ), property );
216 }
217 else if ( element.tagName() == QLatin1String( "page-layout" ) )
218 {
219 QDomElement child = element.firstChildElement();
220 while ( !child.isNull() ) {
221 if ( child.tagName() == QLatin1String( "page-layout-properties" ) ) {
222 const PageFormatProperty property = parsePageProperty( child );
223 m_StyleInformation->addPageProperty( element.attribute( "name" ), property );
224 }
225
226 child = child.nextSiblingElement();
227 }
228 }
229 else if ( element.tagName() == QLatin1String( "list-style" ) )
230 {
231 const ListFormatProperty property = parseListProperty( element );
232 m_StyleInformation->addListProperty( element.attribute( "name" ), property );
233 }
234 else if ( element.tagName() == QLatin1String( "default-style" ) )
235 {
236 StyleFormatProperty property = parseStyleProperty( element );
237 property.setDefaultStyle( true );
238 m_StyleInformation->addStyleProperty( element.attribute( "family" ), property );
239 }
240 else
241 {
242 qDebug( "unknown tag %s", qPrintable( element.tagName() ) );
243 }
244
245 element = element.nextSiblingElement();
246 }
247
248 return true;
249 }
250
parseStyleProperty(QDomElement & parent)251 StyleFormatProperty StyleParser::parseStyleProperty( QDomElement &parent )
252 {
253 StyleFormatProperty property( m_StyleInformation );
254
255 property.setParentStyleName( parent.attribute( "parent-style-name" ) );
256 property.setFamily( parent.attribute( "family" ) );
257 if ( parent.hasAttribute( "master-page-name" ) ) {
258 property.setMasterPageName( parent.attribute( "master-page-name" ) );
259 if ( !m_MasterPageNameSet ) {
260 m_StyleInformation->setMasterPageName( parent.attribute( "master-page-name" ) );
261 m_MasterPageNameSet = true;
262 }
263 }
264
265 QDomElement element = parent.firstChildElement();
266 while ( !element.isNull() )
267 {
268 if ( element.tagName() == QLatin1String( "paragraph-properties" ) )
269 {
270 const ParagraphFormatProperty paragraphProperty = parseParagraphProperty( element );
271 property.setParagraphFormat( paragraphProperty );
272 }
273 else if ( element.tagName() == QLatin1String( "text-properties" ) )
274 {
275 const TextFormatProperty textProperty = parseTextProperty( element );
276 property.setTextFormat( textProperty );
277 }
278 else if ( element.tagName() == QLatin1String( "table-column-properties" ) )
279 {
280 const TableColumnFormatProperty tableColumnProperty = parseTableColumnProperty( element );
281 property.setTableColumnFormat( tableColumnProperty );
282 }
283 else if ( element.tagName() == QLatin1String( "table-cell-properties" ) )
284 {
285 const TableCellFormatProperty tableCellProperty = parseTableCellProperty( element );
286 property.setTableCellFormat( tableCellProperty );
287 }
288 else
289 {
290 qDebug( "unknown tag %s", qPrintable( element.tagName() ) );
291 }
292
293 element = element.nextSiblingElement();
294 }
295
296 return property;
297 }
298
parseParagraphProperty(QDomElement & parent)299 ParagraphFormatProperty StyleParser::parseParagraphProperty( QDomElement &parent )
300 {
301 ParagraphFormatProperty property;
302
303 property.setPageNumber( parent.attribute( "page-number" ).toInt() );
304
305 static QMap<QString, ParagraphFormatProperty::WritingMode> map;
306 if ( map.isEmpty() ) {
307 map.insert( "lr-tb", ParagraphFormatProperty::LRTB );
308 map.insert( "rl-tb", ParagraphFormatProperty::RLTB );
309 map.insert( "tb-rl", ParagraphFormatProperty::TBRL );
310 map.insert( "tb-lr", ParagraphFormatProperty::TBLR );
311 map.insert( "lr", ParagraphFormatProperty::LR );
312 map.insert( "rl", ParagraphFormatProperty::RL );
313 map.insert( "tb", ParagraphFormatProperty::TB );
314 map.insert( "page", ParagraphFormatProperty::PAGE );
315 }
316 property.setWritingMode( map[ parent.attribute( "writing-mode" ) ] );
317
318 static QMap<QString, Qt::Alignment> alignMap;
319 if ( alignMap.isEmpty() ) {
320 alignMap.insert( "center", Qt::AlignCenter );
321 alignMap.insert( "left", Qt::AlignLeft );
322 alignMap.insert( "right", Qt::AlignRight );
323 alignMap.insert( "justify", Qt::AlignJustify );
324 if ( property.writingModeIsRightToLeft() ) {
325 alignMap.insert( "start", Qt::AlignRight );
326 alignMap.insert( "end", Qt::AlignLeft );
327 } else {
328 // not right to left
329 alignMap.insert( "start", Qt::AlignLeft );
330 alignMap.insert( "end", Qt::AlignRight );
331 }
332 }
333 if ( parent.hasAttribute( "text-align" ) ) {
334 property.setTextAlignment( alignMap[ parent.attribute( "text-align", "left" ) ] );
335 }
336
337 const QString marginLeft = parent.attribute( "margin-left" );
338 if ( !marginLeft.isEmpty() ) {
339 qreal leftMargin = qRound( convertUnit( marginLeft ) );
340 property.setLeftMargin( leftMargin );
341 }
342
343 const QString colorText = parent.attribute( "background-color" );
344 if ( !colorText.isEmpty() && colorText != QLatin1String( "transparent" ) ) {
345 property.setBackgroundColor( QColor( colorText ) );
346 }
347
348 return property;
349 }
350
parseTextProperty(QDomElement & parent)351 TextFormatProperty StyleParser::parseTextProperty( QDomElement &parent )
352 {
353 TextFormatProperty property;
354
355 const QString fontSize = parent.attribute( "font-size" );
356 if ( !fontSize.isEmpty() )
357 property.setFontSize( qRound( convertUnit( fontSize ) ) );
358
359 static QMap<QString, QFont::Weight> weightMap;
360 if ( weightMap.isEmpty() )
361 {
362 weightMap.insert( "normal", QFont::Normal );
363 weightMap.insert( "bold", QFont::Bold );
364 }
365
366 const QString fontWeight = parent.attribute( "font-weight" );
367 if ( !fontWeight.isEmpty() )
368 {
369 property.setFontWeight( weightMap[ fontWeight ] );
370 }
371
372 static QMap<QString, QFont::Style> fontStyleMap;
373 if ( fontStyleMap.isEmpty() )
374 {
375 fontStyleMap.insert( "normal", QFont::StyleNormal );
376 fontStyleMap.insert( "italic", QFont::StyleItalic );
377 fontStyleMap.insert( "oblique", QFont::StyleOblique );
378 }
379
380 const QString fontStyle = parent.attribute( "font-style" );
381 if ( !fontStyle.isEmpty() )
382 {
383 property.setFontStyle( fontStyleMap.value( fontStyle, QFont::StyleNormal ) );
384 }
385
386 const QColor color( parent.attribute( "color" ) );
387 if ( color.isValid() )
388 {
389 property.setColor( color );
390 }
391
392 const QString colorText = parent.attribute( "background-color" );
393 if ( !colorText.isEmpty() && colorText != QLatin1String( "transparent" ) )
394 {
395 property.setBackgroundColor( QColor( colorText ) );
396 }
397
398 const QString underlineType = parent.attribute( "text-underline-style" );
399 if ( !underlineType.isEmpty() && underlineType != QLatin1String( "none" ) )
400 {
401 property.setUnderline( true );
402 }
403
404 return property;
405 }
406
parsePageProperty(QDomElement & parent)407 PageFormatProperty StyleParser::parsePageProperty( QDomElement &parent )
408 {
409 PageFormatProperty property;
410
411 property.setBottomMargin( convertUnit( parent.attribute( "margin-bottom" ) ) );
412 property.setLeftMargin( convertUnit( parent.attribute( "margin-left" ) ) );
413 property.setTopMargin( convertUnit( parent.attribute( "margin-top" ) ) );
414 property.setRightMargin( convertUnit( parent.attribute( "margin-right" ) ) );
415 property.setWidth( convertUnit( parent.attribute( "page-width" ) ) );
416 property.setHeight( convertUnit( parent.attribute( "page-height" ) ) );
417
418 return property;
419 }
420
parseListProperty(QDomElement & parent)421 ListFormatProperty StyleParser::parseListProperty( QDomElement &parent )
422 {
423 ListFormatProperty property;
424
425 static QMap<QString, QTextListFormat::Style> map;
426 if ( map.isEmpty() )
427 {
428 map.insert( "●", QTextListFormat::ListDisc );
429 map.insert( "○", QTextListFormat::ListCircle );
430 map.insert( "□", QTextListFormat::ListSquare );
431 map.insert( "1", QTextListFormat::ListDecimal );
432 map.insert( "a", QTextListFormat::ListLowerAlpha );
433 map.insert( "A", QTextListFormat::ListUpperAlpha );
434 map.insert( "i", QTextListFormat::ListLowerRoman );
435 map.insert( "I", QTextListFormat::ListUpperRoman );
436 }
437
438 QDomElement element = parent.firstChildElement();
439 if ( element.tagName() == QLatin1String( "list-level-style-number" ) )
440 {
441 property.setType( map[ element.attribute( "num-format" ) ] );
442 }
443 else if (element.tagName() == QLatin1String( "list-level-style-bullet" ) )
444 {
445 property.setType( map[ element.attribute( "bullet-char" ) ] );
446 }
447
448 while ( !element.isNull() )
449 {
450 if ( element.tagName() == QLatin1String( "list-level-style-number" ) )
451 {
452 int level = element.attribute( "level" ).toInt();
453 property.addItem( level, 0.0 );
454 }
455 else if ( element.tagName() == QLatin1String( "list-level-style-bullet" ) )
456 {
457 int level = element.attribute( "level" ).toInt();
458 property.addItem( level, convertUnit( element.attribute( "space-before" ) ) );
459 }
460
461 element = element.nextSiblingElement();
462 }
463
464 return property;
465 }
466
parseTableColumnProperty(QDomElement & parent)467 TableColumnFormatProperty StyleParser::parseTableColumnProperty( QDomElement &parent )
468 {
469 TableColumnFormatProperty property;
470
471 const double width = convertUnit( parent.attribute( "column-width" ) );
472 property.setWidth( width );
473
474 return property;
475 }
476
parseTableCellProperty(QDomElement & parent)477 TableCellFormatProperty StyleParser::parseTableCellProperty( QDomElement &parent )
478 {
479 TableCellFormatProperty property;
480
481 if ( parent.hasAttribute( "background-color" ) )
482 property.setBackgroundColor( QColor( parent.attribute( "background-color" ) ) );
483
484 property.setPadding( convertUnit( parent.attribute( "padding" ) ) );
485
486 static QMap<QString, Qt::Alignment> map;
487 if ( map.isEmpty() ) {
488 map.insert( "top", Qt::AlignTop );
489 map.insert( "middle", Qt::AlignVCenter );
490 map.insert( "bottom", Qt::AlignBottom );
491 map.insert( "left", Qt::AlignLeft );
492 map.insert( "right", Qt::AlignRight );
493 map.insert( "center", Qt::AlignHCenter );
494 }
495
496 if ( parent.hasAttribute( "align" ) && parent.hasAttribute( "vertical-align" ) ) {
497 property.setAlignment( map[ parent.attribute( "align" ) ] | map[ parent.attribute( "vertical-align" ) ] );
498 } else if ( parent.hasAttribute( "align" ) ) {
499 property.setAlignment( map[ parent.attribute( "align" ) ] );
500 } else if ( parent.hasAttribute( "vertical-align" ) ) {
501 property.setAlignment( map[ parent.attribute( "vertical-align" ) ] );
502 }
503
504 return property;
505 }
506
convertUnit(const QString & data)507 double StyleParser::convertUnit( const QString &data )
508 {
509 #define MM_TO_POINT(mm) ((mm)*2.83465058)
510 #define CM_TO_POINT(cm) ((cm)*28.3465058)
511 #define DM_TO_POINT(dm) ((dm)*283.465058)
512 #define INCH_TO_POINT(inch) ((inch)*72.0)
513 #define PI_TO_POINT(pi) ((pi)*12)
514 #define DD_TO_POINT(dd) ((dd)*154.08124)
515 #define CC_TO_POINT(cc) ((cc)*12.840103)
516
517 double points = 0;
518 if ( data.endsWith( "pt" ) ) {
519 points = data.leftRef( data.length() - 2 ).toDouble();
520 } else if ( data.endsWith( "cm" ) ) {
521 double value = data.leftRef( data.length() - 2 ).toDouble();
522 points = CM_TO_POINT( value );
523 } else if ( data.endsWith( "mm" ) ) {
524 double value = data.leftRef( data.length() - 2 ).toDouble();
525 points = MM_TO_POINT( value );
526 } else if ( data.endsWith( "dm" ) ) {
527 double value = data.leftRef( data.length() - 2 ).toDouble();
528 points = DM_TO_POINT( value );
529 } else if ( data.endsWith( "in" ) ) {
530 double value = data.leftRef( data.length() - 2 ).toDouble();
531 points = INCH_TO_POINT( value );
532 } else if ( data.endsWith( "inch" ) ) {
533 double value = data.leftRef( data.length() - 4 ).toDouble();
534 points = INCH_TO_POINT( value );
535 } else if ( data.endsWith( "pi" ) ) {
536 double value = data.leftRef( data.length() - 4 ).toDouble();
537 points = PI_TO_POINT( value );
538 } else if ( data.endsWith( "dd" ) ) {
539 double value = data.leftRef( data.length() - 4 ).toDouble();
540 points = DD_TO_POINT( value );
541 } else if ( data.endsWith( "cc" ) ) {
542 double value = data.leftRef( data.length() - 4 ).toDouble();
543 points = CC_TO_POINT( value );
544 } else {
545 if ( !data.isEmpty() ) {
546 qDebug( "unknown unit for '%s'", qPrintable( data ) );
547 }
548 points = 12;
549 }
550
551 return points;
552 }
553