3
2
# Major library imports
7
6
# PDF imports from reportlab
8
7
from reportlab.pdfgen.canvas import Canvas
9
from reportlab.lib.pagesizes import letter, A4
8
from reportlab.lib.pagesizes import letter, A4, landscape
10
9
from reportlab.lib.units import inch, cm, mm, pica
12
11
except ImportError:
16
15
from kiva.pdf import GraphicsContext
23
"landscape_letter": landscape(letter),
24
"landscape_A4": landscape(A4)
30
34
if Canvas is not None:
31
35
class PdfPlotGraphicsContext(GraphicsContext):
40
44
pagesize = "letter" # Enum("letter", "A4")
42
46
# A tuple (x, y, width, height) specifying the box into which the plot
43
# should be rendered. **x** and **y** correspond to the lower-left hand
44
# coordinates of the box in the coordinates of the page (i.e. 0,0 is at the
45
# lower left). **width** and **height** can be positive or negative;
47
# should be rendered. **x** and **y** correspond to the lower-left
48
# hand coordinates of the box in the coordinates of the page
49
# (i.e. 0,0 is at the lower left). **width** and **height** can be
50
# positive or negative;
46
51
# if they are positive, they are interpreted as distances from (x,y);
47
# if they are negative, they are interpreted as distances from the right
48
# and top of the page, respectively.
52
# if they are negative, they are interpreted as distances from the
53
# right and top of the page, respectively.
49
54
dest_box = (0.5, 0.5, -0.5, -0.5)
51
56
# The units of the values in dest_box
52
57
dest_box_units = "inch" # Enum("inch", "cm", "mm", "pica")
55
59
def __init__(self, pdf_canvas=None, filename=None, pagesize=None,
56
60
dest_box=None, dest_box_units=None):
64
68
self.dest_box_units = dest_box_units
66
if pdf_canvas == None:
70
if pdf_canvas is None:
67
71
pdf_canvas = self._create_new_canvas()
69
73
GraphicsContext.__init__(self, pdf_canvas)
76
""" Adds a new page to the PDF canvas and makes that the current
80
warnings.warn("PDF Canvas has not been created yet.")
86
# We'll need to call _initialize_page() before drawing
87
self._page_initialized = False
72
89
def render_component(self, component, container_coords=False,
73
90
halign="center", valign="top"):
74
""" Erases the current contents of the graphics context and renders the
75
given component at the maximum possible scaling while preserving aspect
91
""" Erases the current contents of the graphics context and renders
92
the given component at the maximum possible scaling while
93
preserving aspect ratio.
82
99
container_coords : Boolean
83
100
Whether to use coordinates of the component's container
84
101
halign : "center", "left", "right"
85
Determines the position of the component if it is narrower than the
86
graphics context area (after scaling)
102
Determines the position of the component if it is narrower than
103
the graphics context area (after scaling)
87
104
valign : "center", "top", "bottom"
88
Determiens the position of the component if it is shorter than the
89
graphics context area (after scaling)
105
Determiens the position of the component if it is shorter than
106
the graphics context area (after scaling)
93
110
If *container_coords* is False, then the (0,0) coordinate of this
94
111
graphics context corresponds to the lower-left corner of the
95
component's **outer_bounds**. If *container_coords* is True, then the
96
method draws the component as it appears inside its container, i.e., it
97
treats (0,0) of the graphics context as the lower-left corner of the
98
container's outer bounds.
112
component's **outer_bounds**. If *container_coords* is True, then
113
the method draws the component as it appears inside its container,
114
i.e., it treats (0, 0) of the graphics context as the lower-left
115
corner of the container's outer bounds.
118
if not self._page_initialized:
119
# Make sure the origin is set up as before.
120
self._initialize_page(self.gc)
101
122
x, y = component.outer_position
102
123
if container_coords:
103
124
width, height = component.container.bounds
107
128
width, height = component.outer_bounds
109
# Compute the correct scaling to fit the component into the available
110
# canvas space while preserving aspect ratio.
130
# Compute the correct scaling to fit the component into the
131
# available canvas space while preserving aspect ratio.
111
132
units = UNITS_MAP[self.dest_box_units]
112
133
pagesize = PAGE_SIZE_MAP[self.pagesize]
156
177
self.translate_ctm(trans_x, trans_y)
157
178
self.scale_ctm(scale, scale)
158
self.clip_to_rect(0, 0, width, height)
179
self.clip_to_rect(-x, -y, width, height)
159
180
old_bb_setting = component.use_backbuffer
160
181
component.use_backbuffer = False
161
182
component.draw(self, view_bounds=(0, 0, width, height))
168
189
def _create_new_canvas(self):
190
""" Create the PDF canvas context.
192
x, y, w, h, = self._get_bounding_box()
169
197
pagesize = PAGE_SIZE_MAP[self.pagesize]
170
units = UNITS_MAP[self.dest_box_units]
171
198
gc = Canvas(filename=self.filename, pagesize=pagesize)
173
width = pagesize[0] * units * inch / 72.0
174
height = pagesize[1] * units * inch / 72.0
199
self._initialize_page(gc)
203
def _get_bounding_box(self):
204
""" Compute the bounding rect of a page.
206
pagesize = PAGE_SIZE_MAP[self.pagesize]
207
units = UNITS_MAP[self.dest_box_units]
175
209
x = self.dest_box[0] * units
176
210
y = self.dest_box[1] * units
177
211
w = self.dest_box[2] * units
178
212
h = self.dest_box[3] * units
215
w += pagesize[0] * units * inch / PAGE_DPI
217
h += pagesize[1] * units * inch / PAGE_DPI
185
219
if w < 0 or h < 0:
186
220
warnings.warn("Margins exceed page dimensions.")
224
def _initialize_page(self, gc):
225
""" Make sure the origin is set to something consistent.
227
x, y, w, h, = self._get_bounding_box()
191
231
path = gc.beginPath()
192
232
path.rect(0, 0, w, h)
193
233
gc.clipPath(path, stroke=0, fill=0)
235
self._page_initialized = True