Skip to content

Commit c6bb0b9

Browse files
committedApr 2, 2019
Add unit-tests to avoid regression in Server printing to PDF output format
In QGIS 3.4, Selection can be printed in Image output and not in PDF or SVG output. A fix has been done 2752f83 to fix inconsistent use of layout render context flags, and draw selection is activated with a flag.
1 parent de9ca74 commit c6bb0b9

File tree

5 files changed

+167
-1
lines changed

5 files changed

+167
-1
lines changed
 

‎tests/src/python/test_qgsserver_wms_getprint.py‎

Lines changed: 167 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,18 @@
2828

2929
from qgis.testing import unittest
3030
from qgis.PyQt.QtCore import QSize
31+
from qgis.PyQt.QtGui import QImage, QPainter
32+
from qgis.PyQt.QtSvg import QSvgRenderer, QSvgGenerator
3133

3234
import osgeo.gdal # NOQA
35+
import tempfile
36+
import base64
37+
import subprocess
3338

3439
from test_qgsserver import QgsServerTestBase
35-
from qgis.core import QgsProject
40+
from qgis.core import QgsProject, QgsRenderChecker
3641
from qgis.server import QgsServerRequest
42+
from utilities import getExecutablePath, unitTestDataPath
3743

3844
# Strip path and content length because path may vary
3945
RE_STRIP_UNCHECKABLE = b'MAP=[^"]+|Content-Length: \d+'
@@ -42,6 +48,132 @@
4248

4349
class TestQgsServerWMSGetPrint(QgsServerTestBase):
4450

51+
def _pdf_to_png(self, pdf_file_path, rendered_file_path, page, dpi=96):
52+
53+
# PDF-to-image utility
54+
# look for Poppler w/ Cairo, then muPDF
55+
# * Poppler w/ Cairo renders correctly
56+
# * Poppler w/o Cairo does not always correctly render vectors in PDF to image
57+
# * muPDF renders correctly, but slightly shifts colors
58+
for util in [
59+
'pdftocairo',
60+
# 'mudraw',
61+
]:
62+
PDFUTIL = getExecutablePath(util)
63+
if PDFUTIL:
64+
break
65+
66+
# noinspection PyUnboundLocalVariable
67+
if not PDFUTIL:
68+
assert False, ('PDF-to-image utility not found on PATH: '
69+
'install Poppler (with Cairo)')
70+
71+
if PDFUTIL.strip().endswith('pdftocairo'):
72+
filebase = os.path.join(
73+
os.path.dirname(rendered_file_path),
74+
os.path.splitext(os.path.basename(rendered_file_path))[0]
75+
)
76+
call = [
77+
PDFUTIL, '-png', '-singlefile', '-r', str(dpi),
78+
'-x', '0', '-y', '0', '-f', str(page), '-l', str(page),
79+
pdf_file_path, filebase
80+
]
81+
elif PDFUTIL.strip().endswith('mudraw'):
82+
call = [
83+
PDFUTIL, '-c', 'rgba',
84+
'-r', str(dpi), '-f', str(page), '-l', str(page),
85+
# '-b', '8',
86+
'-o', rendered_file_path, pdf_file_path
87+
]
88+
else:
89+
return False, ''
90+
91+
print("exportToPdf call: {0}".format(' '.join(call)))
92+
try:
93+
subprocess.check_call(call)
94+
except subprocess.CalledProcessError as e:
95+
assert False, ("exportToPdf failed!\n"
96+
"cmd: {0}\n"
97+
"returncode: {1}\n"
98+
"message: {2}".format(e.cmd, e.returncode, e.message))
99+
100+
def _pdf_diff(self, pdf, control_image, max_diff, max_size_diff=QSize(), dpi=96):
101+
102+
temp_pdf = os.path.join(tempfile.gettempdir(), "%s_result.pdf" % control_image)
103+
104+
with open(temp_pdf, "wb") as f:
105+
f.write(pdf)
106+
107+
temp_image = os.path.join(tempfile.gettempdir(), "%s_result.png" % control_image)
108+
self._pdf_to_png(temp_pdf, temp_image, dpi=dpi, page=1)
109+
110+
control = QgsRenderChecker()
111+
control.setControlPathPrefix("qgis_server")
112+
control.setControlName(control_image)
113+
control.setRenderedImage(temp_image)
114+
if max_size_diff.isValid():
115+
control.setSizeTolerance(max_size_diff.width(), max_size_diff.height())
116+
return control.compareImages(control_image, max_diff), control.report()
117+
118+
def _pdf_diff_error(self, response, headers, image, max_diff=100, max_size_diff=QSize(), unittest_data_path='control_images', dpi=96):
119+
120+
reference_path = unitTestDataPath(unittest_data_path) + '/qgis_server/' + image + '/' + image + '.pdf'
121+
self.store_reference(reference_path, response)
122+
123+
self.assertEqual(
124+
headers.get("Content-Type"), 'application/pdf',
125+
"Content type is wrong: %s instead of %s\n%s" % (headers.get("Content-Type"), 'application/pdf', response))
126+
127+
test, report = self._pdf_diff(response, image, max_diff, max_size_diff, dpi)
128+
129+
with open(os.path.join(tempfile.gettempdir(), image + "_result.pdf"), "rb") as rendered_file:
130+
if not os.environ.get('ENCODED_OUTPUT'):
131+
message = "PDF is wrong\: rendered file %s/%s_result.%s" % (tempfile.gettempdir(), image, 'pdf')
132+
else:
133+
encoded_rendered_file = base64.b64encode(rendered_file.read())
134+
message = "PDF is wrong\n%s\File:\necho '%s' | base64 -d >%s/%s_result.%s" % (
135+
report, encoded_rendered_file.strip().decode('utf8'), tempfile.gettempdir(), image, 'pdf'
136+
)
137+
138+
with open(os.path.join(tempfile.gettempdir(), image + "_result.png"), "rb") as rendered_file:
139+
if not os.environ.get('ENCODED_OUTPUT'):
140+
message = "Image is wrong\: rendered file %s/%s_result.%s" % (tempfile.gettempdir(), image, 'png')
141+
else:
142+
encoded_rendered_file = base64.b64encode(rendered_file.read())
143+
message = "Image is wrong\n%s\nImage:\necho '%s' | base64 -d >%s/%s_result.%s" % (
144+
report, encoded_rendered_file.strip().decode('utf8'), tempfile.gettempdir(), image, 'png'
145+
)
146+
147+
# If the failure is in image sizes the diff file will not exists.
148+
if os.path.exists(os.path.join(tempfile.gettempdir(), image + "_result_diff.png")):
149+
with open(os.path.join(tempfile.gettempdir(), image + "_result_diff.png"), "rb") as diff_file:
150+
if not os.environ.get('ENCODED_OUTPUT'):
151+
message = "Image is wrong\: diff file %s/%s_result_diff.%s" % (tempfile.gettempdir(), image, 'png')
152+
else:
153+
encoded_diff_file = base64.b64encode(diff_file.read())
154+
message += "\nDiff:\necho '%s' | base64 -d > %s/%s_result_diff.%s" % (
155+
encoded_diff_file.strip().decode('utf8'), tempfile.gettempdir(), image, 'png'
156+
)
157+
158+
self.assertTrue(test, message)
159+
160+
def _svg_to_png(svg_file_path, rendered_file_path, width):
161+
svgr = QSvgRenderer(svg_file_path)
162+
163+
height = width / svgr.viewBoxF().width() * svgr.viewBoxF().height()
164+
165+
image = QImage(width, height, QImage.Format_ARGB32)
166+
image.fill(Qt.transparent)
167+
168+
p = QPainter(image)
169+
p.setRenderHint(QPainter.Antialiasing, False)
170+
svgr.render(p)
171+
p.end()
172+
173+
res = image.save(rendered_file_path, 'png')
174+
if not res:
175+
os.unlink(rendered_file_path)
176+
45177
"""QGIS Server WMS Tests for GetPrint request"""
46178

