/*************************************************************************** * Copyright (C) 2006 by Tobias Koenig * * * * 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 #include #include #include #include #include #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 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 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 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 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 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; }