1
# $Id: images.py 7510 2012-09-12 07:22:25Z milde $
2
# Author: David Goodger <goodger@python.org>
3
# Copyright: This module has been placed in the public domain.
6
Directives for figures and simple images.
9
__docformat__ = 'reStructuredText'
14
from docutils import nodes, utils
15
from docutils.parsers.rst import Directive
16
from docutils.parsers.rst import directives, states
17
from docutils.nodes import fully_normalize_name, whitespace_normalize_name
18
from docutils.parsers.rst.roles import set_classes
19
try: # check for the Python Imaging Library
22
try: # sometimes PIL modules are put in PYTHONPATH's root
24
class PIL(object): pass # dummy wrapper
29
class Image(Directive):
31
align_h_values = ('left', 'center', 'right')
32
align_v_values = ('top', 'middle', 'bottom')
33
align_values = align_v_values + align_h_values
36
# This is not callable as self.align. We cannot make it a
37
# staticmethod because we're saving an unbound method in
39
return directives.choice(argument, Image.align_values)
41
required_arguments = 1
42
optional_arguments = 0
43
final_argument_whitespace = True
44
option_spec = {'alt': directives.unchanged,
45
'height': directives.length_or_unitless,
46
'width': directives.length_or_percentage_or_unitless,
47
'scale': directives.percentage,
49
'name': directives.unchanged,
50
'target': directives.unchanged_required,
51
'class': directives.class_option}
54
if 'align' in self.options:
55
if isinstance(self.state, states.SubstitutionDef):
56
# Check for align_v_values.
57
if self.options['align'] not in self.align_v_values:
59
'Error in "%s" directive: "%s" is not a valid value '
60
'for the "align" option within a substitution '
61
'definition. Valid values for "align" are: "%s".'
62
% (self.name, self.options['align'],
63
'", "'.join(self.align_v_values)))
64
elif self.options['align'] not in self.align_h_values:
66
'Error in "%s" directive: "%s" is not a valid value for '
67
'the "align" option. Valid values for "align" are: "%s".'
68
% (self.name, self.options['align'],
69
'", "'.join(self.align_h_values)))
71
reference = directives.uri(self.arguments[0])
72
self.options['uri'] = reference
74
if 'target' in self.options:
75
block = states.escape2null(
76
self.options['target']).splitlines()
77
block = [line for line in block]
78
target_type, data = self.state.parse_target(
79
block, self.block_text, self.lineno)
80
if target_type == 'refuri':
81
reference_node = nodes.reference(refuri=data)
82
elif target_type == 'refname':
83
reference_node = nodes.reference(
84
refname=fully_normalize_name(data),
85
name=whitespace_normalize_name(data))
86
reference_node.indirect_reference_name = data
87
self.state.document.note_refname(reference_node)
88
else: # malformed target
89
messages.append(data) # data is a system message
90
del self.options['target']
91
set_classes(self.options)
92
image_node = nodes.image(self.block_text, **self.options)
93
self.add_name(image_node)
95
reference_node += image_node
96
return messages + [reference_node]
98
return messages + [image_node]
104
return directives.choice(argument, Figure.align_h_values)
106
def figwidth_value(argument):
107
if argument.lower() == 'image':
110
return directives.length_or_percentage_or_unitless(argument, 'px')
112
option_spec = Image.option_spec.copy()
113
option_spec['figwidth'] = figwidth_value
114
option_spec['figclass'] = directives.class_option
115
option_spec['align'] = align
119
figwidth = self.options.pop('figwidth', None)
120
figclasses = self.options.pop('figclass', None)
121
align = self.options.pop('align', None)
122
(image_node,) = Image.run(self)
123
if isinstance(image_node, nodes.system_message):
125
figure_node = nodes.figure('', image_node)
126
if figwidth == 'image':
127
if PIL and self.state.document.settings.file_insertion_enabled:
128
imagepath = urllib.url2pathname(image_node['uri'])
130
img = PIL.Image.open(
131
imagepath.encode(sys.getfilesystemencoding()))
132
except (IOError, UnicodeEncodeError):
135
self.state.document.settings.record_dependencies.add(
136
imagepath.replace('\\', '/'))
137
figure_node['width'] = img.size[0]
139
elif figwidth is not None:
140
figure_node['width'] = figwidth
142
figure_node['classes'] += figclasses
144
figure_node['align'] = align
146
node = nodes.Element() # anonymous container for parsing
147
self.state.nested_parse(self.content, self.content_offset, node)
149
if isinstance(first_node, nodes.paragraph):
150
caption = nodes.caption(first_node.rawsource, '',
151
*first_node.children)
152
figure_node += caption
153
elif not (isinstance(first_node, nodes.comment)
154
and len(first_node) == 0):
155
error = self.state_machine.reporter.error(
156
'Figure caption must be a paragraph or empty comment.',
157
nodes.literal_block(self.block_text, self.block_text),
159
return [figure_node, error]
161
figure_node += nodes.legend('', *node[1:])