179
|
1 |
# $Id: images.py 4667 2006-07-12 21:40:56Z wiemann $
|
|
2 |
# Author: David Goodger <goodger@python.org>
|
|
3 |
# Copyright: This module has been placed in the public domain.
|
|
4 |
|
|
5 |
"""
|
|
6 |
Directives for figures and simple images.
|
|
7 |
"""
|
|
8 |
|
|
9 |
__docformat__ = 'reStructuredText'
|
|
10 |
|
|
11 |
|
|
12 |
import sys
|
|
13 |
from docutils import nodes, utils
|
|
14 |
from docutils.parsers.rst import Directive
|
|
15 |
from docutils.parsers.rst import directives, states
|
|
16 |
from docutils.nodes import fully_normalize_name, whitespace_normalize_name
|
|
17 |
from docutils.parsers.rst.roles import set_classes
|
|
18 |
|
|
19 |
try:
|
|
20 |
import Image as PIL # PIL
|
|
21 |
except ImportError:
|
|
22 |
PIL = None
|
|
23 |
|
|
24 |
|
|
25 |
class Image(Directive):
|
|
26 |
|
|
27 |
align_h_values = ('left', 'center', 'right')
|
|
28 |
align_v_values = ('top', 'middle', 'bottom')
|
|
29 |
align_values = align_v_values + align_h_values
|
|
30 |
|
|
31 |
def align(argument):
|
|
32 |
# This is not callable as self.align. We cannot make it a
|
|
33 |
# staticmethod because we're saving an unbound method in
|
|
34 |
# option_spec below.
|
|
35 |
return directives.choice(argument, Image.align_values)
|
|
36 |
|
|
37 |
required_arguments = 1
|
|
38 |
optional_arguments = 0
|
|
39 |
final_argument_whitespace = True
|
|
40 |
option_spec = {'alt': directives.unchanged,
|
|
41 |
'height': directives.length_or_unitless,
|
|
42 |
'width': directives.length_or_percentage_or_unitless,
|
|
43 |
'scale': directives.nonnegative_int,
|
|
44 |
'align': align,
|
|
45 |
'target': directives.unchanged_required,
|
|
46 |
'class': directives.class_option}
|
|
47 |
|
|
48 |
def run(self):
|
|
49 |
if self.options.has_key('align'):
|
|
50 |
if isinstance(self.state, states.SubstitutionDef):
|
|
51 |
# Check for align_v_values.
|
|
52 |
if self.options['align'] not in self.align_v_values:
|
|
53 |
raise self.error(
|
|
54 |
'Error in "%s" directive: "%s" is not a valid value '
|
|
55 |
'for the "align" option within a substitution '
|
|
56 |
'definition. Valid values for "align" are: "%s".'
|
|
57 |
% (self.name, self.options['align'],
|
|
58 |
'", "'.join(self.align_v_values)))
|
|
59 |
elif self.options['align'] not in self.align_h_values:
|
|
60 |
raise self.error(
|
|
61 |
'Error in "%s" directive: "%s" is not a valid value for '
|
|
62 |
'the "align" option. Valid values for "align" are: "%s".'
|
|
63 |
% (self.name, self.options['align'],
|
|
64 |
'", "'.join(self.align_h_values)))
|
|
65 |
messages = []
|
|
66 |
reference = directives.uri(self.arguments[0])
|
|
67 |
self.options['uri'] = reference
|
|
68 |
reference_node = None
|
|
69 |
if self.options.has_key('target'):
|
|
70 |
block = states.escape2null(
|
|
71 |
self.options['target']).splitlines()
|
|
72 |
block = [line for line in block]
|
|
73 |
target_type, data = self.state.parse_target(
|
|
74 |
block, self.block_text, self.lineno)
|
|
75 |
if target_type == 'refuri':
|
|
76 |
reference_node = nodes.reference(refuri=data)
|
|
77 |
elif target_type == 'refname':
|
|
78 |
reference_node = nodes.reference(
|
|
79 |
refname=fully_normalize_name(data),
|
|
80 |
name=whitespace_normalize_name(data))
|
|
81 |
reference_node.indirect_reference_name = data
|
|
82 |
self.state.document.note_refname(reference_node)
|
|
83 |
else: # malformed target
|
|
84 |
messages.append(data) # data is a system message
|
|
85 |
del self.options['target']
|
|
86 |
set_classes(self.options)
|
|
87 |
image_node = nodes.image(self.block_text, **self.options)
|
|
88 |
if reference_node:
|
|
89 |
reference_node += image_node
|
|
90 |
return messages + [reference_node]
|
|
91 |
else:
|
|
92 |
return messages + [image_node]
|
|
93 |
|
|
94 |
|
|
95 |
class Figure(Image):
|
|
96 |
|
|
97 |
def align(argument):
|
|
98 |
return directives.choice(argument, Figure.align_h_values)
|
|
99 |
|
|
100 |
def figwidth_value(argument):
|
|
101 |
if argument.lower() == 'image':
|
|
102 |
return 'image'
|
|
103 |
else:
|
|
104 |
return directives.nonnegative_int(argument)
|
|
105 |
|
|
106 |
option_spec = Image.option_spec.copy()
|
|
107 |
option_spec['figwidth'] = figwidth_value
|
|
108 |
option_spec['figclass'] = directives.class_option
|
|
109 |
option_spec['align'] = align
|
|
110 |
has_content = True
|
|
111 |
|
|
112 |
def run(self):
|
|
113 |
figwidth = self.options.get('figwidth')
|
|
114 |
if figwidth:
|
|
115 |
del self.options['figwidth']
|
|
116 |
figclasses = self.options.get('figclass')
|
|
117 |
if figclasses:
|
|
118 |
del self.options['figclass']
|
|
119 |
align = self.options.get('align')
|
|
120 |
if align:
|
|
121 |
del self.options['align']
|
|
122 |
(image_node,) = Image.run(self)
|
|
123 |
if isinstance(image_node, nodes.system_message):
|
|
124 |
return [image_node]
|
|
125 |
figure_node = nodes.figure('', image_node)
|
|
126 |
if figwidth == 'image':
|
|
127 |
if PIL and self.state.document.settings.file_insertion_enabled:
|
|
128 |
# PIL doesn't like Unicode paths:
|
|
129 |
try:
|
|
130 |
i = PIL.open(str(image_node['uri']))
|
|
131 |
except (IOError, UnicodeError):
|
|
132 |
pass
|
|
133 |
else:
|
|
134 |
self.state.document.settings.record_dependencies.add(
|
|
135 |
image_node['uri'])
|
|
136 |
figure_node['width'] = i.size[0]
|
|
137 |
elif figwidth is not None:
|
|
138 |
figure_node['width'] = figwidth
|
|
139 |
if figclasses:
|
|
140 |
figure_node['classes'] += figclasses
|
|
141 |
if align:
|
|
142 |
figure_node['align'] = align
|
|
143 |
if self.content:
|
|
144 |
node = nodes.Element() # anonymous container for parsing
|
|
145 |
self.state.nested_parse(self.content, self.content_offset, node)
|
|
146 |
first_node = node[0]
|
|
147 |
if isinstance(first_node, nodes.paragraph):
|
|
148 |
caption = nodes.caption(first_node.rawsource, '',
|
|
149 |
*first_node.children)
|
|
150 |
figure_node += caption
|
|
151 |
elif not (isinstance(first_node, nodes.comment)
|
|
152 |
and len(first_node) == 0):
|
|
153 |
error = self.state_machine.reporter.error(
|
|
154 |
'Figure caption must be a paragraph or empty comment.',
|
|
155 |
nodes.literal_block(self.block_text, self.block_text),
|
|
156 |
line=self.lineno)
|
|
157 |
return [figure_node, error]
|
|
158 |
if len(node) > 1:
|
|
159 |
figure_node += nodes.legend('', *node[1:])
|
|
160 |
return [figure_node]
|