~ubuntu-branches/ubuntu/raring/z3c.rml/raring-201302251714

« back to all changes in this revision

Viewing changes to src/z3c/rml/canvas.py

  • Committer: Bazaar Package Importer
  • Author(s): Gediminas Paulauskas
  • Date: 2011-01-05 22:34:45 UTC
  • Revision ID: james.westby@ubuntu.com-20110105223445-wkcn61jbbuqid38s
Tags: upstream-0.9.1
ImportĀ upstreamĀ versionĀ 0.9.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
##############################################################################
 
2
#
 
3
# Copyright (c) 2007 Zope Foundation and Contributors.
 
4
# All Rights Reserved.
 
5
#
 
6
# This software is subject to the provisions of the Zope Public License,
 
7
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
 
8
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
 
9
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 
10
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
 
11
# FOR A PARTICULAR PURPOSE.
 
12
#
 
13
##############################################################################
 
14
"""Page Drawing Related Element Processing
 
15
 
 
16
$Id: canvas.py 114908 2010-07-22 02:23:18Z srichter $
 
17
"""
 
18
__docformat__ = "reStructuredText"
 
19
import zope.interface
 
20
import reportlab.pdfgen.canvas
 
21
from z3c.rml import attr, directive, interfaces, occurence, stylesheet
 
22
from z3c.rml import chart, flowable, form, page
 
23
 
 
24
 
 
25
class IShape(interfaces.IRMLDirectiveSignature):
 
26
    """A shape to be drawn on the canvas."""
 
27
 
 
28
    x = attr.Measurement(
 
29
        title=u'X-Coordinate',
 
30
        description=(u'The X-coordinate of the lower-left position of the '
 
31
                     u'shape.'),
 
32
        required=True)
 
33
 
 
34
    y = attr.Measurement(
 
35
        title=u'Y-Coordinate',
 
36
        description=(u'The Y-coordinate of the lower-left position of the '
 
37
                     u'shape.'),
 
38
        required=True)
 
39
 
 
40
    fill = attr.Boolean(
 
41
        title=u'Fill',
 
42
        description=u'A flag to specify whether the shape should be filled.',
 
43
        required=False)
 
44
 
 
45
    stroke = attr.Boolean(
 
46
        title=u'Stroke',
 
47
        description=(u"A flag to specify whether the shape's outline should "
 
48
                     u"be drawn."),
 
49
        required=False)
 
50
 
 
51
 
 
52
class CanvasRMLDirective(directive.RMLDirective):
 
53
    callable = None
 
54
    attrMapping = None
 
55
 
 
56
    def process(self):
 
57
        kwargs = dict(self.getAttributeValues(attrMapping=self.attrMapping))
 
58
        canvas = attr.getManager(self, interfaces.ICanvasManager).canvas
 
59
        getattr(canvas, self.callable)(**kwargs)
 
60
 
 
61
 
 
62
class IDrawString(interfaces.IRMLDirectiveSignature):
 
63
    """Draws a simple string (left aligned) onto the canvas at the specified
 
64
    location."""
 
65
 
 
66
    x = attr.Measurement(
 
67
        title=u'X-Coordinate',
 
68
        description=(u'The X-coordinate of the lower-left position of the '
 
69
                     u'string.'),
 
70
        required=True)
 
71
 
 
72
    y = attr.Measurement(
 
73
        title=u'Y-Coordinate',
 
74
        description=(u'The Y-coordinate of the lower-left position of the '
 
75
                     u'string.'),
 
76
        required=True)
 
77
 
 
78
    text = attr.TextNode(
 
79
        title=u'Text',
 
80
        description=(u'The string/text that is put onto the canvas.'),
 
81
        required=True)
 
82
 
 
83
class DrawString(CanvasRMLDirective):
 
84
    signature = IDrawString
 
85
    callable = 'drawString'
 
86
 
 
87
class IDrawRightString(IDrawString):
 
88
    """Draws a simple string (right aligned) onto the canvas at the specified
 
89
    location."""
 
90
 
 
91
class DrawRightString(DrawString):
 
92
    signature = IDrawRightString
 
93
    callable = 'drawRightString'
 
94
 
 
95
 
 
96
class IDrawCenteredString(IDrawString):
 
97
    """Draws a simple string (centered aligned) onto the canvas at the specified
 
98
    location."""
 
99
 
 
100
class DrawCenteredString(DrawString):
 
101
    signature = IDrawCenteredString
 
