~ubuntu-branches/ubuntu/jaunty/python-docutils/jaunty

« back to all changes in this revision

Viewing changes to docutils/parsers/rst/directives/images.py

  • Committer: Bazaar Package Importer
  • Author(s): Simon McVittie
  • Date: 2008-07-24 10:39:53 UTC
  • mfrom: (1.1.4 upstream) (3.1.7 intrepid)
  • Revision ID: james.westby@ubuntu.com-20080724103953-8gh4uezg17g9ysgy
Tags: 0.5-2
* Upload docutils 0.5 to unstable
* Update rst.el to upstream Subversion r5596, which apparently fixes
  all its performance problems (17_speed_up_rst_el.dpatch, closes: #474941)

Show diffs side-by-side

added added

removed removed

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