okular/generators/ooo/styleparser.cpp
2018-08-31 12:23:45 +03:00

500 lines
19 KiB
C++

/***************************************************************************
* Copyright (C) 2006 by Tobias Koenig <tokoe@kde.org> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
***************************************************************************/
#include "styleparser.h"
#include <QDateTime>
#include <QFont>
#include <QDomDocument>
#include <QDomElement>
#include <QXmlSimpleReader>
#include <KLocalizedString>
#include "document.h"
#include "styleinformation.h"
using namespace OOO;
StyleParser::StyleParser( const Document *document, const QDomDocument &domDocument, StyleInformation *styleInformation )
: mDocument( document ), mDomDocument( domDocument ),
mStyleInformation( styleInformation ), mMasterPageNameSet( false )
{
}
bool StyleParser::parse()
{
if ( !parseContentFile() )
return false;
if ( !parseStyleFile() )
return false;
if ( !parseMetaFile() )
return false;
return true;
}
bool StyleParser::parseContentFile()
{
const QDomElement documentElement = mDomDocument.documentElement();
QDomElement element = documentElement.firstChildElement();
while ( !element.isNull() ) {
if ( element.tagName() == QLatin1String( "document-common-attrs" ) ) {
if ( !parseDocumentCommonAttrs( element ) )
return false;
} else if ( element.tagName() == QLatin1String( "font-face-decls" ) ) {
if ( !parseFontFaceDecls( element ) )
return false;
} else if ( element.tagName() == QLatin1String( "styles" ) ) {
if ( !parseStyles( element ) )
return false;
} else if ( element.tagName() == QLatin1String( "automatic-styles" ) ) {
if ( !parseAutomaticStyles( element ) )
return false;
}
element = element.nextSiblingElement();
}
return true;
}
bool StyleParser::parseStyleFile()
{
if ( mDocument->styles().isEmpty() )
return true;
QXmlSimpleReader reader;
QXmlInputSource source;
source.setData( mDocument->styles() );
QString errorMsg;
int errorLine, errorCol;
QDomDocument document;
if ( !document.setContent( &source, &reader, &errorMsg, &errorLine, &errorCol ) ) {
qDebug( "%s at (%d,%d)", qPrintable( errorMsg ), errorLine, errorCol );
return false;
}
const QDomElement documentElement = document.documentElement();
QDomElement element = documentElement.firstChildElement();
while ( !element.isNull() ) {
if ( element.tagName() == QLatin1String( "styles" ) ) {
if ( !parseAutomaticStyles( element ) )
return false;
} else if ( element.tagName() == QLatin1String( "automatic-styles" ) ) {
if ( !parseAutomaticStyles( element ) )
return false;
} else if ( element.tagName() == QLatin1String( "master-styles" ) ) {
if ( !parseMasterStyles( element ) )
return false;
}
element = element.nextSiblingElement();
}
return true;
}
bool StyleParser::parseMetaFile()
{
if ( mDocument->meta().isEmpty() )
return true;
QXmlSimpleReader reader;
QXmlInputSource source;
source.setData( mDocument->meta() );
QString errorMsg;
int errorLine, errorCol;
QDomDocument document;
if ( !document.setContent( &source, &reader, &errorMsg, &errorLine, &errorCol ) ) {
qDebug( "%s at (%d,%d)", qPrintable( errorMsg ), errorLine, errorCol );
return false;
}
const QDomElement documentElement = document.documentElement();
QDomElement element = documentElement.firstChildElement();
while ( !element.isNull() ) {
if ( element.tagName() == QLatin1String( "meta" ) ) {
QDomElement child = element.firstChildElement();
while ( !child.isNull() ) {
if ( child.tagName() == QLatin1String( "generator" ) ) {
mStyleInformation->addMetaInformation( QStringLiteral("producer"), child.text(), i18n( "Producer" ) );
} else if ( child.tagName() == QLatin1String( "creation-date" ) ) {
const QDateTime dateTime = QDateTime::fromString( child.text(), Qt::ISODate );
mStyleInformation->addMetaInformation( QStringLiteral("creationDate"), QLocale().toString( dateTime, QLocale::LongFormat ),
i18n( "Created" ) );
} else if ( child.tagName() == QLatin1String( "initial-creator" ) ) {
mStyleInformation->addMetaInformation( QStringLiteral("creator"), child.text(), i18n( "Creator" ) );
} else if ( child.tagName() == QLatin1String( "creator" ) ) {
mStyleInformation->addMetaInformation( QStringLiteral("author"), child.text(), i18n( "Author" ) );
} else if ( child.tagName() == QLatin1String( "date" ) ) {
const QDateTime dateTime = QDateTime::fromString( child.text(), Qt::ISODate );
mStyleInformation->addMetaInformation( QStringLiteral("modificationDate"), QLocale().toString( dateTime, QLocale::LongFormat ),
i18n( "Modified" ) );
}
child = child.nextSiblingElement();
}
}
element = element.nextSiblingElement();
}
return true;
}
bool StyleParser::parseDocumentCommonAttrs( QDomElement& )
{
return true;
}
bool StyleParser::parseFontFaceDecls( QDomElement &parent )
{
QDomElement element = parent.firstChildElement();
while ( !element.isNull() ) {
if ( element.tagName() == QLatin1String( "font-face" ) ) {
FontFormatProperty property;
property.setFamily( element.attribute( QStringLiteral("font-family") ) );
mStyleInformation->addFontProperty( element.attribute( QStringLiteral("name") ), property );
} else {
qDebug( "unknown tag %s", qPrintable( element.tagName() ) );
}
element = element.nextSiblingElement();
}
return true;
}
bool StyleParser::parseStyles( QDomElement& )
{
return true;
}
bool StyleParser::parseMasterStyles( QDomElement &parent )
{
QDomElement element = parent.firstChildElement();
while ( !element.isNull() ) {
if ( element.tagName() == QLatin1String( "master-page" ) ) {
mStyleInformation->addMasterLayout( element.attribute( QStringLiteral("name") ), element.attribute( QStringLiteral("page-layout-name") ) );
if ( !mMasterPageNameSet ) {
mStyleInformation->setMasterPageName( element.attribute( QStringLiteral("name") ) );
mMasterPageNameSet = true;
}
} else {
qDebug( "unknown tag %s", qPrintable( element.tagName() ) );
}
element = element.nextSiblingElement();
}
return true;
}
bool StyleParser::parseAutomaticStyles( QDomElement &parent )
{
QDomElement element = parent.firstChildElement();
while ( !element.isNull() ) {
if ( element.tagName() == QLatin1String( "style" ) ) {
const StyleFormatProperty property = parseStyleProperty( element );
mStyleInformation->addStyleProperty( element.attribute( QStringLiteral("name") ), property );
} else if ( element.tagName() == QLatin1String( "page-layout" ) ) {
QDomElement child = element.firstChildElement();
while ( !child.isNull() ) {
if ( child.tagName() == QLatin1String( "page-layout-properties" ) ) {
const PageFormatProperty property = parsePageProperty( child );
mStyleInformation->addPageProperty( element.attribute( QStringLiteral("name") ), property );
}
child = child.nextSiblingElement();
}
} else if ( element.tagName() == QLatin1String( "list-style" ) ) {
const ListFormatProperty property = parseListProperty( element );
mStyleInformation->addListProperty( element.attribute( QStringLiteral("name") ), property );
} else if ( element.tagName() == QLatin1String( "default-style" ) ) {
StyleFormatProperty property = parseStyleProperty( element );
property.setDefaultStyle( true );
mStyleInformation->addStyleProperty( element.attribute( QStringLiteral("family") ), property );
} else {
qDebug( "unknown tag %s", qPrintable( element.tagName() ) );
}
element = element.nextSiblingElement();
}
return true;
}
StyleFormatProperty StyleParser::parseStyleProperty( QDomElement &parent )
{
StyleFormatProperty property( mStyleInformation );
property.setParentStyleName( parent.attribute( QStringLiteral("parent-style-name") ) );
property.setFamily( parent.attribute( QStringLiteral("family") ) );
if ( parent.hasAttribute( QStringLiteral("master-page-name") ) ) {
property.setMasterPageName( parent.attribute( QStringLiteral("master-page-name") ) );
if ( !mMasterPageNameSet ) {
mStyleInformation->setMasterPageName( parent.attribute( QStringLiteral("master-page-name") ) );
mMasterPageNameSet = true;
}
}
QDomElement element = parent.firstChildElement();
while ( !element.isNull() ) {
if ( element.tagName() == QLatin1String( "paragraph-properties" ) ) {
const ParagraphFormatProperty paragraphProperty = parseParagraphProperty( element );
property.setParagraphFormat( paragraphProperty );
} else if ( element.tagName() == QLatin1String( "text-properties" ) ) {
const TextFormatProperty textProperty = parseTextProperty( element );
property.setTextFormat( textProperty );
} else if ( element.tagName() == QLatin1String( "table-column-properties" ) ) {
const TableColumnFormatProperty tableColumnProperty = parseTableColumnProperty( element );
property.setTableColumnFormat( tableColumnProperty );
} else if ( element.tagName() == QLatin1String( "table-cell-properties" ) ) {
const TableCellFormatProperty tableCellProperty = parseTableCellProperty( element );
property.setTableCellFormat( tableCellProperty );
} else {
qDebug( "unknown tag %s", qPrintable( element.tagName() ) );
}
element = element.nextSiblingElement();
}
return property;
}
ParagraphFormatProperty StyleParser::parseParagraphProperty( QDomElement &parent )
{
ParagraphFormatProperty property;
property.setPageNumber( parent.attribute( QStringLiteral("page-number") ).toInt() );
static QMap<QString, ParagraphFormatProperty::WritingMode> map;
if ( map.isEmpty() ) {
map.insert( QStringLiteral("lr-tb"), ParagraphFormatProperty::LRTB );
map.insert( QStringLiteral("rl-tb"), ParagraphFormatProperty::RLTB );
map.insert( QStringLiteral("tb-rl"), ParagraphFormatProperty::TBRL );
map.insert( QStringLiteral("tb-lr"), ParagraphFormatProperty::TBLR );
map.insert( QStringLiteral("lr"), ParagraphFormatProperty::LR );
map.insert( QStringLiteral("rl"), ParagraphFormatProperty::RL );
map.insert( QStringLiteral("tb"), ParagraphFormatProperty::TB );
map.insert( QStringLiteral("page"), ParagraphFormatProperty::PAGE );
}
property.setWritingMode( map[ parent.attribute( QStringLiteral("writing-mode") ) ] );
static QMap<QString, Qt::Alignment> alignMap;
if ( alignMap.isEmpty() ) {
alignMap.insert( QStringLiteral("center"), Qt::AlignCenter );
alignMap.insert( QStringLiteral("left"), Qt::AlignLeft );
alignMap.insert( QStringLiteral("right"), Qt::AlignRight );
alignMap.insert( QStringLiteral("justify"), Qt::AlignJustify );
if ( property.writingModeIsRightToLeft() ) {
alignMap.insert( QStringLiteral("start"), Qt::AlignRight );
alignMap.insert( QStringLiteral("end"), Qt::AlignLeft );
} else {
// not right to left
alignMap.insert( QStringLiteral("start"), Qt::AlignLeft );
alignMap.insert( QStringLiteral("end"), Qt::AlignRight );
}
}
if ( parent.hasAttribute( QStringLiteral("text-align") ) ) {
property.setTextAlignment( alignMap[ parent.attribute( QStringLiteral("text-align"), QStringLiteral("left") ) ] );
}
const QString marginLeft = parent.attribute( QStringLiteral("margin-left") );
if ( !marginLeft.isEmpty() ) {
qreal leftMargin = qRound( convertUnit( marginLeft ) );
property.setLeftMargin( leftMargin );
}
const QString colorText = parent.attribute( QStringLiteral("background-color") );
if ( !colorText.isEmpty() && colorText != QLatin1String( "transparent" ) ) {
property.setBackgroundColor( QColor( colorText ) );
}
return property;
}
TextFormatProperty StyleParser::parseTextProperty( QDomElement &parent )
{
TextFormatProperty property;
const QString fontSize = parent.attribute( QStringLiteral("font-size") );
if ( !fontSize.isEmpty() )
property.setFontSize( qRound( convertUnit( fontSize ) ) );
static QMap<QString, QFont::Weight> weightMap;
if ( weightMap.isEmpty() ) {
weightMap.insert( QStringLiteral("normal"), QFont::Normal );
weightMap.insert( QStringLiteral("bold"), QFont::Bold );
}
const QString fontWeight = parent.attribute( QStringLiteral("font-weight") );
if ( !fontWeight.isEmpty() )
property.setFontWeight( weightMap[ fontWeight ] );
static QMap<QString, QFont::Style> fontStyleMap;
if ( fontStyleMap.isEmpty() ) {
fontStyleMap.insert( QStringLiteral("normal"), QFont::StyleNormal );
fontStyleMap.insert( QStringLiteral("italic"), QFont::StyleItalic );
fontStyleMap.insert( QStringLiteral("oblique"), QFont::StyleOblique );
}
const QString fontStyle = parent.attribute( QStringLiteral("font-style") );
if ( !fontStyle.isEmpty() )
property.setFontStyle( fontStyleMap.value( fontStyle, QFont::StyleNormal ) );
const QColor color( parent.attribute( QStringLiteral("color") ) );
if ( color.isValid() ) {
property.setColor( color );
}
const QString colorText = parent.attribute( QStringLiteral("background-color") );
if ( !colorText.isEmpty() && colorText != QLatin1String( "transparent" ) ) {
property.setBackgroundColor( QColor( colorText ) );
}
return property;
}
PageFormatProperty StyleParser::parsePageProperty( QDomElement &parent )
{
PageFormatProperty property;
property.setBottomMargin( convertUnit( parent.attribute( QStringLiteral("margin-bottom") ) ) );
property.setLeftMargin( convertUnit( parent.attribute( QStringLiteral("margin-left") ) ) );
property.setTopMargin( convertUnit( parent.attribute( QStringLiteral("margin-top") ) ) );
property.setRightMargin( convertUnit( parent.attribute( QStringLiteral("margin-right") ) ) );
property.setWidth( convertUnit( parent.attribute( QStringLiteral("page-width") ) ) );
property.setHeight( convertUnit( parent.attribute( QStringLiteral("page-height") ) ) );
return property;
}
ListFormatProperty StyleParser::parseListProperty( QDomElement &parent )
{
ListFormatProperty property;
QDomElement element = parent.firstChildElement();
if ( element.tagName() == QLatin1String( "list-level-style-number" ) )
property = ListFormatProperty( ListFormatProperty::Number );
else
property = ListFormatProperty( ListFormatProperty::Bullet );
while ( !element.isNull() ) {
if ( element.tagName() == QLatin1String( "list-level-style-number" ) ) {
int level = element.attribute( QStringLiteral("level") ).toInt();
property.addItem( level, 0.0 );
} else if ( element.tagName() == QLatin1String( "list-level-style-bullet" ) ) {
int level = element.attribute( QStringLiteral("level") ).toInt();
property.addItem( level, convertUnit( element.attribute( QStringLiteral("space-before") ) ) );
}
element = element.nextSiblingElement();
}
return property;
}
TableColumnFormatProperty StyleParser::parseTableColumnProperty( QDomElement &parent )
{
TableColumnFormatProperty property;
const double width = convertUnit( parent.attribute( QStringLiteral("column-width") ) );
property.setWidth( width );
return property;
}
TableCellFormatProperty StyleParser::parseTableCellProperty( QDomElement &parent )
{
TableCellFormatProperty property;
if ( parent.hasAttribute( QStringLiteral("background-color") ) )
property.setBackgroundColor( QColor( parent.attribute( QStringLiteral("background-color") ) ) );
property.setPadding( convertUnit( parent.attribute( QStringLiteral("padding") ) ) );
static QMap<QString, Qt::Alignment> map;
if ( map.isEmpty() ) {
map.insert( QStringLiteral("top"), Qt::AlignTop );
map.insert( QStringLiteral("middle"), Qt::AlignVCenter );
map.insert( QStringLiteral("bottom"), Qt::AlignBottom );
map.insert( QStringLiteral("left"), Qt::AlignLeft );
map.insert( QStringLiteral("right"), Qt::AlignRight );
map.insert( QStringLiteral("center"), Qt::AlignHCenter );
}
if ( parent.hasAttribute( QStringLiteral("align") ) && parent.hasAttribute( QStringLiteral("vertical-align") ) ) {
property.setAlignment( map[ parent.attribute( QStringLiteral("align") ) ] | map[ parent.attribute( QStringLiteral("vertical-align") ) ] );
} else if ( parent.hasAttribute( QStringLiteral("align") ) ) {
property.setAlignment( map[ parent.attribute( QStringLiteral("align") ) ] );
} else if ( parent.hasAttribute( QStringLiteral("vertical-align") ) ) {
property.setAlignment( map[ parent.attribute( QStringLiteral("vertical-align") ) ] );
}
return property;
}
double StyleParser::convertUnit( const QString &data )
{
#define MM_TO_POINT(mm) ((mm)*2.83465058)
#define CM_TO_POINT(cm) ((cm)*28.3465058)
#define DM_TO_POINT(dm) ((dm)*283.465058)
#define INCH_TO_POINT(inch) ((inch)*72.0)
#define PI_TO_POINT(pi) ((pi)*12)
#define DD_TO_POINT(dd) ((dd)*154.08124)
#define CC_TO_POINT(cc) ((cc)*12.840103)
double points = 0;
if ( data.endsWith( QLatin1String("pt") ) ) {
points = data.leftRef( data.length() - 2 ).toDouble();
} else if ( data.endsWith( QLatin1String("cm") ) ) {
double value = data.leftRef( data.length() - 2 ).toDouble();
points = CM_TO_POINT( value );
} else if ( data.endsWith( QLatin1String("mm") ) ) {
double value = data.leftRef( data.length() - 2 ).toDouble();
points = MM_TO_POINT( value );
} else if ( data.endsWith( QLatin1String("dm") ) ) {
double value = data.leftRef( data.length() - 2 ).toDouble();
points = DM_TO_POINT( value );
} else if ( data.endsWith( QLatin1String("in") ) ) {
double value = data.leftRef( data.length() - 2 ).toDouble();
points = INCH_TO_POINT( value );
} else if ( data.endsWith( QLatin1String("inch") ) ) {
double value = data.leftRef( data.length() - 4 ).toDouble();
points = INCH_TO_POINT( value );
} else if ( data.endsWith( QLatin1String("pi") ) ) {
double value = data.leftRef( data.length() - 4 ).toDouble();
points = PI_TO_POINT( value );
} else if ( data.endsWith( QLatin1String("dd") ) ) {
double value = data.leftRef( data.length() - 4 ).toDouble();
points = DD_TO_POINT( value );
} else if ( data.endsWith( QLatin1String("cc") ) ) {
double value = data.leftRef( data.length() - 4 ).toDouble();
points = CC_TO_POINT( value );
} else {
if ( !data.isEmpty() ) {
qDebug( "unknown unit for '%s'", qPrintable( data ) );
}
points = 12;
}
return points;
}