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 }