Solved

Outputting SVG as PDF with Qt (QSvgRenderer, QPainter, QPrinter)

Posted on 2014-10-17
14
695 Views
Last Modified: 2014-10-28
I am attempting to use QSvgRenderer, QPainter, and QPrinter to output SVG as PDF.

I was expecting that the SVG would be placed on the paper and potentially be cut off if too large.
However, it seems that QPainter is just stretching the SVG image to fill the page size, destroying its aspect ratio.
QSvgRenderer renderer( data.toUtf8() );

QPrinter printer;
printer.setOutputFileName( filePathWithExtension );		
printer.setOutputFormat( QPrinter::PdfFormat );		
printer.setPaperSize( QPrinter::Letter );
printer.setOrientation( QPrinter::Landscape );

QPainter painter( &printer );

renderer.render( &painter );

painter.end();

Open in new window


I was hoping to simply scale my SVG image to fit within the dimensions of the selected paper size.
But the scaled output is not as expected.
QSvgRenderer renderer( data.toUtf8() );

QPrinter printer;
printer.setOutputFileName( filePathWithExtension );		
printer.setOutputFormat( QPrinter::PdfFormat );		
printer.setPaperSize( QPrinter::Letter );
printer.setOrientation( QPrinter::Landscape );

double paperWidth = 11.0 * static_cast< double >( printer.resolution() );
double paperHeight = 8.5 * static_cast< double >( printer.resolution() );

double widthScale = paperWidth / static_cast< double >( width );
double heightScale = paperHeight / static_cast< double >( height );
double minScale = qMin( widthScale, heightScale );
		
QPainter painter( &printer );
painter.scale( minScale, minScale );

renderer.render( &painter );

painter.end();

Open in new window


I was easily able to output SVG as PNG by sizing the QImage to match the SVG size.
However, PDF output is a more complicated due to QPrinter being restricted to a paper size.

Any help is appreciated.
no-scale.PNG
with-scale.PNG
svg.PNG
0
Comment
Question by:bejhan
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 9
  • 5
14 Comments
 
LVL 34

Expert Comment

by:sarabande
ID: 40393850
I was expecting that the SVG would be placed on the paper and potentially be cut off if too large.
how should the software know where to cut your image?

see http://www.digicamguides.com/print/aspect-ratio.html for the basic principles that need to be considered for a different aspect ratio of input and output.

most paint programs provide a manual "crop" tool, but I failed into finding any automatic tool.

Sara
0
 
LVL 1

Author Comment

by:bejhan
ID: 40394874
how should the software know where to cut your image?
The software shouldn't know where to crop the image, but I would expect that it would crop arbitrarily rather than stretching the image to fit the paper.

Regardless, my question is not about cropping.
I need to know how to resize the image such that it can fit on the paper, while constraining the proportions of the original image, and leaving the unused space blank, rather than stretching the image to fit.
0
 
LVL 34

Expert Comment

by:sarabande
ID: 40396534
I found the following about scaling and coordinate transformation:

Normally, the QPainter operates on the associated device's own coordinate system, but it also has good support for coordinate transformations.

The default coordinate system of a paint device has its origin at the top-left corner. The x values increase to the right and the y values increase downwards. You can scale the coordinate system by a given offset using the QPainter::scale() function, you can rotate it clockwise using the QPainter::rotate() function and you can translate it (i.e. adding a given offset to the points) using the QPainter::translate() function. You can also twist the coordinate system around the origin (called shearing) using the QPainter::shear() function.

you find the statement in http://www.bogotobogo.com/Qt/Qt5_QPainter_Transformation.php.

from that I would say that the scale call you made is not suitable to perform the transformation you intended, instead you may calculate the dimensions of the new image including the right or bottom space which is caused by the difference of the aspect ratio. then create a new image with space block and use that as input for rendering.

Sara
0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 1

Author Comment

by:bejhan
ID: 40400844
instead you may calculate the dimensions of the new image including the right or bottom space which is caused by the difference of the aspect ratio. then create a new image with space block and use that as input for rendering.

I believe the calculations are as follows:
double paperWidth = 11.0 * static_cast< double >( printer.resolution() );
double paperHeight = 8.5 * static_cast< double >( printer.resolution() );

double widthScale = paperWidth / static_cast< double >( width );
double heightScale = paperHeight / static_cast< double >( height );
double minScale = qMin( widthScale, heightScale );
	
double scaledWidth = static_cast< double >( width ) * minScale;
double scaledHeight = static_cast< double >( height ) * minScale;

double blankWidth = scaledWidth - static_cast< double >( width );
double blankHeight = scaledHeight - static_cast< double >( width );

Open in new window


I am unsure of the API to actually render to an image of this scaled size as well as how to then apply that image to the QPrinter. Do you know of the classes I can use to do this work?
0
 
LVL 34

Accepted Solution

by:
sarabande earned 500 total points
ID: 40401811
i didn't use SVG format myself and it is some time ago, when i lastly made qt programming.

you should try to create a copy of the SVG QImage but either the width or height increased such that the aspect ratio of the new image is the same as with the pdf print page. the points of the blank part should be made either white or transaparent.

if you achieved that, you should use the new image as input for the pdf with no scaling.

i will try to find some code how to do this.

Sara
0
 
LVL 1

Author Comment

by:bejhan
ID: 40403324
QImage has a scaled() method which allows specifying to keep the aspect ratio of the original image, i.e. the image will be scaled and unused space will be blank.

