Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #9692 from rldhont/fix-regression-server-print-sel…
…ection-pdf-release-3_4

Fix regression server print selection pdf release 3.4
  • Loading branch information
rldhont committed Jun 14, 2019
2 parents 9511168 + ea5bcdc commit 3ed9a53
Show file tree
Hide file tree
Showing 6 changed files with 175 additions and 1 deletion.
8 changes: 8 additions & 0 deletions src/core/layout/qgslayoutexporter.cpp
Expand Up @@ -490,6 +490,8 @@ QgsLayoutExporter::ExportResult QgsLayoutExporter::exportToPdf( const QString &f
( void )contextRestorer;
mLayout->renderContext().setDpi( settings.dpi );

mLayout->renderContext().setFlags( settings.flags );

// If we are not printing as raster, temporarily disable advanced effects
// as QPrinter does not support composition modes and can result
// in items missing from the output
Expand Down Expand Up @@ -561,6 +563,8 @@ QgsLayoutExporter::ExportResult QgsLayoutExporter::exportToPdf( QgsAbstractLayou
( void )contextRestorer;
iterator->layout()->renderContext().setDpi( settings.dpi );

iterator->layout()->renderContext().setFlags( settings.flags );

// If we are not printing as raster, temporarily disable advanced effects
// as QPrinter does not support composition modes and can result
// in items missing from the output
Expand Down Expand Up @@ -671,6 +675,7 @@ QgsLayoutExporter::ExportResult QgsLayoutExporter::print( QPrinter &printer, con
( void )contextRestorer;
mLayout->renderContext().setDpi( settings.dpi );

mLayout->renderContext().setFlags( settings.flags );
// If we are not printing as raster, temporarily disable advanced effects
// as QPrinter does not support composition modes and can result
// in items missing from the output
Expand Down Expand Up @@ -730,6 +735,8 @@ QgsLayoutExporter::ExportResult QgsLayoutExporter::print( QgsAbstractLayoutItera
( void )contextRestorer;
iterator->layout()->renderContext().setDpi( settings.dpi );

iterator->layout()->renderContext().setFlags( settings.flags );

// If we are not printing as raster, temporarily disable advanced effects
// as QPrinter does not support composition modes and can result
// in items missing from the output
Expand Down Expand Up @@ -784,6 +791,7 @@ QgsLayoutExporter::ExportResult QgsLayoutExporter::exportToSvg( const QString &f
( void )contextRestorer;
mLayout->renderContext().setDpi( settings.dpi );

mLayout->renderContext().setFlags( settings.flags );
mLayout->renderContext().setFlag( QgsLayoutRenderContext::FlagForceVectorOutput, settings.forceVectorOutput );
mLayout->renderContext().setTextRenderFormat( s.textRenderFormat );

Expand Down
168 changes: 167 additions & 1 deletion tests/src/python/test_qgsserver_wms_getprint.py
Expand Up @@ -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+'
Expand All @@ -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):
Expand Down Expand Up @@ -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({
Expand Down Expand Up @@ -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),
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.
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 3ed9a53

Please sign in to comment.