47179
def test_wms_getprint_basic(self):
@@ -107,6 +239,22 @@ def test_wms_getprint_basic(self):
107239
r, h = self._result(self._execute_request(qs))
108240
self._img_diff_error(r, h, "WMS_GetPrint_Basic", outputJpg=True)
109241

242+
# Output PDF
243+
qs = "?" + "&".join(["%s=%s" % i for i in list({
244+
"MAP": urllib.parse.quote(self.projectPath),
245+
"SERVICE": "WMS",
246+
"VERSION": "1.1.1",
247+
"REQUEST": "GetPrint",
248+
"TEMPLATE": "layoutA4",
249+
"FORMAT": "pdf",
250+
"map0:EXTENT": "-33626185.498,-13032965.185,33978427.737,16020257.031",
251+
"LAYERS": "Country,Hello",
252+
"CRS": "EPSG:3857"
253+
}.items())])
254+
255+
r, h = self._result(self._execute_request(qs))
256+
self._pdf_diff_error(r, h, "WMS_GetPrint_Basic_Pdf", dpi=300)
257+
110258
def test_wms_getprint_style(self):
111259
# default style
112260
qs = "?" + "&".join(["%s=%s" % i for i in list({
@@ -312,6 +460,24 @@ def test_wms_getprint_selection(self):
312460
r, h = self._result(self._execute_request(qs))
313461
self._img_diff_error(r, h, "WMS_GetPrint_Selection")
314462

463+
# Output PDF
464+
qs = "?" + "&".join(["%s=%s" % i for i in list({
465+
"MAP": urllib.parse.quote(self.projectPath),
466+
"SERVICE": "WMS",
467+
"VERSION": "1.1.1",
468+
"REQUEST": "GetPrint",
469+
"TEMPLATE": "layoutA4",
470+
"FORMAT": "pdf",
471+
"LAYERS": "Country,Hello",
472+
"map0:EXTENT": "-33626185.498,-13032965.185,33978427.737,16020257.031",
473+
"map0:LAYERS": "Country,Hello",
474+
"CRS": "EPSG:3857",
475+
"SELECTION": "Country: 4"
476+
}.items())])
477+
478+
r, h = self._result(self._execute_request(qs))
479+
self._pdf_diff_error(r, h, "WMS_GetPrint_Selection_Pdf", dpi=300)
480+
315481
def test_wms_getprint_opacity(self):
316482
qs = "?" + "&".join(["%s=%s" % i for i in list({
317483
"MAP": urllib.parse.quote(self.projectPath),
256 KB

Error rendering embedded code

Invalid image source.

51.8 KB

Error rendering embedded code

Invalid image source.

294 KB

Error rendering embedded code

Invalid image source.

387 KB

Error rendering embedded code

Invalid image source.

0 commit comments

Comments
 (0)
Please sign in to comment.