Skip to content

Commit 0697787

Browse files
committed
Unit tests - upgrade printing module to use pypdf package + update pdf printing test
1 parent 67f7222 commit 0697787

3 files changed

Lines changed: 53 additions & 72 deletions

File tree

mapnik/printing/__init__.py

Lines changed: 30 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,8 @@
22

33
"""Mapnik classes to assist in creating printable maps."""
44

5-
from __future__ import absolute_import, print_function
6-
75
import logging
86
import math
9-
107
from mapnik import Box2d, Coord, Geometry, Layer, Map, Projection, Style, render
118
from mapnik.printing.conversions import m2pt, m2px
129
from mapnik.printing.formats import pagesizes
@@ -25,12 +22,12 @@
2522
HAS_PANGOCAIRO_MODULE = False
2623

2724
try:
28-
from PyPDF2 import PdfFileReader, PdfFileWriter
29-
from PyPDF2.generic import (ArrayObject, DecodedStreamObject, DictionaryObject, FloatObject, NameObject,
30-
NumberObject, TextStringObject)
31-
HAS_PYPDF2 = True
25+
from pypdf import PdfReader, PdfWriter
26+
from pypdf.generic import (ArrayObject, DecodedStreamObject, DictionaryObject, FloatObject, NameObject,
27+
NumberObject, TextStringObject)
28+
HAS_PYPDF = True
3229
except ImportError:
33-
HAS_PYPDF2 = False
30+
HAS_PYPDF = False
3431

