# Author: David Goodger # Contact: goodger@users.sourceforge.net # Revision: $Revision: 4234 $ # Date: $Date: 2005-12-29 02:14:21 +0100 (Thu, 29 Dec 2005) $ # Copyright: This module has been placed in the public domain. """ Directives for figures and simple images. """ __docformat__ = 'reStructuredText' import sys from docutils import nodes, utils from docutils.parsers.rst import directives, states from docutils.nodes import fully_normalize_name, whitespace_normalize_name from docutils.parsers.rst.roles import set_classes try: import Image # PIL except ImportError: Image = None align_h_values = ('left', 'center', 'right') align_v_values = ('top', 'middle', 'bottom') align_values = align_v_values + align_h_values def align(argument): return directives.choice(argument, align_values) def image(name, arguments, options, content, lineno, content_offset, block_text, state, state_machine): if options.has_key('align'): # check for align_v values only if isinstance(state, states.SubstitutionDef): if options['align'] not in align_v_values: error = state_machine.reporter.error( 'Error in "%s" directive: "%s" is not a valid value for ' 'the "align" option within a substitution definition. ' 'Valid values for "align" are: "%s".' % (name, options['align'], '", "'.join(align_v_values)), nodes.literal_block(block_text, block_text), line=lineno) return [error] elif options['align'] not in align_h_values: error = state_machine.reporter.error( 'Error in "%s" directive: "%s" is not a valid value for ' 'the "align" option. Valid values for "align" are: "%s".' % (name, options['align'], '", "'.join(align_h_values)), nodes.literal_block(block_text, block_text), line=lineno) return [error] messages = [] reference = directives.uri(arguments[0]) options['uri'] = reference reference_node = None if options.has_key('target'): block = states.escape2null(options['target']).splitlines() block = [line for line in block] target_type, data = state.parse_target(block, block_text, lineno) if target_type == 'refuri': reference_node = nodes.reference(refuri=data) elif target_type == 'refname': reference_node = nodes.reference( refname=fully_normalize_name(data), name=whitespace_normalize_name(data)) reference_node.indirect_reference_name = data state.document.note_refname(reference_node) else: # malformed target messages.append(data) # data is a system message del options['target'] set_classes(options) image_node = nodes.image(block_text, **options) if reference_node: reference_node += image_node return messages + [reference_node] else: return messages + [image_node] image.arguments = (1, 0, 1) image.options = {'alt': directives.unchanged, 'height': directives.length_or_unitless, 'width': directives.length_or_percentage_or_unitless, 'scale': directives.nonnegative_int, 'align': align, 'target': directives.unchanged_required, 'class': directives.class_option} def figure_align(argument): return directives.choice(argument, align_h_values) def figure(name, arguments, options, content, lineno, content_offset, block_text, state, state_machine): figwidth = options.get('figwidth') if figwidth: del options['figwidth'] figclasses = options.get('figclass') if figclasses: del options['figclass'] align = options.get('align') if align: del options['align'] (image_node,) = image(name, arguments, options, content, lineno, content_offset, block_text, state, state_machine) if isinstance(image_node, nodes.system_message): return [image_node] figure_node = nodes.figure('', image_node) if figwidth == 'image': if Image and state.document.settings.file_insertion_enabled: # PIL doesn't like Unicode paths: try: i = Image.open(str(image_node['uri'])) except (IOError, UnicodeError): pass else: state.document.settings.record_dependencies.add(image_node['uri']) figure_node['width'] = i.size[0] elif figwidth is not None: figure_node['width'] = figwidth if figclasses: figure_node['classes'] += figclasses if align: figure_node['align'] = align if content: node = nodes.Element() # anonymous container for parsing state.nested_parse(content, content_offset, node) first_node = node[0] if isinstance(first_node, nodes.paragraph): caption = nodes.caption(first_node.rawsource, '', *first_node.children) figure_node += caption elif not (isinstance(first_node, nodes.comment) and len(first_node) == 0): error = state_machine.reporter.error( 'Figure caption must be a paragraph or empty comment.', nodes.literal_block(block_text, block_text), line=lineno) return [figure_node, error] if len(node) > 1: figure_node += nodes.legend('', *node[1:]) return [figure_node] def figwidth_value(argument): if argument.lower() == 'image': return 'image' else: return directives.nonnegative_int(argument) figure.arguments = (1, 0, 1) figure.options = {'figwidth': figwidth_value, 'figclass': directives.class_option} figure.options.update(image.options) figure.options['align'] = figure_align figure.content = 1