102
    callable = 'drawCentredString'
 
103
 
 
104
 
 
105
class IDrawAlignedString(IDrawString):
 
106
    """Draws a simple string (aligned to the pivot character) onto the canvas
 
107
    at the specified location."""
 
108
 
 
109
    pivotChar = attr.Text(
 
110
        title=u'Text',
 
111
        description=(u'The string/text that is put onto the canvas.'),
 
112
        min_length=1,
 
113
        max_length=1,
 
114
        default=u'.',
 
115
        required=True)
 
116
 
 
117
class DrawAlignedString(DrawString):
 
118
    signature = IDrawAlignedString
 
119
    callable = 'drawAlignedString'
 
120
 
 
121
 
 
122
class IEllipse(IShape):
 
123
    """Draws an ellipse on the canvas."""
 
124
 
 
125
    width = attr.Measurement(
 
126
        title=u'Width',
 
127
        description=u'The width of the ellipse.',
 
128
        required=True)
 
129
 
 
130
    height = attr.Measurement(
 
131
        title=u'Height',
 
132
        description=u'The height of the ellipse.',
 
133
        required=True)
 
134
 
 
135
class Ellipse(CanvasRMLDirective):
 
136
    signature = IEllipse
 
137
    callable = 'ellipse'
 
138
    attrMapping = {'x': 'x1', 'y': 'y1'}
 
139
 
 
140
    def process(self):
 
141
        kwargs = dict(self.getAttributeValues(attrMapping=self.attrMapping))
 
142
        canvas = attr.getManager(self, interfaces.ICanvasManager).canvas
 
143
        # Convert width and height to end locations
 
144
        kwargs['x2'] = kwargs['x1'] + kwargs['width']
 
145
        del kwargs['width']
 
146
        kwargs['y2'] = kwargs['y1'] + kwargs['height']
 
147
        del kwargs['height']
 
148
        getattr(canvas, self.callable)(**kwargs)
 
149
 
 
150
 
 
151
class ICircle(IShape):
 
152
    """Draws a circle on the canvas."""
 
153
 
 
154
    radius = attr.Measurement(
 
155
        title=u'Radius',
 
156
        description=u'The radius of the circle.',
 
157
        required=True)
 
158
 
 
159
class Circle(CanvasRMLDirective):
 
160
    signature = ICircle
 
161
    callable = 'circle'
 
162
    attrMapping = {'x': 'x_cen', 'y': 'y_cen', 'radius': 'r'}
 
163
 
 
164
 
 
165
class IRectangle(IShape):
 
166
    """Draws an ellipse on the canvas."""
 
167
 
 
168
    width = attr.Measurement(
 
169
        title=u'Width',
 
170
        description=u'The width of the rectangle.',
 
171
        required=True)
 
172
 
 
173
    height = attr.Measurement(
 
174
        title=u'Height',
 
175
        description=u'The height of the rectangle.',
 
176
        required=True)
 
177
 
 
178
    round = attr.Measurement(
 
179
        title=u'Corner Radius',
 
180
        description=u'The radius of the rounded corners.',
 
181
        required=False)
 
182
 
 
183
class Rectangle(CanvasRMLDirective):
 
184
    signature = IRectangle
 
185
    callable = 'rect'
 
186
    attrMapping = {'round': 'radius'}
 
187
 
 
188
    def process(self):
 
189
        if 'round' in self.element.keys():
 
190
            self.callable = 'roundRect'
 
191
        super(Rectangle, self).process()
 
192
 
 
193
 
 
194
class IGrid(interfaces.IRMLDirectiveSignature):
 
195
    """A shape to be drawn on the canvas."""
 
196
 
 
197
    xs = attr.Sequence(
 
198
        title=u'X-Coordinates',
 
199
        description=(u'A sequence x-coordinates that represent the vertical '
 
200
                     u'line positions.'),
 
201
        value_type=attr.Measurement(),
 
202
        required=True)
 
203
 
 
204
    ys = attr.Sequence(
 
205
        title=u'Y-Coordinates',
 
206
        description=(u'A sequence y-coordinates that represent the horizontal '
 
207
                     u'line positions.'),
 
208
        value_type=attr.Measurement(),
 
209
        required=True)
 
210
 
 
211
 
 
212
class Grid(CanvasRMLDirective):
 
213
    signature = IGrid
 
214
    callable = 'grid'
 
215
    attrMapping = {'xs': 'xlist', 'ys': 'ylist'}
 
