Skip to content
Permalink
Browse files
Merge pull request #9705 from qgis/backport-9654-to-release-3_6
[Backport release-3_6] [Unit-test]To avoid regression in Server printing to PDF output format
  • Loading branch information
m-kuhn committed Apr 4, 2019
2 parents de9ca74 + c6bb0b9 commit 83dfcc9f2c1c8667f2c58c296095411b64729a0e
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 1 deletion.
@@ -28,12 +28,18 @@

from qgis.testing import unittest
from qgis.PyQt.QtCore import QSize
from qgis.PyQt.QtGui import QImage, QPainter
from qgis.PyQt.QtSvg import QSvgRenderer, QSvgGenerator

import osgeo.gdal # NOQA
import tempfile
import base64
import subprocess

from test_qgsserver import QgsServerTestBase
from qgis.core import QgsProject
from qgis.core import QgsProject, QgsRenderChecker
from qgis.server import QgsServerRequest
from utilities import getExecutablePath, unitTestDataPath

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

class TestQgsServerWMSGetPrint(QgsServerTestBase):

def _pdf_to_png(self, pdf_file_path, rendered_file_path, page, dpi=96):

# 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 slightly shifts colors
for util in [
'pdftocairo',
# 'mudraw',
]:
PDFUTIL = getExecutablePath(util)
if PDFUTIL:
break

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

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))

def _pdf_diff(self, pdf, control_image, max_diff, max_size_diff=QSize(), dpi=96):

temp_pdf = os.path.join(tempfile.gettempdir(), "%s_result.pdf" % control_image)

with open(temp_pdf, "wb") as f:
f.write(pdf)

temp_image = os.path.join(tempfile.gettempdir(), "%s_result.png" % control_image)
self._pdf_to_png(temp_pdf, temp_image, dpi=dpi, page=1)

control = QgsRenderChecker()
control.setControlPathPrefix("qgis_server")
control.setControlName(control_image)
control.setRenderedImage(temp_image)
if max_size_diff.isValid():
control.setSizeTolerance(max_size_diff.width(), max_size_diff.height())
return control.compareImages(control_image, max_diff), control.report()

def _pdf_diff_error(self, response, headers, image, max_diff=100, max_size_diff=QSize(), unittest_data_path='control_images', dpi=96):

reference_path = unitTestDataPath(unittest_data_path) + '/qgis_server/' + image + '/' + image + '.pdf'
self.store_reference(reference_path, response)

self.assertEqual(
headers.get("Content-Type"), 'application/pdf',
"Content type is wrong: %s instead of %s\n%s" % (headers.get("Content-Type"), 'application/pdf', response))

test, report = self._pdf_diff(response, image, max_diff, max_size_diff, dpi)

with open(os.path.join(tempfile.gettempdir(), image + "_result.pdf"), "rb") as rendered_file:
if not os.environ.get('ENCODED_OUTPUT'):
message = "PDF is wrong\: rendered file %s/%s_result.%s" % (tempfile.gettempdir(), image, 'pdf')
else:
encoded_rendered_file = base64.b64encode(rendered_file.read())
message = "PDF is wrong\n%s\File:\necho '%s' | base64 -d >%s/%s_result.%s" % (
report, encoded_rendered_file.strip().decode('utf8'), tempfile.gettempdir(), image, 'pdf'
)

with open(os.path.join(tempfile.gettempdir(), image + "_result.png"), "rb") as rendered_file:
if not os.environ.get('ENCODED_OUTPUT'):
message = "Image is wrong\: rendered file %s/%s_result.%s" % (tempfile.gettempdir(), image, 'png')
else:
encoded_rendered_file = base64.b64encode(rendered_file.read())
message = "Image is wrong\n%s\nImage:\necho '%s' | base64 -d >%s/%s_result.%s" % (
report, encoded_rendered_file.strip().decode('utf8'), tempfile.gettempdir(), image, 'png'
)

# If the failure is in image sizes the diff file will not exists.
if os.path.exists(os.path.join(tempfile.gettempdir(), image + "_result_diff.png")):
with open(os.path.join(tempfile.gettempdir(), image + "_result_diff.png"), "rb") as diff_file:
if not os.environ.get('ENCODED_OUTPUT'):
message = "Image is wrong\: diff file %s/%s_result_diff.%s" % (tempfile.gettempdir(), image, 'png')
else:
encoded_diff_file = base64.b64encode(diff_file.read())
message += "\nDiff:\necho '%s' | base64 -d > %s/%s_result_diff.%s" % (
encoded_diff_file.strip().decode('utf8'), tempfile.gettempdir(), image, 'png'
)

self.assertTrue(test, message)

def _svg_to_png(svg_file_path, rendered_file_path, width):
svgr = QSvgRenderer(svg_file_path)

height = width / svgr.viewBoxF().width() * svgr.viewBoxF().height()

image = QImage(width, height, QImage.Format_ARGB32)
image.fill(Qt.transparent)

p = QPainter(image)
p.setRenderHint(QPainter.Antialiasing, False)
svgr.render(p)
p.end()

res = image.save(rendered_file_path, 'png')
if not res:
os.unlink(rendered_file_path)

"""QGIS Server WMS Tests for GetPrint request"""

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

# Output PDF
qs = "?" + "&".join(["%s=%s" % i for i in list({
"MAP": urllib.parse.quote(self.projectPath),
"SERVICE": "WMS",
"VERSION": "1.1.1",
"REQUEST": "GetPrint",
"TEMPLATE": "layoutA4",
"FORMAT": "pdf",
"map0:EXTENT": "-33626185.498,-13032965.185,33978427.737,16020257.031",
"LAYERS": "Country,Hello",
"CRS": "EPSG:3857"
}.items())])

r, h = self._result(self._execute_request(qs))
self._pdf_diff_error(r, h, "WMS_GetPrint_Basic_Pdf", dpi=300)

def test_wms_getprint_style(self):
# default style
qs = "?" + "&".join(["%s=%s" % i for i in list({
@@ -312,6 +460,24 @@ def test_wms_getprint_selection(self):
r, h = self._result(self._execute_request(qs))
self._img_diff_error(r, h, "WMS_GetPrint_Selection")

# Output PDF
qs = "?" + "&".join(["%s=%s" % i for i in list({
"MAP": urllib.parse.quote(self.projectPath),
"SERVICE": "WMS",
"VERSION": "1.1.1",
"REQUEST": "GetPrint",
"TEMPLATE": "layoutA4",
"FORMAT": "pdf",
"LAYERS": "Country,Hello",
"map0:EXTENT": "-33626185.498,-13032965.185,33978427.737,16020257.031",
"map0:LAYERS": "Country,Hello",
"CRS": "EPSG:3857",
"SELECTION": "Country: 4"
}.items())])

r, h = self._result(self._execute_request(qs))
self._pdf_diff_error(r, h, "WMS_GetPrint_Selection_Pdf", dpi=300)

def test_wms_getprint_opacity(self):
qs = "?" + "&".join(["%s=%s" % i for i in list({
"MAP": urllib.parse.quote(self.projectPath),
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.
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 83dfcc9

Please sign in to comment.