Warning, file /office/calligra/filters/karbon/xfig/XFigOdgWriter.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /* This file is part of the Calligra project, made within the KDE community.
0002 
0003    SPDX-FileCopyrightText: 2012 Friedrich W. H. Kossebau <kossebau@kde.org>
0004 
0005    SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "XFigOdgWriter.h"
0009 
0010 // filter
0011 #include "XFigDocument.h"
0012 // Calligra
0013 #include <KoXmlWriter.h>
0014 #include <KoXmlNS.h>
0015 #include <KoOdf.h>
0016 #include <KoStore.h>
0017 #include <KoDocumentInfo.h>
0018 
0019 // keep in sync with XFigPageSizeType
0020 static const
0021 struct PageSize
0022 {
0023     const char* width;
0024     const char* height;
0025 } pageSizeTable[29] =
0026 {
0027     {  "8.5in",  "11in"}, // letter
0028     {  "8.5in",  "14in"}, // legal
0029     { "11in",    "17in"}, // tabloid
0030 
0031     {  "8.5in",  "11in"}, // A
0032     { "11in",    "17in"}, // B
0033     { "17in",    "22in"}, // C
0034     { "22in",    "34in"}, // D
0035     { "34in",    "44in"}, // E
0036 
0037     { "37mm",    "52mm"}, // A9
0038     { "52mm",    "74mm"}, // A8
0039     { "74mm",   "105mm"}, // A7
0040     {"105mm",   "148mm"}, // A6
0041     {"148mm",   "297mm"}, // A5
0042     {"210mm",   "297mm"}, // A4
0043     {"297mm",   "420mm"}, // A3
0044     {"420mm",   "594mm"}, // A2
0045     {"594mm",   "841mm"}, // A1
0046     {"841mm",  "1189mm"}, // A0
0047 
0048     {  "32mm",    "45mm"}, // B10
0049     {  "45mm",    "64mm"}, // B9
0050     {  "64mm",    "91mm"}, // B8
0051     {  "91mm",   "128mm"}, // B7
0052     { "128mm",   "182mm"}, // B6
0053     { "182mm",   "257mm"}, // B5
0054     { "257mm",   "364mm"}, // B4
0055     { "364mm",   "515mm"}, // B3
0056     { "515mm",   "728mm"}, // B2
0057     { "728mm",  "1030mm"}, // B1
0058     {"1030mm",  "1456mm"}  // B0
0059 };
0060 
0061 
0062 // Metric conversion
0063 // 1 point = 1/72 inches
0064 static const double inchtoPtFactor = 72;
0065 // TODO: check that support for both inch and metric system works
0066 
0067 static inline
0068 double
0069 ptUnit( double x )
0070 {
0071     return x * inchtoPtFactor;
0072 }
0073 
0074 static inline
0075 double
0076 odfCornerRadius( qint32 xfigRadius )
0077 {
0078     return ptUnit( static_cast<double>(xfigRadius) / 80);
0079 }
0080 
0081 static inline
0082 double
0083 odfLineThickness( qint32 xfigLineThickness )
0084 {
0085     return ptUnit( static_cast<double>(xfigLineThickness) / 80);
0086 }
0087 
0088 double
0089 XFigOdgWriter::odfLength( qint32 length ) const
0090 {
0091     return ptUnit( static_cast<double>(length) / m_Document->resolution() );
0092 }
0093 
0094 double
0095 XFigOdgWriter::odfLength( double length ) const
0096 {
0097     return ptUnit( length / m_Document->resolution() );
0098 }
0099 
0100 double
0101 XFigOdgWriter::odfXCoord( qint32 x ) const
0102 {
0103     return ptUnit( static_cast<double>(x) / m_Document->resolution() );
0104 }
0105 
0106 double
0107 XFigOdgWriter::odfYCoord( qint32 y ) const
0108 {
0109     return ptUnit( static_cast<double>(y) / m_Document->resolution() );
0110 }
0111 
0112 
0113 
0114 XFigOdgWriter::XFigOdgWriter( KoStore* outputStore )
0115   : m_CLocale(QLocale::c())
0116   , m_OdfWriteStore( outputStore )
0117   , m_OutputStore( outputStore )
0118   , m_PageCount( 0 )
0119 {
0120     m_CLocale.setNumberOptions(QLocale::OmitGroupSeparator);
0121     m_ManifestWriter = m_OdfWriteStore.manifestWriter( KoOdf::mimeType(KoOdf::Graphics) );
0122 }
0123 
0124 XFigOdgWriter::~XFigOdgWriter()
0125 {
0126     m_OdfWriteStore.closeManifestWriter();
0127 
0128     delete m_OutputStore;
0129 }
0130 
0131 bool
0132 XFigOdgWriter::write( XFigDocument* document )
0133 {
0134     m_Document = document;
0135 
0136     storePixelImageFiles();
0137 
0138     // Create content.xml
0139     storeContentXml();
0140 
0141     // Create the styles.xml file
0142     m_StyleCollector.saveOdfStylesDotXml( m_OutputStore, m_ManifestWriter );
0143 
0144     // Create meta.xml
0145     storeMetaXml();
0146 
0147     return true;
0148 }
0149 
0150 void
0151 XFigOdgWriter::storePixelImageFiles()
0152 {
0153     // TODO: store pixel files linked from XFigPictureBoxObject
0154 #if 0
0155     // TODO: as mManifestWriter needs full rel path, perhaps drop enterDirectory/leaveDirectory
0156     mOutputStore->enterDirectory( QLatin1String("Pictures") );
0157 
0158     foreach( const XFigPixelImage* image, mDocument->pixelImages() )
0159     {
0160         mOutputStore->open( fileName );
0161         mOutputStore->write( *image );
0162         mOutputStore->close();
0163 
0164         mManifestWriter->addManifestEntry( filePath, mediaType );
0165     }
0166 
0167     mOutputStore->leaveDirectory();
0168 #endif
0169 }
0170 
0171 
0172 void
0173 XFigOdgWriter::storeMetaXml()
0174 {
0175     KoDocumentInfo documentInfo;
0176     documentInfo.setOriginalGenerator(QLatin1String("Calligra XFig filter"));
0177     documentInfo.setAboutInfo(QLatin1String("comments"), m_Document->comment());
0178 
0179     const QString documentInfoFilePath = QLatin1String( "meta.xml" );
0180 
0181     m_OutputStore->open(documentInfoFilePath);
0182     documentInfo.saveOasis(m_OutputStore);
0183     m_OutputStore->close();
0184 
0185     // TODO: "text/xml" could be a static string
0186     m_ManifestWriter->addManifestEntry( documentInfoFilePath, QLatin1String("text/xml") );
0187 }
0188 
0189 void
0190 XFigOdgWriter::storeContentXml()
0191 {
0192     KoXmlWriter* contentWriter = m_OdfWriteStore.contentWriter();
0193     m_BodyWriter = m_OdfWriteStore.bodyWriter();
0194 
0195     m_BodyWriter->startElement( "office:body" );
0196     m_BodyWriter->startElement( KoOdf::bodyContentElement(KoOdf::Graphics, true));
0197 
0198     writeMasterPage();
0199 
0200     foreach( const XFigPage* page, m_Document->pages() )
0201         writePage( page );
0202 
0203     m_BodyWriter->endElement(); //office:drawing
0204     m_BodyWriter->endElement(); //office:body
0205     m_BodyWriter->endDocument();
0206 
0207     m_StyleCollector.saveOdfStyles( KoGenStyles::DocumentAutomaticStyles, contentWriter );
0208 
0209     m_OdfWriteStore.closeContentWriter();
0210 
0211     // TODO: mOdfWriteStore.closeContentWriter() should do that, or? also "text/xml" could be a static string
0212     m_ManifestWriter->addManifestEntry( QLatin1String("content.xml"), QLatin1String("text/xml") );
0213 }
0214 
0215 
0216 void
0217 XFigOdgWriter::writeMasterPage()
0218 {
0219     KoGenStyle masterPageStyle( KoGenStyle::MasterPageStyle );
0220 
0221     KoGenStyle masterPageLayoutStyle( KoGenStyle::PageLayoutStyle );
0222     masterPageLayoutStyle.setAutoStyleInStylesDotXml( true );
0223 
0224     if (m_Document->pageSizeType() != XFigPageSizeUnknown) {
0225         const PageSize& pageSize = pageSizeTable[m_Document->pageSizeType()-1];
0226 
0227         // defaults to portrait in case orientation is unknown
0228         const bool isLandscape = (m_Document->pageOrientation() == XFigPageLandscape);
0229         masterPageLayoutStyle.addProperty( QLatin1String("fo:page-width"),
0230                                            isLandscape ? pageSize.height : pageSize.width );
0231         masterPageLayoutStyle.addProperty( QLatin1String("fo:page-height"),
0232                                            isLandscape ? pageSize.width : pageSize.height );
0233     }
0234 
0235     const QString masterPageLayoutStyleName =
0236         m_StyleCollector.insert( masterPageLayoutStyle, QLatin1String("masterPageLayoutStyle") );
0237 
0238     masterPageStyle.addAttribute( QLatin1String("style:page-layout-name"), masterPageLayoutStyleName );
0239 
0240     KoGenStyle drawingMasterPageStyle( KoGenStyle::DrawingPageStyle, "drawing-page" );
0241     drawingMasterPageStyle.setAutoStyleInStylesDotXml( true );
0242 
0243     drawingMasterPageStyle.addProperty( QLatin1String("draw:fill"), "none" );
0244 
0245     const QString drawingMasterPageStyleName =
0246         m_StyleCollector.insert( drawingMasterPageStyle, QLatin1String("drawingMasterPageStyle") );
0247 
0248     masterPageStyle.addAttribute( QLatin1String("draw:style-name"), drawingMasterPageStyleName );
0249 
0250     m_MasterPageStyleName =
0251         m_StyleCollector.insert( masterPageStyle, QLatin1String("masterPageStyle") );
0252 }
0253 
0254 void
0255 XFigOdgWriter::writePage( const XFigPage* page )
0256 {
0257     m_BodyWriter->startElement( "draw:page" );
0258 
0259     m_BodyWriter->addAttribute( "xml:id", QLatin1String("page")+QString::number(m_PageCount++) );
0260     m_BodyWriter->addAttribute( "draw:master-page-name", m_MasterPageStyleName );
0261 
0262     // objects
0263     foreach( const XFigAbstractObject* object, page->objects() ) {
0264         writeObject( object );
0265     }
0266 
0267     m_BodyWriter->endElement(); //draw:page
0268 }
0269 
0270 void
0271 XFigOdgWriter::writeObject( const XFigAbstractObject* object )
0272 {
0273     const XFigAbstractObject::TypeId typeId = object->typeId();
0274 
0275     if (typeId == XFigAbstractObject::EllipseId)
0276         writeEllipseObject( static_cast<const XFigEllipseObject*>(object) );
0277     else if (typeId == XFigAbstractObject::PolylineId)
0278         writePolylineObject( static_cast<const XFigPolylineObject*>(object) );
0279     else if (typeId == XFigAbstractObject::PolygonId)
0280         writePolygonObject( static_cast<const XFigPolygonObject*>(object) );
0281     else if (typeId == XFigAbstractObject::BoxId)
0282         writeBoxObject( static_cast<const XFigBoxObject*>(object) );
0283     else if (typeId == XFigAbstractObject::PictureBoxId)
0284         writePictureBoxObject( static_cast<const XFigPictureBoxObject*>(object) );
0285     else if (typeId == XFigAbstractObject::SplineId)
0286         writeSplineObject( static_cast<const XFigSplineObject*>(object) );
0287     else if (typeId == XFigAbstractObject::ArcId)
0288         writeArcObject( static_cast<const XFigArcObject*>(object) );
0289     else if (typeId == XFigAbstractObject::TextId)
0290         writeTextObject( static_cast<const XFigTextObject*>(object) );
0291     else if( typeId == XFigAbstractObject::CompoundId )
0292         writeCompoundObject( static_cast<const XFigCompoundObject*>(object) );
0293 }
0294 
0295 void
0296 XFigOdgWriter::writeCompoundObject( const XFigCompoundObject* groupObject )
0297 {
0298 // for now compounds are not written as groups, as in OpenDocument groups
0299 // are kind of sublayers with an automatically assigned z-index
0300 // while in XFig objects from different compounds can be intertwined in the z-order
0301 // losing the grouping seems the less tragic solution over wrong order of objects
0302 // TODO: find if a compound really needs to be dissolved, or if it does not overlap with others
0303 //     mBodyWriter->startElement("draw:g");
0304 
0305 //     writeComment(groupObject);
0306 
0307     foreach( const XFigAbstractObject* object, groupObject->objects() ) {
0308         writeObject( object );
0309     }
0310 
0311 //     mBodyWriter->endElement(); //draw:g
0312 }
0313 
0314 void
0315 XFigOdgWriter::writeEllipseObject(const XFigEllipseObject* ellipseObject)
0316 {
0317     m_BodyWriter->startElement("draw:ellipse");
0318 
0319     writeZIndex(ellipseObject);
0320 
0321     const XFigPoint centerPoint = ellipseObject->centerPoint();
0322     m_BodyWriter->addAttribute("svg:cx", "0pt");
0323     m_BodyWriter->addAttribute("svg:cy", "0pt");
0324     m_BodyWriter->addAttributePt("svg:rx", odfLength(ellipseObject->xRadius()));
0325     m_BodyWriter->addAttributePt("svg:ry", odfLength(ellipseObject->yRadius()));
0326 
0327     const QString transformationString =
0328         QLatin1String("rotate(") + m_CLocale.toString(ellipseObject->xAxisAngle()) +
0329         QLatin1String(")translate(") +
0330         m_CLocale.toString(odfXCoord(centerPoint.x())) + QLatin1String("pt ") +
0331         m_CLocale.toString(odfYCoord(centerPoint.y())) + QLatin1String("pt)");
0332     m_BodyWriter->addAttribute("draw:transform", transformationString);
0333 
0334     KoGenStyle ellipseStyle(KoGenStyle::GraphicAutoStyle, "graphic");
0335     writeStroke(ellipseStyle, ellipseObject );
0336     writeFill(ellipseStyle, ellipseObject, ellipseObject->lineColorId());
0337     const QString ellipseStyleName = m_StyleCollector.insert(ellipseStyle, QLatin1String("ellipseStyle"));
0338     m_BodyWriter->addAttribute("draw:style-name", ellipseStyleName);
0339 
0340     writeComment(ellipseObject);
0341 
0342     m_BodyWriter->endElement(); // draw:ellipse
0343 }
0344 
0345 void
0346 XFigOdgWriter::writePolylineObject(const XFigPolylineObject* polylineObject)
0347 {
0348     m_BodyWriter->startElement("draw:polyline");
0349 
0350     writeZIndex(polylineObject);
0351 
0352     writePoints(polylineObject->points());
0353 
0354     KoGenStyle polylineStyle(KoGenStyle::GraphicAutoStyle, "graphic");
0355     writeStroke(polylineStyle, polylineObject);
0356     writeFill(polylineStyle, polylineObject, polylineObject->lineColorId());
0357     writeJoinType(polylineStyle, polylineObject->joinType());
0358     writeCapType(polylineStyle, polylineObject);
0359     writeArrow(polylineStyle, polylineObject->backwardArrow(), LineStart);
0360     writeArrow(polylineStyle, polylineObject->forwardArrow(), LineEnd);
0361     const QString polylineStyleName =
0362         m_StyleCollector.insert(polylineStyle, QLatin1String("polylineStyle"));
0363     m_BodyWriter->addAttribute("draw:style-name", polylineStyleName);
0364 
0365     writeComment(polylineObject);
0366 
0367     m_BodyWriter->endElement(); // draw:polyline
0368 }
0369 
0370 void
0371 XFigOdgWriter::writePolygonObject( const XFigPolygonObject* polygonObject )
0372 {
0373     m_BodyWriter->startElement("draw:polygon");
0374 
0375     writeZIndex(polygonObject);
0376 
0377     writePoints(polygonObject->points());
0378 
0379     KoGenStyle polygonStyle(KoGenStyle::GraphicAutoStyle, "graphic");
0380     writeStroke(polygonStyle, polygonObject);
0381     writeFill(polygonStyle, polygonObject, polygonObject->lineColorId());
0382     writeJoinType(polygonStyle, polygonObject->joinType());
0383     const QString polygonStyleName =
0384         m_StyleCollector.insert(polygonStyle, QLatin1String("polygonStyle"));
0385     m_BodyWriter->addAttribute("draw:style-name", polygonStyleName);
0386 
0387     writeComment(polygonObject);
0388 
0389     m_BodyWriter->endElement(); // draw:polygon
0390 }
0391 
0392 void
0393 XFigOdgWriter::writeBoxObject( const XFigBoxObject* boxObject )
0394 {
0395     m_BodyWriter->startElement("draw:rect");
0396 
0397     writeZIndex( boxObject );
0398 
0399     const XFigPoint upperleft = boxObject->upperLeft();
0400     m_BodyWriter->addAttributePt("svg:x", odfXCoord(upperleft.x()));
0401     m_BodyWriter->addAttributePt("svg:y", odfYCoord(upperleft.y()));
0402     m_BodyWriter->addAttributePt("svg:width", odfLength(boxObject->width()));
0403     m_BodyWriter->addAttributePt("svg:height", odfLength(boxObject->height()));
0404 
0405     const qint32 radius = boxObject->radius();
0406     if (radius != 0) {
0407         const double odfRadius = odfCornerRadius(radius);
0408         m_BodyWriter->addAttributePt("svg:rx", odfRadius);
0409         m_BodyWriter->addAttributePt("svg:ry", odfRadius);
0410     }
0411 
0412     {
0413         KoGenStyle boxStyle(KoGenStyle::GraphicAutoStyle, "graphic");
0414         writeStroke(boxStyle, boxObject);
0415         writeFill(boxStyle, boxObject, boxObject->lineColorId());
0416         writeJoinType(boxStyle, boxObject->joinType());
0417         const QString boxStyleName = m_StyleCollector.insert(boxStyle, QLatin1String("boxStyle"));
0418         m_BodyWriter->addAttribute("draw:style-name", boxStyleName);
0419     }
0420 
0421     writeComment(boxObject);
0422 
0423     m_BodyWriter->endElement(); // draw:rect
0424 }
0425 
0426 void
0427 XFigOdgWriter::writePictureBoxObject( const XFigPictureBoxObject* /*pictureBoxObject*/ )
0428 {
0429     // TODO
0430 }
0431 
0432 void
0433 XFigOdgWriter::writeSplineObject( const XFigSplineObject* /*object*/ )
0434 {
0435 }
0436 
0437 void XFigOdgWriter::writeArcObject( const XFigArcObject* arcObject )
0438 {
0439     const XFigPoint centerPoint = arcObject->centerPoint();
0440     const XFigPoint point1 = arcObject->point1();
0441     const XFigPoint point3 = arcObject->point3();
0442 
0443     const XFigCoord diffX1 = point1.x() - centerPoint.x();
0444     const XFigCoord diffY1 = point1.y() - centerPoint.y();
0445     const XFigCoord diffX3 = point3.x() - centerPoint.x();
0446     const XFigCoord diffY3 = point3.y() - centerPoint.y();
0447 
0448     double startAngle = -atan2( (qreal)diffY1, diffX1 ) * 180.0/M_PI;
0449     double endAngle   = -atan2( (qreal)diffY3, diffX3 ) * 180.0/M_PI;
0450     if (arcObject->direction() == XFigArcObject::Clockwise) {
0451         const double helper = startAngle;
0452         startAngle = endAngle;
0453         endAngle = helper;
0454     }
0455     const double radius = qSqrt((diffX1*diffX1) + (diffY1*diffY1));
0456 
0457     m_BodyWriter->startElement("draw:circle");
0458 
0459     writeZIndex( arcObject );
0460 
0461     m_BodyWriter->addAttributePt("svg:cx", odfXCoord(centerPoint.x()));
0462     m_BodyWriter->addAttributePt("svg:cy", odfXCoord(centerPoint.y()));
0463     m_BodyWriter->addAttributePt("svg:r", odfLength(radius));
0464     m_BodyWriter->addAttribute("draw:start-angle", startAngle);
0465     m_BodyWriter->addAttribute("draw:end-angle", endAngle);
0466 
0467     // TODO: cut in XFig has no line on the cut side, only on the curve
0468     const char* kind =
0469         (arcObject->subtype() == XFigArcObject::PieWedgeClosed) ? "section" :
0470         (arcObject->fillType() != XFigFillNone ) ?                "cut" :
0471                                                                   "arc";
0472     m_BodyWriter->addAttribute("draw:kind", kind);
0473 
0474     KoGenStyle arcStyle(KoGenStyle::GraphicAutoStyle, "graphic");
0475     writeStroke(arcStyle, arcObject);
0476     writeFill(arcStyle, arcObject, arcObject->lineColorId());
0477     writeCapType(arcStyle, arcObject);
0478     writeArrow(arcStyle, arcObject->backwardArrow(),
0479                (arcObject->direction() == XFigArcObject::Clockwise)?LineEnd:LineStart);
0480     writeArrow(arcStyle, arcObject->forwardArrow(),
0481                (arcObject->direction() == XFigArcObject::Clockwise)?LineStart:LineEnd);
0482     const QString arcStyleName = m_StyleCollector.insert(arcStyle, QLatin1String("arcStyle"));
0483     m_BodyWriter->addAttribute("draw:style-name", arcStyleName);
0484 
0485     writeComment(arcObject);
0486 
0487     m_BodyWriter->endElement(); // draw:circle
0488 }
0489 
0490 
0491 void
0492 XFigOdgWriter::writeTextObject( const XFigTextObject* textObject )
0493 {
0494     m_BodyWriter->startElement("draw:frame");
0495 
0496     writeZIndex(textObject);
0497 
0498     const double length = odfLength(textObject->length()) * 1.3; // 1.3 to adapt to wider fonts being used
0499     const double height = odfLength(textObject->height())*1.3;
0500 
0501     const XFigTextAlignment alignment = textObject->textAlignment();
0502     const XFigPoint point = textObject->baselineStartPoint();
0503     double xCoord = odfXCoord(point.x());
0504     if (alignment == XFigTextCenterAligned) {
0505         xCoord -= length * 0.5;
0506     } else if (alignment == XFigTextRightAligned) {
0507         xCoord -= length;
0508     }
0509     // given point is at baseline, with height the ascend of the font
0510     double yCoord = odfYCoord(point.y() - textObject->height());
0511 
0512     m_BodyWriter->addAttribute("svg:x", "0pt");
0513     m_BodyWriter->addAttribute("svg:y", "0pt");
0514     m_BodyWriter->addAttributePt("svg:width", length);
0515     m_BodyWriter->addAttributePt("svg:height", height);
0516     const QString transformationString =
0517         QLatin1String("rotate(") + m_CLocale.toString(textObject->xAxisAngle()) +
0518         QLatin1String(")translate(") +
0519         m_CLocale.toString(xCoord) + QLatin1String("pt ") +
0520         m_CLocale.toString(yCoord) + QLatin1String("pt)");
0521     m_BodyWriter->addAttribute("draw:transform", transformationString);
0522 
0523     KoGenStyle frameStyle( KoGenStyle::GraphicAutoStyle, "graphic" );
0524     frameStyle.addProperty( QLatin1String("style:overflow-behavior"), "clip" );
0525     const QString frameStyleName =
0526         m_StyleCollector.insert( frameStyle, QLatin1String("frameStyle") );
0527     m_BodyWriter->addAttribute( "draw:style-name", frameStyleName );
0528 
0529     m_BodyWriter->startElement("draw:text-box");
0530 
0531     m_BodyWriter->startElement( "text:p", false );  //false: we should not indent the inner tags
0532 
0533     KoGenStyle paragraphStyle( KoGenStyle::ParagraphAutoStyle, "paragraph" );
0534     writeParagraphStyle( paragraphStyle, textObject );
0535 
0536     const QString paragraphStyleName =
0537         m_StyleCollector.insert( paragraphStyle, QLatin1String("paragraphStyle") );
0538     m_BodyWriter->addAttribute( "text:style-name", paragraphStyleName );
0539 
0540     m_BodyWriter->startElement( "text:span" );
0541 
0542     KoGenStyle textSpanStyle( KoGenStyle::TextAutoStyle, "text" );
0543     writeFont( textSpanStyle, textObject );
0544 
0545     const QString textSpanStyleName =
0546         m_StyleCollector.insert( textSpanStyle, QLatin1String("textSpanStyle") );
0547     m_BodyWriter->addAttribute( "text:style-name", textSpanStyleName );
0548 
0549     m_BodyWriter->addTextNode( textObject->text() );
0550 
0551     m_BodyWriter->endElement(); //text:span
0552     m_BodyWriter->endElement(); //text:p
0553 
0554     m_BodyWriter->endElement();//draw:text-box
0555 
0556     writeComment(textObject);
0557 
0558     m_BodyWriter->endElement();//draw:frame
0559 }
0560 
0561 void
0562 XFigOdgWriter::writeZIndex( const XFigAbstractGraphObject* graphObject )
0563 {
0564     m_BodyWriter->addAttribute( "draw:z-index", (1000-graphObject->depth()) );
0565 }
0566 
0567 void
0568 XFigOdgWriter::writePoints( const QVector<XFigPoint>& points )
0569 {
0570     const XFigPoint& firstPoint = points.at(0);
0571 
0572     XFigCoord minX = firstPoint.x();
0573     XFigCoord minY = firstPoint.y();
0574     XFigCoord maxX = firstPoint.x();
0575     XFigCoord maxY = firstPoint.y();
0576 
0577     QString pointsString;
0578     int i = 0;
0579     while (true) {
0580         const XFigPoint& point = points.at(i);
0581         const XFigCoord x = point.x();
0582         const XFigCoord y = point.y();
0583 
0584         if( x < minX ) {
0585             minX = x;
0586         } else if( maxX < x ) {
0587             maxX = x;
0588         }
0589         if( y < minY ) {
0590             minY = y;
0591         } else if( maxY < y ) {
0592             maxY = y;
0593         }
0594 
0595         pointsString +=  m_CLocale.toString(x)+QLatin1Char(',')+m_CLocale.toString(y);
0596         ++i;
0597         if (i >= points.count()) {
0598             break;
0599         }
0600         pointsString += QLatin1Char(' ');
0601     }
0602     const XFigCoord width =  maxX - minX + 1;
0603     const XFigCoord height = maxY - minY + 1;
0604     const QString viewBoxString =
0605         QString::number(minX) + QLatin1Char(' ') + QString::number(minY) + QLatin1Char(' ') +
0606         QString::number(width) + QLatin1Char(' ') + QString::number(height);
0607 
0608     m_BodyWriter->addAttributePt("svg:x", odfXCoord(minX));
0609     m_BodyWriter->addAttributePt("svg:y", odfYCoord(minY));
0610     m_BodyWriter->addAttributePt("svg:width", odfLength(width) );
0611     m_BodyWriter->addAttributePt("svg:height", odfLength(height) );
0612     m_BodyWriter->addAttribute("svg:viewBox", viewBoxString);
0613     m_BodyWriter->addAttribute("draw:points", pointsString );
0614 }
0615 
0616 void XFigOdgWriter::writeComment(const XFigAbstractObject* object)
0617 {
0618     const QString& comment = object->comment();
0619     if (comment.isEmpty()) {
0620         return;
0621     }
0622 
0623     m_BodyWriter->startElement("svg:desc");
0624     m_BodyWriter->addTextNode(comment);
0625     m_BodyWriter->endElement(); // svg:desc
0626 }
0627 
0628 void
0629 XFigOdgWriter::writeFill(KoGenStyle& odfStyle, const XFigFillable* fillable, qint32 penColorId)
0630 {
0631     const XFigFillType fillType = fillable->fillType();
0632 
0633     const char* const fillString =
0634         (fillType == XFigFillSolid) ?   "solid" :
0635         (fillType == XFigFillPattern) ? "hatch" :
0636         /*(fillType == XFigFillNone)*/  "none";
0637     odfStyle.addProperty(QLatin1String("draw:fill"), fillString);
0638 
0639     if (fillType != XFigFillNone) {
0640         const qint32 fillColorId = fillable->fillColorId();
0641 
0642         QString colorString;
0643         if (fillType == XFigFillSolid) {
0644             // BLACK or DEFAULT color?
0645             if (fillColorId < 1) {
0646                 // 0: white, 20: black, 1..19 shades of grey, from lighter to darker
0647                 const int value = qRound((20 - fillable->fillTinting()) * 255.0 / 20.0);
0648                 colorString = QColor(value, value, value).name();
0649             // WHITE color ?
0650             } else if (fillColorId == 7) {
0651                 // 0: black, 20: white, 1..19 shades of grey, from darker to lighter
0652                 const int value = qRound(fillable->fillTinting() * 255.0 / 20.0);
0653                 colorString = QColor(value, value, value).name();
0654             } else {
0655                 //TODO: tint blackness/whiteness of color
0656                 const QColor* const color = m_Document->color(fillColorId);
0657                 if (color != 0) {
0658                     colorString = color->name();
0659                 }
0660             }
0661 
0662             odfStyle.addProperty(QLatin1String("draw:fill-color"), colorString);
0663         } else {
0664             // ODF 1.2 does not support a hatch pattern with a background
0665             // Options are to
0666             // * just do the hatch (not all XFig ones can be mapped)
0667             // * just do the background
0668             // * use pixmaps (needs pixmaps created with given colors)
0669             // Decision for now: just do the hatch
0670             const QColor* const color = m_Document->color(penColorId);
0671             if (color != 0) {
0672                 colorString = color->name();
0673             }
0674 
0675             writeHatch(odfStyle, fillable->fillPatternType(), colorString);
0676         }
0677     }
0678 }
0679 
0680 void
0681 XFigOdgWriter::writeDotDash( KoGenStyle& odfStyle, int lineType, double distance )
0682 {
0683     const double odfDistance = odfLineThickness(distance);
0684     odfStyle.addAttribute(QLatin1String("draw:style"), "rect");
0685     odfStyle.addAttribute(QLatin1String("draw:distance"),
0686                           m_CLocale.toString(odfDistance)+QLatin1String("pt"));
0687 
0688     const char* displayName = 0;
0689     bool isFirstDot = false;
0690     const char* secondDotsNumber = 0;
0691 
0692     switch (lineType) {
0693     case XFigLineDashed:
0694         displayName = "Dashed";
0695         break;
0696     case XFigLineDotted:
0697         displayName = "Dotted";
0698         isFirstDot = true;
0699         break;
0700     case XFigLineDashDotted:
0701         displayName = "1 Dot 1 Dash";
0702         secondDotsNumber = "1";
0703         break;
0704     case XFigLineDashDoubleDotted:
0705         displayName = "1 Dash 2 Dots";
0706         secondDotsNumber = "2";
0707         break;
0708     case XFigLineDashTrippleDotted:
0709         displayName = "1 Dash 3 Dots";
0710         secondDotsNumber = "3";
0711         break;
0712     }
0713 
0714     odfStyle.addAttribute(QLatin1String("draw:display-name"), displayName);
0715     odfStyle.addAttribute(QLatin1String("draw:dots1"), "1");
0716     odfStyle.addAttribute(QLatin1String("draw:dots1-length"),
0717                           isFirstDot ? QString::fromLatin1("100%") :
0718                                        m_CLocale.toString(odfDistance)+QLatin1String("pt"));
0719     if (secondDotsNumber!=0) {
0720         odfStyle.addAttribute(QLatin1String("draw:dots2"), QLatin1String(secondDotsNumber));
0721         odfStyle.addAttribute(QLatin1String("draw:dots2-length"), "100%");
0722     }
0723 }
0724 
0725 void
0726 XFigOdgWriter::writeStroke( KoGenStyle& odfStyle, const XFigLineable* lineable )
0727 {
0728     const qint32 colorId = lineable->lineColorId();
0729     if (colorId >= 0) {
0730         const QColor* color = m_Document->color(colorId);
0731         if (color != 0) {
0732             odfStyle.addProperty( QLatin1String("svg:stroke-color"), color->name() );
0733         }
0734     }
0735 
0736     odfStyle.addPropertyPt( QLatin1String("svg:stroke-width"), odfLineThickness(lineable->lineThickness()) );
0737 
0738     const XFigLineType lineType = lineable->lineType();
0739     const bool isDashed = (lineType != XFigLineSolid) && (lineType != XFigLineDefault);
0740 
0741     odfStyle.addProperty( QLatin1String("draw:stroke"), (isDashed) ? "dash" : "solid" );
0742 
0743     if (isDashed) {
0744         KoGenStyle dashStyle(KoGenStyle::StrokeDashStyle);
0745         writeDotDash(dashStyle, lineType, lineable->lineStyleValue());
0746         const QString dashStyleName = m_StyleCollector.insert( dashStyle, QLatin1String("dashStyle") );
0747 
0748         odfStyle.addProperty(QLatin1String("draw:stroke-dash"), dashStyleName);
0749     }
0750 }
0751 
0752 void
0753 XFigOdgWriter::writeJoinType(KoGenStyle& odfStyle, int joinType)
0754 {
0755     const char* const linejoin =
0756         (joinType == XFigJoinRound) ?  "round" :
0757         (joinType == XFigJoinBevel) ?  "bevel" :
0758                                        "miter";
0759     odfStyle.addProperty(QLatin1String("draw:stroke-linejoin"), linejoin);
0760 }
0761 
0762 void
0763 XFigOdgWriter::writeCapType(KoGenStyle& odfStyle, const XFigLineEndable* lineEndable)
0764 {
0765     const XFigCapType capType = lineEndable->capType();
0766 
0767     const char* const linecap =
0768         (capType == XFigCapRound) ?      "round" :
0769         (capType == XFigCapProjecting) ? "square" :
0770                                          "butt";
0771     odfStyle.addProperty(QLatin1String("svg:stroke-linecap"), linecap);
0772 }
0773 
0774 static const
0775 struct ArrowData
0776 {
0777     const char* displayName;
0778     const char* viewBox;
0779     const char* d;
0780 } arrowDataList[13] =
0781 {
0782     {   // 0
0783         "Arrowheads 7",//"Stick Arrow",
0784         "0 0 1122 2243",//"0 0 1 1",
0785         "m0 2108v17 17l12 42 30 34 38 21 43 4 29-8 30-21 25-26 13-34 343-1532 339 1520 13 42 29 34 39 21 42 4 42-12 34-30 21-42v-39-12l-4 4-440-1998-9-42-25-39-38-25-43-8-42 8-38 25-26 39-8 42z"//"m0,1 l0.5,-1 0.5,1"
0786     },
0787     {   // 1
0788         "Arrowheads 6",//"Triangle Arrow",
0789         "0 0 1131 902",//"0 0 1 1",
0790         "m564 0-564 902h1131z"//"m0,1 l0.5,-1 0.5,1 -1,0z"
0791     },
0792     {   // 2
0793         "Concave Spear Arrow",
0794         "0 0 1131 1580",//"0 0 1 1.25",
0795         "m1013 1491 118 89-567-1580-564 1580 114-85 136-68 148-46 161-17 161 13 153 46z"//"m0,1.25 l0.5,-1.25 0.5,1.25 -0.5,-0.25 -0.5,0.25z"
0796     },
0797     {   // 3
0798         "Convex Spear Arrow",
0799         "0 0 1 1",
0800         "m0,0.75 l0.5,-0.75 0.5,0.75 -0.5,0.25 -0.5,-0.25z"
0801     },
0802     {   // 4
0803         "Arrowheads 10",//"Diamond Arrow",
0804         "0 0 1131 1131",//"0 0 1 1",
0805         "m0 564 564 567 567-567-567-564z",//"m0,0.5 l0.5,-0.5 0.5,0.5 -0.5,0.5 -0.5,-0.5z"
0806     },
0807     {   // 5
0808         "Arrowheads 9",//"Circle Arrow",
0809         "0 0 1131 1131",//"0 0 1 1",
0810         "m462 1118-102-29-102-51-93-72-72-93-51-102-29-102-13-105 13-102 29-106 51-102 72-89 93-72 102-50 102-34 106-9 101 9 106 34 98 50 93 72 72 89 51 102 29 106 13 102-13 105-29 102-51 102-72 93-93 72-98 51-106 29-101 13z",//"m0,0.5 a0.5,0.5 0 1,1 0,0.5z"
0811     },
0812     {   // 6
0813         "Half Circle Arrow",
0814         "0 0 1 1",
0815         "a0.5,0.5 0 1,0 1,0z"
0816     },
0817     {   // 7
0818         "Arrowheads 1",//"Square Arrow",
0819         "0 0 10 10",
0820         "m0 0h10v10h-10z",
0821     },
0822     {   // 8
0823         "Reverse Triangle Arrow",
0824         "0 0 1 1",
0825         "l0.5,1 0.5,-1 -1,0z"
0826     },
0827     {   // 9
0828         "Wye Arrow",
0829         "0 0 1 1",
0830         "l0.5,1 0.5,-1" // TODO
0831     },
0832     {   // 10
0833         "Arrowheads 3",//"Bar Arrow",
0834         "0 0 836 110",
0835         "m0 0h278 278 280v36 36 38h-278-278-280v-36-36z",
0836     },
0837     {   // 11
0838         "Two Prong Fork Arrow",
0839         "0 0 1 1",
0840         "l0,1 1,0 0,-1" // TODO
0841     },
0842     {   // 12
0843         "Reverse Two Prong Fork Arrow",
0844         "0 0 1 1",
0845         "m0,1 l0,-1 1,0 0,1" // TODO
0846     }
0847 };
0848 
0849 // ODF seems to miss support for:
0850 // * non-centric arrows
0851 // * stroke-based arrows
0852 // * scaling by length, not just width
0853 // Non-centric arrows are for now simply mapped to the similar centric ones.
0854 // TODO: calculate hollow/stroke arrow data; scale all with arrow->length()
0855 static const int arrowDataMap[XFigArrowHeadTypeCount] =
0856 {
0857     0, // XFigArrowHeadStick
0858     1, //XFigArrowHeadHollowTriangle
0859     1, //XFigArrowHeadFilledTriangle
0860     2, //XFigArrowHeadHollowConcaveSpear
0861     2, //XFigArrowHeadFilledConcaveSpear
0862     3, //XFigArrowHeadHollowConvexSpear
0863     3, //XFigArrowHeadFilledConvexSpear
0864     4, //XFigArrowHeadHollowDiamond
0865     4, //XFigArrowHeadFilledDiamond
0866     5, //XFigArrowHeadHollowCircle
0867     5, //XFigArrowHeadFilledCircle
0868     6, //XFigArrowHeadHollowHalfCircle
0869     6, //XFigArrowHeadFilledHalfCircle
0870     7, //XFigArrowHeadHollowSquare
0871     7, //XFigArrowHeadFilledSquare
0872     8, //XFigArrowHeadHollowReverseTriangle
0873     8, //XFigArrowHeadFilledReverseTriangle
0874     2, //XFigArrowHeadTopHalfFilledConcaveSpear
0875     2, //XFigArrowHeadBottomHalfFilledConcaveSpear
0876     1, //XFigArrowHeadHollowTopHalfTriangle
0877     1, //XFigArrowHeadFilledTopHalfTriangle
0878     2, //XFigArrowHeadHollowTopHalfConcaveSpear
0879     2, //XFigArrowHeadFilledTopHalfConcaveSpear
0880     3, //XFigArrowHeadHollowTopHalfConvexSpear
0881     3, //XFigArrowHeadFilledTopHalfConvexSpear
0882     8, //XFigArrowHeadWye
0883     10, //XFigArrowHeadBar
0884     1, //XFigArrowHeadTwoProngFork
0885     1 //XFigArrowHeadReverseTwoProngFork
0886 };
0887 
0888 void XFigOdgWriter::writeArrow(KoGenStyle& odfStyle, const XFigArrowHead* arrow, LineEndType lineEndType)
0889 {
0890     if (arrow == 0) {
0891         return;
0892     }
0893 
0894     KoGenStyle arrowStyle(KoGenStyle::MarkerStyle);
0895     const ArrowData& arrowData = arrowDataList[arrowDataMap[arrow->type()]];
0896     arrowStyle.addAttribute(QLatin1String("draw:display-name"), arrowData.displayName);
0897     arrowStyle.addAttribute(QLatin1String("svg:viewBox"), arrowData.viewBox);
0898     arrowStyle.addAttribute(QLatin1String("svg:d"), arrowData.d);
0899     const QString arrowStyleName =
0900         m_StyleCollector.insert(arrowStyle, QLatin1String("arrowStyle"));
0901 
0902     const char* const marker =
0903         (lineEndType==LineStart) ? "draw:marker-start" : "draw:marker-end";
0904     const char* const markerWidth =
0905         (lineEndType==LineStart) ? "draw:marker-start-width" : "draw:marker-end-width";
0906     const char* const markerCenter =
0907         (lineEndType==LineStart) ? "draw:marker-start-center" : "draw:marker-end-center";
0908     odfStyle.addProperty(QLatin1String(marker), arrowStyleName);
0909     odfStyle.addPropertyPt(QLatin1String(markerWidth), odfLength(arrow->width()));
0910     odfStyle.addProperty(QLatin1String(markerCenter), "1.0");
0911 }
0912 
0913 void XFigOdgWriter::writeHatch(KoGenStyle& odfStyle, int patternType, const QString& colorString)
0914 {
0915     KoGenStyle hatchStyle(KoGenStyle::HatchStyle);
0916 
0917     const char* displayName = 0;
0918     const char* style = 0;
0919     const char* distance = 0;
0920     const char* rotation = 0;
0921 
0922     // Shingles, bricks, tire treads cannot be done, are mapped to horizontal/vertical lines
0923     // Fish, circles, hexagons, octagons cannot be done, are mapped to cross hatchess
0924     // 30 degree crosshatch cannot be done, mapped to 45 degree one
0925     switch (patternType) {
0926     default:
0927     case XFigFillVerticalShinglesSkewedDown:
0928     case XFigFillVerticalShinglesSkewedUp:
0929     case XFigFillVerticalTireTreads:
0930     case XFigFillVerticalBricks:
0931     case XFigFillVerticalLines:
0932         displayName = " Vertical";
0933         style = "single";
0934         distance = "0.102cm";
0935         rotation = "900";
0936         break;
0937     case XFigFillHorizontalShinglesSkewedRight:
0938     case XFigFillHorizontalShinglesSkewedLeft:
0939     case XFigFillHorizontalTireTreads:
0940     case XFigFillHorizontalBricks:
0941     case XFigFillHorizontalLines:
0942         displayName = " Horizontal";
0943         style = "single";
0944         distance = "0.102cm";
0945         rotation = "0";
0946         break;
0947     case XFigFillRightDiagonal30Degree:
0948         displayName = " 30 Degrees";
0949         style = "single";
0950         distance = "0.102cm";
0951         rotation = "300";
0952         break;
0953     case XFigFillLeftDiagonal30Degree:
0954         displayName = " -30 Degrees";
0955         style = "single";
0956         distance = "0.102cm";
0957         rotation = "3300";
0958         break;
0959     case XFigFillRightDiagonal45Degree:
0960         displayName =  " 45 Degrees";
0961         style = "single";
0962         distance = "0.102cm";
0963         rotation = "450";
0964         break;
0965     case XFigFillLeftDiagonal45Degree:
0966         displayName = " -45 Degrees";
0967         style = "single";
0968         distance = "0.102cm";
0969         rotation = "3150";
0970         break;
0971     case XFigFillFishScales:
0972     case XFigFillSmallFishScales:
0973     case XFigFillCircles:
0974     case XFigFillHexagons:
0975     case XFigFillOctagons:
0976     case XFigFillCrossHatch:
0977         displayName = " Crossed 0 Degrees";
0978         style = "double";
0979         distance = "0.102cm";
0980         rotation = "900";
0981         break;
0982     case XFigFillCrossHatch30Degree:
0983     case XFigFillCrossHatch45Degree:
0984         displayName = " Crossed 45 Degrees";
0985         style = "double";
0986         distance = "0.102cm";
0987         rotation = "450";
0988         break;
0989     }
0990 
0991     hatchStyle.addAttribute("draw:display-name", colorString+QLatin1String(displayName));
0992     hatchStyle.addAttribute("draw:style", style);
0993     hatchStyle.addAttribute("draw:color", colorString);
0994     hatchStyle.addAttribute("draw:distance", distance);
0995     hatchStyle.addAttribute("draw:rotation", rotation);
0996     const QString hatchStyleName =
0997         m_StyleCollector.insert(hatchStyle, QLatin1String("hatchStyle"));
0998 
0999     odfStyle.addProperty("draw:fill-hatch-name", hatchStyleName);
1000 }
1001 
1002 void
1003 XFigOdgWriter::writeFont( KoGenStyle& odfStyle, const XFigTextObject* textObject )
1004 {
1005     const XFigFontData& fontData = textObject->fontData();
1006 
1007     odfStyle.addPropertyPt(QLatin1String("fo:font-size"), fontData.mSize);
1008     const char* const weight =
1009         (fontData.mWeight == QFont::Bold) ?     "bold" :
1010         (fontData.mWeight == QFont::DemiBold) ? "600" :
1011                                                 "normal";
1012     odfStyle.addProperty(QLatin1String("fo:font-weight"), weight);
1013     const char* const style =
1014         (fontData.mStyle == QFont::StyleItalic) ?  "italic" :
1015         (fontData.mStyle == QFont::StyleOblique) ? "oblique" :
1016                                                    "normal";
1017     odfStyle.addProperty(QLatin1String("fo:font-style"), style);
1018     if (! fontData.mFamily.isEmpty())
1019         odfStyle.addProperty( QLatin1String("fo:font-family"), fontData.mFamily );
1020 }
1021 
1022 
1023 void
1024 XFigOdgWriter::writeParagraphStyle( KoGenStyle& odfStyle, const XFigTextObject* textObject )
1025 {
1026     const XFigTextAlignment textAlignment = textObject->textAlignment();
1027     const char* const alignmentName =
1028         (textAlignment == XFigTextCenterAligned) ? "center" :
1029         (textAlignment == XFigTextRightAligned) ?  "right" :
1030         /* XFigTextLeftAligned */                  "left";
1031     odfStyle.addProperty( QLatin1String("fo:text-align"), QLatin1String(alignmentName) );
1032 
1033     odfStyle.addProperty(QLatin1String("fo:margin"), "0pt");
1034     odfStyle.addProperty(QLatin1String("fo:padding"), "0pt");
1035 }