216
 
 
217
 
 
218
class ILines(interfaces.IRMLDirectiveSignature):
 
219
    """A path of connected lines drawn on the canvas."""
 
220
 
 
221
    linelist = attr.TextNodeGrid(
 
222
        title=u'Line List',
 
223
        description=(u'A list of lines coordinates to draw.'),
 
224
        value_type=attr.Measurement(),
 
225
        columns=4,
 
226
        required=True)
 
227
 
 
228
class Lines(CanvasRMLDirective):
 
229
    signature = ILines
 
230
    callable = 'lines'
 
231
 
 
232
 
 
233
class ICurves(interfaces.IRMLDirectiveSignature):
 
234
    """A path of connected bezier curves drawn on the canvas."""
 
235
 
 
236
    curvelist = attr.TextNodeGrid(
 
237
        title=u'Curve List',
 
238
        description=(u'A list of curve coordinates to draw.'),
 
239
        value_type=attr.Measurement(),
 
240
        columns=8,
 
241
        required=True)
 
242
 
 
243
class Curves(CanvasRMLDirective):
 
244
    signature = ICurves
 
245
    callable = 'bezier'
 
246
 
 
247
    def process(self):
 
248
        argset = self.getAttributeValues(valuesOnly=True)[0]
 
249
        canvas = attr.getManager(self, interfaces.ICanvasManager).canvas
 
250
        for args in argset:
 
251
            getattr(canvas, self.callable)(*args)
 
252
 
 
253
 
 
254
class IImage(interfaces.IRMLDirectiveSignature):
 
255
    """Draws an external image on the canvas."""
 
256
 
 
257
    file = attr.Image(
 
258
        title=u'File',
 
259
        description=(u'Reference to the external file of the iamge.'),
 
260
        required=True)
 
261
 
 
262
    x = attr.Measurement(
 
263
        title=u'X-Coordinate',
 
264
        description=(u'The X-coordinate of the lower-left position of the '
 
265
                     u'shape.'),
 
266
        required=True)
 
267
 
 
268
    y = attr.Measurement(
 
269
        title=u'Y-Coordinate',
 
270
        description=(u'The Y-coordinate of the lower-left position of the '
 
271
                     u'shape.'),
 
272
        required=True)
 
273
 
 
274
    width = attr.Measurement(
 
275
        title=u'Width',
 
276
        description=u'The width of the image.',
 
277
        required=False)
 
278
 
 
279
    height = attr.Measurement(
 
280
        title=u'Height',
 
281
        description=u'The height of the image.',
 
282
        required=False)
 
283
 
 
284
    showBoundary = attr.Boolean(
 
285
        title=u'Show Boundary',
 
286
        description=(u'A flag determining whether a border should be drawn '
 
287
                     u'around the image.'),
 
288
        default=False,
 
289
        required=False)
 
290
 
 
291
    preserveAspectRatio = attr.Boolean(
 
292
        title=u'Preserve Aspect Ratio',
 
293
        description=(u"A flag determining whether the image's aspect ration "
 
294
                     u"should be conserved under any circumstances."),
 
295
        default=False,
 
296
        required=False)
 
297
 
 
298
class Image(CanvasRMLDirective):
 
299
    signature = IImage
 
300
    callable = 'drawImage'
 
301
    attrMapping = {'file': 'image'}
 
302
 
 
303
    def process(self):
 
304
        kwargs = dict(self.getAttributeValues(attrMapping=self.attrMapping))
 
305
        preserve = kwargs.pop('preserveAspectRatio')
 
306
        show = kwargs.pop('showBoundary')
 
307
 
 
308
        if preserve:
 
309
            imgX, imgY = kwargs['image'].getSize()
 
310
 
 
311
            # Scale image correctly, if width and/or height were specified
 
312
            if 'width' in kwargs and 'height' not in kwargs:
 
313
                kwargs['height'] = imgY * kwargs['width'] / imgX
 
314
            elif 'height' in kwargs and 'width' not in kwargs:
 
315
                kwargs['width'] = imgX * kwargs['height'] / imgY
 
316
            elif 'width' in kwargs and 'height' in kwargs:
 
317
                if float(kwargs['width'])/kwargs['height'] > float(imgX)/imgY:
 
318
                    kwargs['width'] = imgX * kwargs['height'] / imgY
 
319
                else:
 
320
                    kwargs['height'] = imgY * kwargs['width'] / imgX
 