3532
"""
3633
Style of centering to use with the map.
@@ -90,7 +87,7 @@ def __init__(self,
9087
Args:
9188
pagesize: tuple of page size in meters, see predefined sizes in mapnik.formats module
9289
margin: page margin in meters
93-
box: the box to render the map into. Must be within page area, margin excluded.
90+
box: the box to render the map into. Must be within page area, margin excluded.
9491
This should be a Mapnik Box2d object. Default is the full page without margin.
9592
percent_box: similar to box argument but specified as a percent (0->1) of the full page size.
9693
If both box and percent_box are specified percent_box will be used.
@@ -104,7 +101,7 @@ def __init__(self,
104101
be a value from the mapnik.utils.centering class. The default is to center on the maps constrained
105102
axis. Typically this will be horizontal for portrait pages and vertical for landscape pages.
106103
is_latlon: whether the map is in lat lon degrees or not.
107-
use_ocg_layers: create OCG layers in the PDF, requires PyPDF2
104+
use_ocg_layers: create OCG layers in the PDF, requires pypdf
108105
font_name: the font name used each time text is written (e.g., legend titles, representative fraction, etc.)
109106
"""
110107
self._pagesize = pagesize
@@ -563,7 +560,7 @@ def render_scale(self, m, ctx=None, width=0.05, num_divisions=3, bar_size=8.0, w
563560
564561
Args:
565562
m: the Map object to render the scale for
566-
ctx: A cairo context to render the scale into. If this is None, we create a context and find out
563+
ctx: A cairo context to render the scale into. If this is None, we create a context and find out
567564
the best location for the scale bar
568565
width: the width of area available for rendering the scale bar (in meters)
569566
num_divisions: the number of divisions for the scale bar
@@ -737,7 +734,7 @@ def render_graticule_on_map(self, m, dec_degrees=True, grid_layer_name="Graticul
737734

738735
# renders the vertical graticule axes
739736
self._render_graticule_axes_and_text(
740-
m,
737+
m,
741738
p2,
742739
latlon_bounds,
743740
latlon_buffer,
@@ -1119,7 +1116,7 @@ def convert_pdf_pages_to_layers(self, filename, layer_names=None, reverse_all_bu
11191116
Takes a multi pages PDF as input and converts each page to a layer in a single page PDF.
11201117
11211118
Note:
1122-
requires PyPDF2 to be available
1119+
requires pypdf to be available
11231120
11241121
Args:
11251122
layer_names should be a sequence of the user visible names of the layers, if not given
@@ -1128,17 +1125,17 @@ def convert_pdf_pages_to_layers(self, filename, layer_names=None, reverse_all_bu
11281125
if output_name is not provided a temporary file will be used for the conversion which
11291126
will then be copied back over the source file.
11301127
"""
1131-
if not HAS_PYPDF2:
1132-
raise RuntimeError("PyPDF2 not available; PyPDF2 required to convert pdf pages to layers")
1128+
if not HAS_PYPDF:
1129+
raise RuntimeError("pypdf not available; pypdf required to convert pdf pages to layers")
11331130

11341131
with open(filename, "rb+") as f:
1135-
file_reader = PdfFileReader(f)
1136-
file_writer = PdfFileWriter()
1132+
file_reader = PdfReader(f)
1133+
file_writer = PdfWriter()
11371134

1138-
template_page_size = file_reader.pages[0].mediaBox
1139-
output_pdf = file_writer.addBlankPage(
1140-
width=template_page_size.getWidth(),
1141-
height=template_page_size.getHeight())
1135+
template_page_size = file_reader.pages[0].mediabox
1136+
output_pdf = file_writer.add_blank_page(
1137+
width=template_page_size.width,
1138+
height=template_page_size.height)
11421139

11431140
content_key = NameObject('/Contents')
11441141
output_pdf[content_key] = ArrayObject()
@@ -1149,15 +1146,15 @@ def convert_pdf_pages_to_layers(self, filename, layer_names=None, reverse_all_bu
11491146
(properties, ocgs) = self._make_ocg_layers(file_reader, file_writer, output_pdf, layer_names)
11501147

11511148
properties_key = NameObject('/Properties')
1152-
output_pdf[resource_key][properties_key] = file_writer._addObject(properties)
1149+
output_pdf[resource_key][properties_key] = file_writer._add_object(properties)
11531150

11541151
ocproperties = DictionaryObject()
11551152
ocproperties[NameObject('/OCGs')] = ocgs
11561153

11571154
default_view = self._get_pdf_default_view(ocgs, reverse_all_but_last)
1158-
ocproperties[NameObject('/D')] = file_writer._addObject(default_view)
1155+
ocproperties[NameObject('/D')] = file_writer._add_object(default_view)
11591156

1160-
file_writer._root_object[NameObject('/OCProperties')] = file_writer._addObject(ocproperties)
1157+
file_writer._root_object[NameObject('/OCProperties')] = file_writer._add_object(ocproperties)
11611158

11621159
f.seek(0)
11631160
file_writer.write(f)
@@ -1189,7 +1186,7 @@ def _make_ocg_layers(self, file_reader, file_writer, output_pdf, layer_names=Non
11891186
page[NameObject(
11901187
'/Contents')] = ArrayObject((ocgs_start, page['/Contents'], ocg_end))
11911188

1192-
output_pdf.mergePage(page)
1189+
output_pdf.merge_page(page)
11931190

11941191
ocg = DictionaryObject()
11951192
ocg[NameObject('/Type')] = NameObject('/OCG')
@@ -1199,7 +1196,7 @@ def _make_ocg_layers(self, file_reader, file_writer, output_pdf, layer_names=Non
11991196
else:
12001197
ocg[NameObject('/Name')] = TextStringObject('Layer %d' % (idx + 1))
12011198

1202-
indirect_ocg = file_writer._addObject(ocg)
1199+
indirect_ocg = file_writer._add_object(ocg)
12031200
properties[ocg_name] = indirect_ocg
12041201
ocgs.append(indirect_ocg)
12051202

@@ -1238,20 +1235,20 @@ def add_geospatial_pdf_header(self, m, filename, epsg=None, wkt=None):
12381235
The epsg code or the wkt text of the projection must be provided.
12391236
Must be called *after* the page has had .finish() called.
12401237
"""
1241-
if not HAS_PYPDF2:
1242-
raise RuntimeError("PyPDF2 not available; PyPDF2 required to add geospatial header to PDF")
1238+
if not HAS_PYPDF:
1239+
raise RuntimeError("pypdf not available; pypdf required to add geospatial header to PDF")
12431240

12441241
if not any((epsg,wkt)):
12451242
raise RuntimeError("EPSG or WKT required to add geospatial header to PDF")
12461243

12471244
with open(filename, "rb+") as f:
1248-
file_reader = PdfFileReader(f)
1249-
file_writer = PdfFileWriter()
1245+
file_reader = PdfReader(f)
1246+
file_writer = PdfWriter()
12501247

12511248
# preserve OCProperties at document root if we have one
1252-
if file_reader.trailer['/Root'].has_key(NameObject('/OCProperties')):
1249+
if NameObject('/OCProperties') in file_reader.trailer['/Root']:
12531250
file_writer._root_object[NameObject('/OCProperties')] = file_reader.trailer[
1254-
'/Root'].getObject()[NameObject('/OCProperties')]
1251+
'/Root'].get_object()[NameObject('/OCProperties')]
12551252

12561253
for page in file_reader.pages:
12571254
gcs = DictionaryObject()
@@ -1265,7 +1262,7 @@ def add_geospatial_pdf_header(self, m, filename, epsg=None, wkt=None):
12651262
measure = self._get_pdf_measure(m, gcs)
12661263
page[NameObject('/VP')] = self._get_pdf_vp(measure)
12671264

1268-
file_writer.addPage(page)
1265+
file_writer.add_page(page)
12691266

12701267
f.seek(0)
12711268
file_writer.write(f)
-15.3 KB
Binary file not shown.
Lines changed: 23 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,41 @@
1-
#!/usr/bin/env python
2-
3-
import os
4-
5-
from nose.tools import eq_
6-
71
import mapnik
8-
from .utilities import execution_path, run_all
9-
10-
def setup():
11-
# All of the paths used are relative, if we run the tests
12-
# from another directory we need to chdir()
13-
os.chdir(execution_path('.'))
2+
import os
143

154
def make_map_from_xml(source_xml):
16-
m = mapnik.Map(100, 100)
17-
mapnik.load_map(m, source_xml, True)
18-
m.zoom_all()
19-
20-
return m
5+
m = mapnik.Map(100, 100)
6+
mapnik.load_map(m, source_xml, True)
7+
m.zoom_all()
8+
return m
219

2210
def make_pdf(m, output_pdf, esri_wkt):
23-
# renders a PDF with a grid and a legend
24-
page = mapnik.printing.PDFPrinter(use_ocg_layers=True)
11+
# renders a PDF with a grid and a legend
12+
page = mapnik.printing.PDFPrinter(use_ocg_layers=True)
2513

26-
page.render_map(m, output_pdf)
27-
page.render_grid_on_map(m)
28-
page.render_legend(m)
14+
page.render_map(m, output_pdf)
15+
page.render_grid_on_map(m)
16+
page.render_legend(m)
2917

30-
page.finish()
31-
page.add_geospatial_pdf_header(m, output_pdf, wkt=esri_wkt)
18+
page.finish()
19+
page.add_geospatial_pdf_header(m, output_pdf, wkt=esri_wkt)
3220

3321
if mapnik.has_pycairo():
34-
import mapnik.printing
22+
import mapnik.printing
3523

36-
def test_pdf_printing():
37-
source_xml = '../data/good_maps/marker-text-line.xml'.encode('utf-8')
38-
m = make_map_from_xml(source_xml)
24+
def test_pdf_printing():
25+
source_xml = './test/data/good_maps/marker-text-line.xml'.encode('utf-8')
26+
m = make_map_from_xml(source_xml)
3927

40-
actual_pdf = "/tmp/pdf-printing-actual.pdf"
41-
esri_wkt = 'GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]'
42-
make_pdf(m, actual_pdf, esri_wkt)
28+
actual_pdf = "/tmp/pdf-printing-actual.pdf"
29+
esri_wkt = 'GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]'
30+
make_pdf(m, actual_pdf, esri_wkt)
4331

44-
expected_pdf = 'images/pycairo/pdf-printing-expected.pdf'
32+
expected_pdf = './test/python_tests/images/pycairo/pdf-printing-expected.pdf'
4533

46-
diff = abs(os.stat(expected_pdf).st_size - os.stat(actual_pdf).st_size)
47-
msg = 'diff in size (%s) between actual (%s) and expected(%s)' % (diff, actual_pdf, 'tests/python_tests/' + expected_pdf)
48-
eq_(diff < 1500, True, msg)
34+
diff = abs(os.stat(expected_pdf).st_size - os.stat(actual_pdf).st_size)
35+
msg = 'diff in size (%s) between actual (%s) and expected(%s)' % (diff, actual_pdf, 'tests/python_tests/' + expected_pdf)
36+
assert diff < 1500, msg
4937

5038
# TODO: ideas for further testing on printing module
5139
# - test with and without pangocairo
5240
# - test legend with attribution
5341
# - test graticule (bug at the moment)
54-
55-
if __name__ == "__main__":
56-
setup()
57-
exit(run_all(eval(x) for x in dir() if x.startswith("test_")))

0 commit comments

Comments
 (0)