I attempted to scale the image to the paper size.
QPrinter printer;
printer.setOutputFileName( filePathWithExtension );		
printer.setOutputFormat( QPrinter::PdfFormat );		
printer.setPaperSize( QPrinter::Letter );
printer.setOrientation( QPrinter::Landscape );

QSvgRenderer renderer( data.toUtf8() );
QImage image( width, height, QImage::Format_ARGB32_Premultiplied );
QPainter painter( &image );

renderer.render( &painter );
image.setText( "Description", description );

double paperWidth = 11.0 * static_cast< double >( printer.resolution() );
double paperHeight = 8.5 * static_cast< double >( printer.resolution() );

QImage scaledImage = image.scaled( paperWidth, paperHeight, Qt::KeepAspectRatio );

Open in new window


However, the scaled image looks very grainy.
original.png
scaled.png
0
 
LVL 34

Expert Comment

by:sarabande
ID: 40406265
However, the scaled image looks very grainy.
I would assume that is because the scaled() doesn't "add" the space part to the image (as I suggested) but let one side constant what reduces the resolution for the other direction.

so, I still think that if you simply create an image with the wished aspect ratio which could be covered by the original image and then copy the pixel data from one to the other without scaling, you will have a better result.

Sara
0
 
LVL 1

Author Comment

by:bejhan
ID: 40406969
Actually it seems that QImage::scaled() has a Qt::TransformationMode transformMode parameter, which defaults to Qt::FastTransformation.

Changing this to Qt::SmoothTransformation removes the grainy look.
0
 
LVL 1

Author Comment

by:bejhan
ID: 40406980
My finally solution is as follows:
QSvgRenderer renderer( data.toUtf8() );
QImage image( width, height, QImage::Format_ARGB32_Premultiplied );
QPainter svgPainter( &image );

renderer.render( &svgPainter );

QPrinter printer;
printer.setOutputFileName( filePathWithExtension );		
printer.setOutputFormat( QPrinter::PdfFormat );		
printer.setPaperSize( QPrinter::Letter );
printer.setOrientation( QPrinter::Landscape );		
printer.setFullPage( true );

double paperWidth = 11.0 * static_cast< double >( printer.resolution() );
double paperHeight = 8.5 * static_cast< double >( printer.resolution() );

QImage scaledImage = image.scaled( paperWidth, paperHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation );
QPainter pdfPainter( &printer );
pdfPainter.drawImage( 0, 0, scaledImage );
pdfPainter.end();

Open in new window

0
 
LVL 1

Author Closing Comment

by:bejhan
ID: 40406982
You pointed in my the right direction but did not provide any API or code examples.
For this reason, I am only awarding you B.
0
 
LVL 1

Author Comment

by:bejhan
ID: 40407029
Note, the above solution generates a cloudy PDF, change
QPrinter printer;

Open in new window

to
QPrinter printer( QPrinter::HighResolution );

Open in new window

to correct this issue.
0
 
LVL 34

Expert Comment

by:sarabande
ID: 40407047
that is quite ok. I did something with qt but no graphics. so the main work to find a solution was on your side.

I meddled in the question not because I knew so much better but because of the scale statements of your original code which look wrong.  

Sara
0
 
LVL 1

Author Comment

by:bejhan
ID: 40409467
For anyone reading this in the future, the scaling dimensions can be determined using QPrinter instead of constants, giving more reliable results.

Change the following:
double paperWidth = 11.0 * static_cast< double >( printer.resolution() );
double paperHeight = 8.5 * static_cast< double >( printer.resolution() );

QImage scaledImage = image.scaled( paperWidth, paperHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation );

Open in new window


To the following:
QSizeF paperSize = printer.paperSize( QPrinter::DevicePixel );

QImage scaledImage = image.scaled( paperSize.width(), paperSize.height(), Qt::KeepAspectRatio, Qt::SmoothTransformation );

Open in new window

0
 
LVL 1

Author Comment

by:bejhan
ID: 40409482
Note, instead of using high resolution, which results in a huge file size
QPrinter printer( QPrinter::HighResolution );

Open in new window

set a resolution of your choosing
QPrinter printer;
printer.setResolution( 300 ); //dpi

Open in new window

0

Featured Post

Free Tool: ZipGrep

ZipGrep is a utility that can list and search zip (.war, .ear, .jar, etc) archives for text patterns, without the need to extract the archive's contents.

One of a set of tools we're offering as a way to say thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
Losing latter half of command line in Visual Studio C++ online program 10 97
How do i run a c++ file? 15 58
Finding Divisors 5 61
Need some help with design 17 48
Errors will happen. It is a fact of life for the programmer. How and when errors are detected have a great impact on quality and cost of a product. It is better to detect errors at compile time, when possible and practical. Errors that make their wa…
Container Orchestration platforms empower organizations to scale their apps at an exceptional rate. This is the reason numerous innovation-driven companies are moving apps to an appropriated datacenter wide platform that empowers them to scale at a …
The goal of the tutorial is to teach the user how to use functions in C++. The video will cover how to define functions, how to call functions and how to create functions prototypes. Microsoft Visual C++ 2010 Express will be used as a text editor an…
The goal of the video will be to teach the user the concept of local variables and scope. An example of a locally defined variable will be given as well as an explanation of what scope is in C++. The local variable and concept of scope will be relat…

752 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question