321
 
 
322
        canvas = attr.getManager(self, interfaces.ICanvasManager).canvas
 
323
        getattr(canvas, self.callable)(**kwargs)
 
324
 
 
325
        if show:
 
326
            width = kwargs.get('width', kwargs['image'].getSize()[0])
 
327
            height = kwargs.get('height', kwargs['image'].getSize()[1])
 
328
            canvas.rect(kwargs['x'], kwargs['y'], width, height)
 
329
 
 
330
 
 
331
class IPlace(interfaces.IRMLDirectiveSignature):
 
332
    """Draws a set of flowables on the canvas within a given region."""
 
333
 
 
334
    x = attr.Measurement(
 
335
        title=u'X-Coordinate',
 
336
        description=(u'The X-coordinate of the lower-left position of the '
 
337
                     u'place.'),
 
338
        required=True)
 
339
 
 
340
    y = attr.Measurement(
 
341
        title=u'Y-Coordinate',
 
342
        description=(u'The Y-coordinate of the lower-left position of the '
 
343
                     u'place.'),
 
344
        required=True)
 
345
 
 
346
    width = attr.Measurement(
 
347
        title=u'Width',
 
348
        description=u'The width of the place.',
 
349
        required=False)
 
350
 
 
351
    height = attr.Measurement(
 
352
        title=u'Height',
 
353
        description=u'The height of the place.',
 
354
        required=False)
 
355
 
 
356
class Place(CanvasRMLDirective):
 
357
    signature = IPlace
 
358
 
 
359
    def process(self):
 
360
        x, y, width, height = self.getAttributeValues(
 
361
            select=('x', 'y', 'width', 'height'), valuesOnly=True)
 
362
        y += height
 
363
 
 
364
        flows = flowable.Flow(self.element, self.parent)
 
365
        flows.process()
 
366
 
 
367
        canvas = attr.getManager(self, interfaces.ICanvasManager).canvas
 
368
        for flow in flows.flow:
 
369
            flowWidth, flowHeight = flow.wrap(width, height)
 
370
            if flowWidth <= width and flowHeight <= height:
 
371
                y -= flowHeight
 
372
                flow.drawOn(canvas, x, y)
 
373
                height -= flowHeight
 
374
            else:
 
375
                raise ValueError("Not enough space")
 
376
 
 
377
 
 
378
class IParam(interfaces.IRMLDirectiveSignature):
 
379
    """Sets one paramter for the text annotation."""
 
380
 
 
381
    name = attr.String(
 
382
        title=u'Name',
 
383
        description=u'The name of the paramter.',
 
384
        required=True)
 
385
 
 
386
    value = attr.TextNode(
 
387
        title=u'Value',
 
388
        description=(u'The parameter value.'),
 
389
        required=True)
 
390
 
 
391
class Param(directive.RMLDirective):
 
392
    signature = IParam
 
393
 
 
394
    def process(self):
 
395
        args = dict(self.getAttributeValues())
 
396
        self.parent.params[args['name']] = args['value']
 
397
 
 
398
 
 
399
class ITextAnnotation(interfaces.IRMLDirectiveSignature):
 
400
    """Writes a low-level text annotation into the PDF."""
 
401
    occurence.containing(
 
402
        occurence.ZeroOrMore('param', IParam))
 
403
 
 
404
    contents = attr.FirstLevelTextNode(
 
405
        title=u'Contents',
 
406
        description=u'The PDF commands that are inserted as annotation.',
 
407
        required=True)
 
408
 
 
409
class TextAnnotation(CanvasRMLDirective):
 
410
    signature = ITextAnnotation
 
411
    factories = {'param': Param}
 
412
 
 
413
    paramTypes = {'escape': attr.Integer()}
 
414
 
 
415
    def process(self):
 
416
        contents = self.getAttributeValues(valuesOnly=True)[0]
 
417
        self.params = {}
 
418
        self.processSubDirectives()
 
419
        for name, type in self.paramTypes.items():
 
420
            if name in self.params:
 
421
                bound = type.bind(self)
 
422
                self.params[name] = bound.fromUnicode(self.params[name])
 
423
        canvas = attr.getManager(self, interfaces.ICanvasManager).canvas
 
424
        canvas.textAnnotation(contents, **self.params)
 
425
 
 
426
 
 
427
class IMoveTo(interfaces.IRMLDirectiveSignature):
 
428
    """Move the path cursor to the specified location."""
 
