Exporting shapes to SVG files

Scalable Vector Graphics (SVG) is an XML-based language for describing two-dimensional vector graphics. Qt provides classes for saving vector shapes into an SVG file. This feature can be used to create a simple vector graphics editor similar to Adobe Illustrator and Inkscape.

In the next example, we will continue using the same project file from the previous example.

How to do it…

Let's learn how to create a simple program that displays SVG graphics on screen:

  1. First of all, let's create a menu bar by right-clicking the main window widget on the hierarchy window and selecting Create Menu Bar option from the pop-up menu. After that, add a File option to the menu bar and a Save as SVG action underneath it:
    How to do it…
  2. After that, you will see an item called actionSave_as_SVG in the Action Editor window at the bottom of the Qt Creator window. Right-click on the item and choose Go to slot… from the pop-up menu. A window will now appear, which carries a list of slots available for the particular action. Choose the default signal called triggered() and click the OK button:
    How to do it…
  3. Once you have clicked the OK button, Qt Creator will switch over to the script editor. You will realize that a slot called on_actionSave_as_SVG_triggered() has been automatically added to your main window class. At the bottom of your mainwindow.h, you will see something like this:
    void MainWindow::on_actionSave_as_SVG_triggered()
    {
    }

    The preceding function will be called when you clicked on the Save as SVG option from the menu bar. We will write our code within this function to save all the vector graphics into an SVG file.

  4. To do that, we need to first of all include a class header called QSvgGenerator at the top of our source file. This header is very important as it's required for generating SVG files. Then, we also need to include another class header called QFileDialog, which will be used to open the save dialog:
    #include <QtSvg/QSvgGenerator>
    #include <QFileDialog>
  5. We also need to add the SVG module to our project file, like so:
    QT += core gui svg
  6. Then, create a new function called paintAll() within mainwindow.h, like so:
    public:
      explicit MainWindow(QWidget *parent = 0);
      ~MainWindow();
    
      virtual void paintEvent(QPaintEvent *event);
      void paintAll(QSvgGenerator *generator = 0);
  7. After that, in mainwindow.cpp, move all the code from paintEvent() to the paintAll() function. Then, replace all the individual QPainter objects with a single, unified QPainter for drawing all the graphics. Also, call the begin() function before drawing anything and call the end() function after finishing drawing. The code should look like this:
    void MainWindow::paintAll(QSvgGenerator *generator)
    {
      QPainter painter;
    
      if (engine)
        painter.begin(engine);
      else
        painter.begin(this);
    
      painter.setFont(QFont("Times", 14, QFont::Bold));
      painter.drawText(QPoint(20, 30), "Testing");
    
      painter.drawLine(QPoint(50, 60), QPoint(100, 100));
    
      painter.setBrush(Qt::BDiagPattern);
      painter.drawRect(QRect(40, 120, 80, 30));
    
      QPen ellipsePen;
      ellipsePen.setColor(Qt::red);
      ellipsePen.setStyle(Qt::DashDotLine);
    
      painter.setPen(ellipsePen);
      painter.drawEllipse(QPoint(80, 200), 50, 20);
    
      QPainterPath rectPath;
      rectPath.addRect(QRect(150, 20, 100, 50));
    
      painter.setPen(QPen(Qt::red, 1, Qt::DashDotLine, Qt::FlatCap, Qt::MiterJoin));
      painter.setBrush(Qt::yellow);
      painter.drawPath(rectPath);
    
      QPainterPath ellipsePath;
      ellipsePath.addEllipse(QPoint(200, 120), 50, 20);
    
      painter.setPen(QPen(QColor(79, 106, 25), 5, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin));
      painter.setBrush(QColor(122, 163, 39));
      painter.drawPath(ellipsePath);
    
      QImage image;
      image.load("tux.png");
    
      painter.drawImage(QPoint(100, 150), image);
    
      painter.end();
    }
  8. Since we have moved all the code from paintEvent() to paintAll(), we shall now call the paintAll() function inside paintEvent(), like so:
    void MainWindow::paintEvent(QPaintEvent *event)
    {
      paintAll();
    }
  9. Then, we will write the code for exporting the graphics to an SVG file. The code will be written inside the slot function called on_actionSave_as_SVG_triggered(), which was generated by Qt. We start by calling the save file dialog and obtain the directory path with the desired file name from the user:
    void MainWindow::on_actionSave_as_SVG_triggered()
    {
      QString filePath = QFileDialog::getSaveFileName(this, "Save SVG", "", "SVG files (*.svg)");
    
      if (filePath == "")
        return;
    }
  10. After that, create a QSvgGenerator object and save the graphics to an SVG file by passing the QSvgGenerator object to the paintAll() function:
    void MainWindow::on_actionSave_as_SVG_triggered()
    {
      QString filePath = QFileDialog::getSaveFileName(this, "Save SVG", "", "SVG files (*.svg)");
    
      if (filePath == "")
        return;
    
      QSvgGenerator generator;
      generator.setFileName(filePath);
      generator.setSize(QSize(this->width(), this->height()));
      generator.setViewBox(QRect(0, 0, this->width(), this->height()));
      generator.setTitle("SVG Example");
      generator.setDescription("This SVG file is generated by Qt.");
    
      paintAll(&generator);
    }
  11. Compile and run the program now and you should be able to export the graphics by going to File | Save as SVG:
    How to do it…

How it works...

By default, QPainter will use the paint engine from its parent object to draw the graphics assigned to it. If you don't assign any parent to QPainter, you can manually assign a paint engine to it, which is what we have done in this example.

The reason why we placed the code into paintAll() is because we want to reuse the same code for two different purposes: for displaying the graphics on the window and exporting the graphics to an SVG file. Notice the default value of the generator variable in the paintAll() function is set to 0, which means no QSvgGenerator object is required to run the function unless specified. Later on, in the paintAll() function, we check whether the generator object exists. If it does exist, use it as the paint engine for the painter, like so:

if (engine)
  painter.begin(engine);
else
  painter.begin(this);

Otherwise, pass the main window to the begin() function (since we're writing the code in mainwindow.cpp, we can directly use this to refer to main window's pointer) so that it will use the paint engine of the main window itself, which means the graphics will be drawn onto the surface of the main window.

In this example, it's required to use a single QPainter object to save the graphics into the SVG file. If you use multiple QPainter objects, the resulting SVG file will contain multiple XML header definitions and thus the file will be deemed to be invalid by any graphics editor software out there.

QFileDialog::getSaveFileName() will open up the native save file dialog for the user to choose the save directory and set a desired file name. Once the user is done with that, the full path will be returned as a string and we will be able to pass that information to the QSvgGenerator object to export the graphics.

Notice that in the previous screenshot, the penguin in the SVG file has been cropped. This is because the canvas size of the SVG was set to follow the size of the main window. To help the poor penguin getting its body back, scale the window bigger before exporting the SVG file.

There's more…

Scalable Vector Graphics (SVG) defines the graphics in XML format. Since it is vector graphics, SVG graphics do not lose any quality if they are zoomed or resized.

SVG allows three types of graphic object: vector graphics, raster graphics, and text. Graphical objects, including PNG and JPEG raster images, can be grouped, styled, transformed, and composited into previously rendered objects.

You can check out the full specification of SVG graphics at https://www.w3.org/TR/SVG.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.17.18