Skip to content

Commit

Permalink
Add unit test for pdf exports
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Dec 18, 2017
1 parent b4829a7 commit 0052eb8
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 1 deletion.
10 changes: 10 additions & 0 deletions python/core/qgsmultirenderchecker.sip
Expand Up @@ -84,6 +84,16 @@ Default value is 0.

:param colorTolerance: The maximum difference for each color component
including alpha to be considered correct.
%End

void setSizeTolerance( int xTolerance, int yTolerance );
%Docstring
Sets the largest allowable difference in size between the rendered and the expected image.

:param xTolerance: x tolerance in pixels
:param yTolerance: y tolerance in pixels

.. versionadded:: 3.0
%End

bool runTest( const QString &testName, unsigned int mismatchCount = 0 );
Expand Down
1 change: 1 addition & 0 deletions src/core/qgsmultirenderchecker.cpp
Expand Up @@ -55,6 +55,7 @@ bool QgsMultiRenderChecker::runTest( const QString &testName, unsigned int misma
QgsRenderChecker checker;
checker.enableDashBuffering( true );
checker.setColorTolerance( mColorTolerance );
checker.setSizeTolerance( mMaxSizeDifferenceX, mMaxSizeDifferenceY );
checker.setControlPathPrefix( mControlPathPrefix );
checker.setControlPathSuffix( suffix );
checker.setControlName( mControlName );
Expand Down
10 changes: 10 additions & 0 deletions src/core/qgsmultirenderchecker.h
Expand Up @@ -94,6 +94,14 @@ class CORE_EXPORT QgsMultiRenderChecker
*/
void setColorTolerance( unsigned int colorTolerance ) { mColorTolerance = colorTolerance; }

/**
* Sets the largest allowable difference in size between the rendered and the expected image.
* \param xTolerance x tolerance in pixels
* \param yTolerance y tolerance in pixels
* \since QGIS 3.0
*/
void setSizeTolerance( int xTolerance, int yTolerance ) { mMaxSizeDifferenceX = xTolerance; mMaxSizeDifferenceY = yTolerance; }

/**
* Test using renderer to generate the image to be compared.
*
Expand Down Expand Up @@ -134,6 +142,8 @@ class CORE_EXPORT QgsMultiRenderChecker
QString mControlName;
QString mControlPathPrefix;
unsigned int mColorTolerance = 0;
int mMaxSizeDifferenceX = 0;
int mMaxSizeDifferenceY = 0;
QgsMapSettings mMapSettings;
};

Expand Down
107 changes: 106 additions & 1 deletion tests/src/python/test_qgslayoutexporter.py
Expand Up @@ -17,6 +17,7 @@
import tempfile
import shutil
import os
import subprocess

from qgis.core import (QgsMultiRenderChecker,
QgsLayoutExporter,
Expand All @@ -35,6 +36,58 @@

from qgis.testing import start_app, unittest

from utilities import getExecutablePath

# PDF-to-image utility
# look for Poppler w/ Cairo, then muPDF
# * Poppler w/ Cairo renders correctly
# * Poppler w/o Cairo does not always correctly render vectors in PDF to image
# * muPDF renders correctly, but sightly shifts colors
for util in [
'pdftocairo',
# 'mudraw',
]:
PDFUTIL = getExecutablePath(util)
if PDFUTIL:
break

# noinspection PyUnboundLocalVariable
if not PDFUTIL:
raise Exception('PDF-to-image utility not found on PATH: '
'install Poppler (with Cairo)')


def pdfToPng(pdf_file_path, rendered_file_path, page, dpi=96):
if PDFUTIL.strip().endswith('pdftocairo'):
filebase = os.path.join(
os.path.dirname(rendered_file_path),
os.path.splitext(os.path.basename(rendered_file_path))[0]
)
call = [
PDFUTIL, '-png', '-singlefile', '-r', str(dpi),
'-x', '0', '-y', '0', '-f', str(page), '-l', str(page),
pdf_file_path, filebase
]
elif PDFUTIL.strip().endswith('mudraw'):
call = [
PDFUTIL, '-c', 'rgba',
'-r', str(dpi), '-f', str(page), '-l', str(page),
# '-b', '8',
'-o', rendered_file_path, pdf_file_path
]
else:
return False, ''

print("exportToPdf call: {0}".format(' '.join(call)))
try:
subprocess.check_call(call)
except subprocess.CalledProcessError as e:
assert False, ("exportToPdf failed!\n"
"cmd: {0}\n"
"returncode: {1}\n"
"message: {2}".format(e.cmd, e.returncode, e.message))


start_app()


Expand All @@ -54,12 +107,13 @@ def tearDown(self):
with open(report_file_path, 'a') as report_file:
report_file.write(self.report)

def checkImage(self, name, reference_image, rendered_image):
def checkImage(self, name, reference_image, rendered_image, size_tolerance=0):
checker = QgsMultiRenderChecker()
checker.setControlPathPrefix("layout_exporter")
checker.setControlName("expected_layoutexporter_" + reference_image)
checker.setRenderedImage(rendered_image)
checker.setColorTolerance(2)
checker.setSizeTolerance(size_tolerance, size_tolerance)
result = checker.runTest(name, 20)
self.report += checker.report()
print((self.report))
Expand Down Expand Up @@ -278,6 +332,57 @@ def testExportToImage(self):
page2_path = os.path.join(self.basetestpath, 'test_exporttoimagesize_2.png')
self.assertTrue(self.checkImage('exporttoimagesize_page2', 'exporttoimagesize_page2', page2_path))

def testExportToPdf(self):
l = QgsLayout(QgsProject.instance())
l.initializeDefaults()

# add a second page
page2 = QgsLayoutItemPage(l)
page2.setPageSize('A5')
l.pageCollection().addPage(page2)

# add some items
item1 = QgsLayoutItemShape(l)
item1.attemptSetSceneRect(QRectF(10, 20, 100, 150))
fill = QgsSimpleFillSymbolLayer()
fill_symbol = QgsFillSymbol()
fill_symbol.changeSymbolLayer(0, fill)
fill.setColor(Qt.green)
fill.setStrokeStyle(Qt.NoPen)
item1.setSymbol(fill_symbol)
l.addItem(item1)

item2 = QgsLayoutItemShape(l)
item2.attemptSetSceneRect(QRectF(10, 20, 100, 150))
item2.attemptMove(QgsLayoutPoint(10, 20), page=1)
fill = QgsSimpleFillSymbolLayer()
fill_symbol = QgsFillSymbol()
fill_symbol.changeSymbolLayer(0, fill)
fill.setColor(Qt.cyan)
fill.setStrokeStyle(Qt.NoPen)
item2.setSymbol(fill_symbol)
l.addItem(item2)

exporter = QgsLayoutExporter(l)
# setup settings
settings = QgsLayoutExporter.PdfExportSettings()
settings.dpi = 80
settings.rasterizeWholeImage = False
settings.forceVectorOutput = False

pdf_file_path = os.path.join(self.basetestpath, 'test_exporttopdfdpi.pdf')
self.assertEqual(exporter.exportToPdf(pdf_file_path, settings), QgsLayoutExporter.Success)
self.assertTrue(os.path.exists(pdf_file_path))

rendered_page_1 = os.path.join(self.basetestpath, 'test_exporttopdfdpi.png')
dpi = 80
pdfToPng(pdf_file_path, rendered_page_1, dpi=dpi, page=1)
rendered_page_2 = os.path.join(self.basetestpath, 'test_exporttopdfdpi2.png')
pdfToPng(pdf_file_path, rendered_page_2, dpi=dpi, page=2)

self.assertTrue(self.checkImage('exporttopdfdpi_page1', 'exporttopdfdpi_page1', rendered_page_1, size_tolerance=1))
self.assertTrue(self.checkImage('exporttopdfdpi_page2', 'exporttopdfdpi_page2', rendered_page_2, size_tolerance=1))

def testExportWorldFile(self):
l = QgsLayout(QgsProject.instance())
l.initializeDefaults()
Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 0052eb8

Please sign in to comment.