429
 
 
430
    position = attr.TextNodeSequence(
 
431
        title=u'Position',
 
432
        description=u'Position to which the path pointer is moved to.',
 
433
        value_type=attr.Measurement(),
 
434
        min_length=2,
 
435
        max_length=2,
 
436
        required=True)
 
437
 
 
438
class MoveTo(directive.RMLDirective):
 
439
    signature = IMoveTo
 
440
 
 
441
    def process(self):
 
442
        args = self.getAttributeValues(valuesOnly=True)
 
443
        self.parent.path.moveTo(*args[0])
 
444
 
 
445
 
 
446
class ICurveTo(interfaces.IRMLDirectiveSignature):
 
447
    """Create a bezier curve from the current location to the specified one."""
 
448
 
 
449
    curvelist = attr.TextNodeGrid(
 
450
        title=u'Curve Specification',
 
451
        description=u'Describes the end position and the curve properties.',
 
452
        value_type=attr.Measurement(),
 
453
        columns=6,
 
454
        required=True)
 
455
 
 
456
class CurveTo(directive.RMLDirective):
 
457
    signature = ICurveTo
 
458
 
 
459
    def process(self):
 
460
        argset = self.getAttributeValues(valuesOnly=True)[0]
 
461
        for args in argset:
 
462
            self.parent.path.curveTo(*args)
 
463
 
 
464
class ICurvesTo(ICurveTo):
 
465
    pass
 
466
directive.DeprecatedDirective(
 
467
    ICurvesTo,
 
468
    'Available for ReportLab RML compatibility. Please use the "curveto" '
 
469
    'directive instead.')
 
470
 
 
471
 
 
472
class IPath(IShape):
 
473
    """Create a line path."""
 
474
    occurence.containing(
 
475
        occurence.ZeroOrMore('moveto', IMoveTo),
 
476
        occurence.ZeroOrMore('curveto', ICurveTo),
 
477
        occurence.ZeroOrMore('curvesto', ICurvesTo),
 
478
        )
 
479
 
 
480
    points = attr.TextNodeGrid(
 
481
        title=u'Points',
 
482
        description=(u'A list of coordinate points that define th path.'),
 
483
        value_type=attr.Measurement(),
 
484
        columns=2,
 
485
        required=True)
 
486
 
 
487
    close = attr.Boolean(
 
488
        title=u'Close Path',
 
489
        description=(u"A flag specifying whether the path should be closed."),
 
490
        default=False,
 
491
        required=False)
 
492
 
 
493
class Path(CanvasRMLDirective):
 
494
    signature = IPath
 
495
    factories = {
 
496
        'moveto': MoveTo,
 
497
        'curveto': CurveTo,
 
498
        'curvesto': CurveTo
 
499
        }
 
500
 
 
501
    def processPoints(self, text):
 
502
        if text.strip() == '':
 
503
            return
 
504
        bound = self.signature['points'].bind(self)
 
505
        for coords in bound.fromUnicode(text):
 
506
            self.path.lineTo(*coords)
 
507
 
 
508
    def process(self):
 
509
        kwargs = dict(self.getAttributeValues(ignore=('points',)))
 
510
 
 
511
        # Start the path and set the cursor to the start location.
 
512
        canvas = attr.getManager(self, interfaces.ICanvasManager).canvas
 
513
        self.path = canvas.beginPath()
 
514
        self.path.moveTo(kwargs.pop('x'), kwargs.pop('y'))
 
515
 
 
516
        # Process the text before the first sub-directive.
 
517
        if self.element.text is not None:
 
518
            self.processPoints(self.element.text)
 
519
        # Handle each sub-directive.
 
520
        for directive in self.element.getchildren():
 
521
            if directive.tag in self.factories:
 
522
                self.factories[directive.tag](directive, self).process()
 
523
            # If there is more text after sub-directive, process it.
 
524
            if directive.tail is not None:
 
525
                self.processPoints(directive.tail)
 
526
 
 
527
        if kwargs.pop('close', False):
 
528
            self.path.close()
 
529
 
 
530
        canvas.drawPath(self.path, **kwargs)
 
531
 
 
532
 
 
533
class IFill(interfaces.IRMLDirectiveSignature):
 
534
    """Set the fill color."""
 
535
 
 
536
    color = attr.Color(
 
537
        title=u'Color',
 
538
        description=(u'The color value to be set.'),
 
539
        required=True)
 
540
 
 
541
class Fill(CanvasRMLDirective):
 
542
    signature = IFill
 
543
    callable = 'setFillColor'
 
544
    attrMapping = {'color': 'aColor'}
 
545
 
 
546
 
 
547
class IStroke(interfaces.IRMLDirectiveSignature):
 
548
    """Set the stroke/line color."""
 
549
 
 
550
    color = attr.Color(
 
551
        title=u'Color',
 
552
        description=(u'The color value to be set.'),
 
553
        required=True)
 
554
 
 
555
class Stroke(CanvasRMLDirective):
 
556
    signature = IStroke
 
557
    callable = 'setStrokeColor'
 
558
    attrMapping = {'color': 'aColor'}
 
559
 
 
560
 
 
561
class ISetFont(interfaces.IRMLDirectiveSignature):
 
562
    """Set the font name and/or size."""
 
563
 
 
564
    name = attr.String(
 
565
        title=u'Font Name',
 
566
        description=(u'The name of the font as it was registered.'),
 
567
        required=True)
 
568
 
 
569
    size = attr.Measurement(
 
570
        title=u'Size',
 
571
        description=(u'The font size.'),
 
572
        required=True)
 
573
 
 
574
    leading = attr.Measurement(
 
575
        title=u'Leading',
 
576
        description=(u'The font leading.'),
 
577
        required=False)
 
578
 
 
579
class SetFont(CanvasRMLDirective):
 
580
    signature = ISetFont
 
581
    callable = 'setFont'
 
582
    attrMapping = {'name': 'psfontname'}
 
583
 
 
584
 
 
585
class IScale(interfaces.IRMLDirectiveSignature):
 
586
    """Scale the drawing using x and y scaling factors."""
 
587
 
 
588
    sx = attr.Float(
 
589
        title=u'X-Scaling-Factor',
 
590
        description=(u'The scaling factor applied on x-coordinates.'),
 
591
        required=True)
 
592
 
 
593
    sy = attr.Float(
 
594
        title=u'Y-Scaling-Factor',
 
595
        description=(u'The scaling factor applied on y-coordinates.'),
 
596
        required=True)
 
597
 
 
598
class Scale(CanvasRMLDirective):
 
599
    signature = IScale
 
600
    callable = 'scale'
 
601
    attrMapping = {'sx': 'x', 'sy': 'y'}
 
602
 
 
603
 
 
604
class ITranslate(interfaces.IRMLDirectiveSignature):
 
605
    """Translate the drawing coordinates by the specified x and y offset."""
 
606
 
 
607
    dx = attr.Measurement(
 
608
        title=u'X-Offset',
 
609
        description=(u'The amount to move the drawing to the right.'),
 
610
        required=True)
 
611
 
 
612
    dy = attr.Measurement(
 
613
        title=u'Y-Offset',
 
614
        description=(u'The amount to move the drawing upward.'),
 
615
        required=True)
 
616
 
 
617
class Translate(CanvasRMLDirective):
 
618
    signature = ITranslate
 
619
    callable = 'translate'
 
620
 
 
621
 
 
622
class IRotate(interfaces.IRMLDirectiveSignature):
 
623
    """Rotate the drawing counterclockwise."""
 
624
 
 
625
    degrees = attr.Measurement(
 
626
        title=u'Angle',
 
627
        description=(u'The angle in degrees.'),
 
628
        required=True)
 
629
 
 
630
class Rotate(CanvasRMLDirective):
 
631
    signature = IRotate
 
632
    callable = 'rotate'
 
633
    attrMapping = {'degrees': 'theta'}
 
634
 
 
635
 
 
636
class ISkew(interfaces.IRMLDirectiveSignature):
 
637
    """Skew the drawing."""
 
638
 
 
639
    alpha = attr.Measurement(
 
640
        title=u'Alpha',
 
641
        description=(u'The amount to skew the drawing in the horizontal.'),
 
642
        required=True)
 
643
 
 
644
    beta = attr.Measurement(
 
645
        title=u'Beta',
 
646
        description=(u'The amount to skew the drawing in the vertical.'),
 
647
        required=True)
 
648
 
 
649
class Skew(CanvasRMLDirective):
 
650
    signature = ISkew
 
651
    callable = 'skew'
 
652
 
 
653
 
 
654
class ITransform(interfaces.IRMLDirectiveSignature):
 
655
    """A full 2-D matrix transformation"""
 
656
 
 
657
    matrix = attr.TextNodeSequence(
 
658
        title=u'Matrix',
 
659
        description=u'The transformation matrix.',
 
660
        value_type=attr.Float(),
 
661
        min_length=6,
 
662
        max_length=6,
 
663
        required=True)
 
664
 
 
665
class Transform(CanvasRMLDirective):
 
666
    signature = ITransform
 
667
 
 
668
    def process(self):
 
669
        args = self.getAttributeValues(valuesOnly=True)
 
670
        canvas = attr.getManager(self, interfaces.ICanvasManager).canvas
 
671
        canvas.transform(*args[0])
 
672
 
 
673
 
 
674
class ILineMode(interfaces.IRMLDirectiveSignature):
 
675
    """Set the line mode for the following graphics elements."""
 
676
 
 
677
    width = attr.Measurement(
 
678
        title=u'Width',
 
679
        description=(u'The line width.'),
 
680
        required=False)
 
681
 
 
682
    dash = attr.Sequence(
 
683
        title=u'Dash-Pattern',
 
684
        description=(u'The dash-pattern of a line.'),
 
685
        value_type=attr.Measurement(),
 
686
        required=False)
 
687
 
 
688
    miterLimit = attr.Measurement(
 
689
        title=u'Miter Limit',
 
690
        description=(u'The ???.'),
 
691
        required=False)
 
692
 
 
693
    join = attr.Choice(
 
694
        title=u'Join',
 
695
        description=u'The way lines are joined together.',
 
696
        choices=interfaces.JOIN_CHOICES,
 
697
        required=False)
 
698
 
 
699
    cap = attr.Choice(
 
700
        title=u'Cap',
 
701
        description=u'The cap is the desciption of how the line-endings look.',
 
702
        choices=interfaces.CAP_CHOICES,
 
703
        required=False)
 
704
 
 
705
class LineMode(CanvasRMLDirective):
 
706
    signature = ILineMode
 
707
 
 
708
    def process(self):
 
709
        kw = dict(self.getAttributeValues())
 
710
        canvas = attr.getManager(self, interfaces.ICanvasManager).canvas
 
711
        if 'width' in kw:
 
712
            canvas.setLineWidth(kw['width'])
 
713
        if 'join' in kw:
 
714
            canvas.setLineJoin(kw['join'])
 
715
        if 'cap' in kw:
 
716
            canvas.setLineCap(kw['cap'])
 
717
        if 'miterLimit' in kw:
 
718
            canvas.setMiterLimit(kw['miterLimit'])
 
719
        if 'dash' in kw:
 
720
            canvas.setDash(kw['dash'])
 
721
 
 
722
 
 
723
class IDrawing(interfaces.IRMLDirectiveSignature):
 
724
    """A container directive for all directives that draw directly on the
 
725
    cnavas."""
 
726
    occurence.containing(
 
727
        occurence.ZeroOrMore('drawString', IDrawString),
 
728
        occurence.ZeroOrMore('drawRightString', IDrawRightString),
 
729
        occurence.ZeroOrMore('drawCenteredString', IDrawCenteredString),
 
730
        occurence.ZeroOrMore('drawCentredString', IDrawCenteredString),
 
731
        occurence.ZeroOrMore('drawAlignedString', IDrawAlignedString),
 
732
        # Drawing Operations
 
733
        occurence.ZeroOrMore('ellipse', IEllipse),
 
734
        occurence.ZeroOrMore('circle', ICircle),
 
735
        occurence.ZeroOrMore('rect', IRectangle),
 
736
        occurence.ZeroOrMore('grid', IGrid),
 
737
        occurence.ZeroOrMore('lines', ILines),
 
738
        occurence.ZeroOrMore('curves', ICurves),
 
739
        occurence.ZeroOrMore('image', IImage),
 
740
        occurence.ZeroOrMore('place', IPlace),
 
741
        occurence.ZeroOrMore('textAnnotation', ITextAnnotation),
 
742
        occurence.ZeroOrMore('path', IPath),
 
743
        # State Change Operations
 
744
        occurence.ZeroOrMore('fill', IFill),
 
745
        occurence.ZeroOrMore('stroke', IStroke),
 
746
        occurence.ZeroOrMore('setFont', ISetFont),
 
747
        occurence.ZeroOrMore('scale', IScale),
 
748
        occurence.ZeroOrMore('translate', ITranslate),
 
749
        occurence.ZeroOrMore('rotate', IRotate),
 
750
        occurence.ZeroOrMore('skew', ISkew),
 
751
        occurence.ZeroOrMore('transform', ITransform),
 
752
        occurence.ZeroOrMore('lineMode', ILineMode),
 
753
        # Form Field Elements
 
754
        occurence.ZeroOrMore('barCode', form.IBarCode),
 
755
        occurence.ZeroOrMore('textField', form.ITextField),
 
756
        occurence.ZeroOrMore('buttonField', form.IButtonField),
 
757
        occurence.ZeroOrMore('selectField', form.ISelectField),
 
758
        # Charts
 
759
        occurence.ZeroOrMore('barChart', chart.IBarChart),
 
760
        occurence.ZeroOrMore('barChart3D', chart.IBarChart3D),
 
761
        occurence.ZeroOrMore('linePlot', chart.ILinePlot),
 
762
        occurence.ZeroOrMore('linePlot3D', chart.ILinePlot3D),
 
763
        occurence.ZeroOrMore('pieChart', chart.IPieChart),
 
764
        occurence.ZeroOrMore('pieChart3D', chart.IPieChart3D),
 
765
        occurence.ZeroOrMore('spiderChart', chart.ISpiderChart),
 
766
        )
 
767
 
 
768
class Drawing(directive.RMLDirective):
 
769
    signature = IDrawing
 
770
 
 
771
    factories = {
 
772
        'drawString': DrawString,
 
773
        'drawRightString': DrawRightString,
 
774
        'drawCenteredString': DrawCenteredString,
 
775
        'drawCentredString': DrawCenteredString,
 
776
        'drawAlignedString': DrawAlignedString,
 
777
        # Drawing Operations
 
778
        'ellipse': Ellipse,
 
779
        'circle': Circle,
 
780
        'rect': Rectangle,
 
781
        'grid': Grid,
 
782
        'lines': Lines,
 
783
        'curves': Curves,
 
784
        'image': Image,
 
785
        'place': Place,
 
786
        'textAnnotation': TextAnnotation,
 
787
        'path': Path,
 
788
        # Form Field Elements
 
789
        'barCode': form.BarCode,
 
790
        'textField': form.TextField,
 
791
        'buttonField': form.ButtonField,
 
792
        'selectField': form.SelectField,
 
793
        # State Change Operations
 
794
        'fill': Fill,
 
795
        'stroke': Stroke,
 
796
        'setFont': SetFont,
 
797
        'scale': Scale,
 
798
        'translate': Translate,
 
799
        'rotate': Rotate,
 
800
        'skew': Skew,
 
801
        'transform': Transform,
 
802
        'lineMode': LineMode,
 
803
        # Charts
 
804
        'barChart': chart.BarChart,
 
805
        'barChart3D': chart.BarChart3D,
 
806
        'linePlot': chart.LinePlot,
 
807
        'linePlot3D': chart.LinePlot3D,
 
808
        'pieChart': chart.PieChart,
 
809
        'pieChart3D': chart.PieChart3D,
 
810
        'spiderChart': chart.SpiderChart
 
811
        }
 
812
 
 
813
 
 
814
class IPageDrawing(IDrawing):
 
815
    """Draws directly on the content of one page's canvas. Every call of this
 
816
    directive creates a new page."""
 
817
 
 
818
    occurence.containing(
 
819
        #'mergePage': IMergePage,
 
820
        *IDrawing.getTaggedValue('directives'))
 
821
 
 
822
class PageDrawing(Drawing):
 
823
    signature = IDrawing
 
824
 
 
825
    factories = Drawing.factories.copy()
 
826
    factories.update({
 
827
        'mergePage': page.MergePage
 
828
        })
 
829
 
 
830
    def process(self):
 
831
        super(Drawing, self).process()
 
832
        canvas = attr.getManager(self, interfaces.ICanvasManager).canvas
 
833
        canvas.showPage()
 
834
 
 
835
 
 
836
class IPageInfo(interfaces.IRMLDirectiveSignature):
 
837
    """Set's up page-global settings."""
 
838
 
 
839
    pageSize = attr.PageSize(
 
840
        title=u'Page Size',
 
841
        description=(u'The page size of all pages within this document.'),
 
842
        required=True)
 
843
 
 
844
class PageInfo(CanvasRMLDirective):
 
845
    signature=IPageInfo
 
846
    callable = 'setPageSize'
 
847
    attrMapping = {'pageSize': 'size'}