#! /Library/Frameworks/Python.framework/Versions/2.7/bin/python
# $Id$
import sys, copy, os, ConfigParser, pprint, types
from docutils_fo_dicts import *
from xml.sax import saxutils
class FOConfigFileException(Exception):
pass
def dump(object):
pp = pprint.PrettyPrinter(indent=4)
sys.stderr.write(pp.pformat(object))
sys.stderr.write('\n')
class Dump:
def __init__(self):
pass
class WriteStylesheet:
def __init__(self, verbose = 0):
self.__verbose = verbose
self.__string = ''
def __write_start_element(self, name, atts):
pass
def __write_end_element(self, name):
pass
def __write_root_start(self):
self.__string += """\n\n"""
pass
def __write_root_end(self):
self.__string +=''
def __write_att_sets(self):
self.__string += '\n \n\n'
att_sets = self.__att_sets.keys()
for att_set in att_sets:
self.__string += ' \n' % (att_set)
att_dict = self.__att_sets[att_set]
atts = att_dict.keys()
for att in atts:
self.__string += ' ' % (att)
self.__string += saxutils.escape(att_dict[att])
self.__string += '\n'
self.__string += ' \n\n'
def __write_import(self):
self.__string += """ \n\n""" % (self.__import_ss)
def __write_params(self):
self.__string += '\n \n\n'
the_keys = self.__params.keys()
the_keys.sort()
for the_key in the_keys:
value = saxutils.escape(self.__params[the_key])
self.__string += ' %s\n' % (the_key, value)
self.__string += '\n\n'
def write_stylesheet(self, import_ss, params, att_sets, out=None):
self.__import_ss = import_ss
self.__params = params
self.__att_sets = att_sets
self.__out = out
self.__write_root_start()
self.__write_import()
self.__write_params()
self.__write_att_sets()
self.__write_root_end()
return self.__string
class PostProcess:
def __init__(self, params, attribute_sets):
self.__attribute_sets = attribute_sets
self.__params = params
# not used
def __test_measure(self, the_string):
"""
test if string is a measure:
12pt returns 12, pt
1.5 returns None, None
"""
accept_units = ['em', 'px', 'in', 'cm', 'mm', 'pt', 'pc']
try:
float(the_string)
return None, None
except ValueError:
pass
if len(the_string) < 3:
return None, None
unit = the_string[-2:]
if unit not in accept_units:
return None, None
num = the_string[:-2]
try:
num = float(num)
except ValueError:
return None, None
return num, unit
# not used
def __get_default_font_size(self):
document_att_set = self.__attribute_sets.get('default-page-sequence')
body_att_set = self.__attribute_sets.get('default-flow')
if document_att_set and document_att_set.get('font-size'):
default_font_size = document_att_set.get('font-size')
elif body_att_set and document_att_set.get('font-size'):
default_font_size = body_att_set.get('font-size')
else:
default_font_size = '12pt'
num, unit = self.__test_measure(default_font_size)
if not num:
default_font_size = '12pt'
self.__default_font_size = default_font_size
def __fix_header_footer(self):
# have to set the param spacing-header to extent, if not already set
#custom-spacing-header-footer
header_att_set = self.__attribute_sets.get('header-region-before')
if header_att_set:
extent = header_att_set.get('extent')
if extent and not self.__params.get('spacing-header'):
# self.__params['spacing-header'] = extent
self.__params['custom-spacing-header-footer'] = 'yes'
footer_att_set = self.__attribute_sets.get('footer-region-after')
if footer_att_set:
extent = footer_att_set.get('extent')
if extent and not self.__params.get('spacing-footer'):
# self.__params['spacing-footer'] = extent
self.__params['custom-spacing-header-footer'] = 'yes'
def __fix_title(self):
doc_tit_att_set = self.__attribute_sets.get('document-title-page-block')
if doc_tit_att_set:
space_before = doc_tit_att_set.get('space-before')
if space_before:
self.__attribute_sets['document-title-page-block']['space-before.conditionality'] = 'retain'
def __get_page_layout(self):
odd_page = self.__attribute_sets.get('odd-simple-page-master')
even_page = self.__attribute_sets.get('even-simple-page-master')
first_page = self.__attribute_sets.get('first-simple-page-master')
suppress_first_header = self.__params.get('suppress-first-page-header')
suppress_first_footer = self.__params.get('suppress-first-page-footer')
need_odd_or_even_page = False
if odd_page or even_page:
need_odd_or_even_page = True
need_first_page = False
# if suppress_first_footer or suppress_first_header or first_page:
# need_first_page = True
if self.__params.get('page-layout'):
page_layout = self.__params.get('page-layout')
elif need_first_page and need_odd_or_even_page:
page_layout = 'first-odd-even'
elif need_first_page:
page_layout = 'first'
elif need_odd_or_even_page:
page_layout = 'odd-even'
else:
page_layout = ''
self.__params['page-layout'] = page_layout
def post_process(self):
self.__get_default_font_size()
self.__fix_header_footer()
self.__get_page_layout()
self. __fix_title()
return self.__attribute_sets, self.__params
class ReadConfig:
def __init__(self, import_ss = None, verbose = 0, config_file = None):
self.__verbose = verbose
if self.__verbose > 4:
sys.stderr.write('modules is "%s"\n' % __file__)
self.__attribute_sets = {}
self.__params = {}
self.__import_ss = import_ss
if not self.__import_ss:
self.__import_ss = os.path.join(os.path.dirname(__file__), 'xsl_fo','docutils_to_fo.xsl')
if os.sep != '/':
self.__correct_path = self.__correct_path(self.__import_ss)
if not os.path.isfile(self.__import_ss):
msg = '"%s" cannot be found\n' % (self.__import_ss)
raise FOConfigFileException(msg)
if self.__verbose > 3:
sys.stderr.write('self.__import_ss (stylesheet to import) is "%s" \n' % self.__import_ss)
self.__config_file = config_file
if self.__verbose > 3 and self.__config_file:
sys.stderr.write('self.__config_file is "%s" \n' % self.__config_file)
def __correct_path(self, the_path):
"""
make sure the_path is os.path.abs(the_path) and that
the_path really exits or this may not work
"""
paths = []
head = None
counter = 1
while 1:
counter += 1
if not head:
head = the_path
head, tail = os.path.split(head)
paths.insert(0,tail)
if not tail:
break
if counter > 100:
raise RuntimeError, 'max num recursions (100) reached'
return '/'.join(paths)
def write_config_file(self, dest=None):
w = WriteStylesheet()
ss_string = w.write_stylesheet(import_ss = self.__import_ss, params = self.__params,
att_sets = self.__attribute_sets)
return ss_string
def read_config_files(self):
config = ConfigParser.SafeConfigParser()
config_files = []
if self.__config_file:
config.read(self.__config_file)
return config
if os.environ.get('HOME'):
config_files.append(os.path.join(os.environ.get('HOME'), '.docutils'))
config_files.append(os.path.join(os.getcwd(), 'docutils.conf'))
for the_path in config_files:
config.read(the_path)
if self.__verbose > 4:
sys.stderr.write('config is: \n' )
dump(config.items('FO'))
return config
def parse_config_files(self):
config = self.read_config_files()
self.__config = config
if not 'FO' in config.sections():
return
opts = config.items('FO')
opts_dict = {}
for pair_tupple in opts:
first = pair_tupple[0]
second = pair_tupple[1]
fields = first.split('.', 1)
if prop_as_param_dict.get(first):
self.__handle_param(prop_as_param_dict.get(first), second)
elif len(fields) == 2:
self.__handle_attributes(fields[0], fields[1], second)
elif first in param_list:
self.__handle_param(first, second)
elif first in commands_list:
pass
else:
self.__error('"%s" = "%s" not a valid config option\n' % (first, second))
def __post_process(self):
post_process_obj = PostProcess(attribute_sets = self.__attribute_sets, params = self.__params)
self.__attribute_sets, self.__params = post_process_obj.post_process()
if self.__verbose > 4:
sys.stderr.write('self.__attribute_sets after post process:\n')
dump(self.__attribute_sets)
sys.stderr.write('self.__params after post process:\n')
dump(self.__params)
sys.stderr.write('\n')
def __get_shortcut_att_set(self, user_att_set):
format = self.__get_config_option(option = 'option-list.format', default = 'list')
att_set = short_cut_att_sets.get(user_att_set)
if att_set:
return att_set
att_set = short_cut_att_sets2.get((user_att_set, format))
if att_set:
return att_set
elif att_set == None:
return user_att_set
def __get_special_set_att(self, user_att_set, user_att):
format = self.__get_config_option(option = 'option-list.format', default = 'list')
spc_att_set_att_list = special_att_set_att_dict.get((user_att_set, user_att))
if spc_att_set_att_list:
return spc_att_set_att_list
spc_att_set_att_list = special_att_set_att_dict2.get((user_att_set, user_att, format))
if spc_att_set_att_list:
return spc_att_set_att_list
def __handle_attributes(self, user_att_set, user_att, value, check_special = True, s=None):
if special_att_sets_dict.get(user_att_set) and check_special:
self.__handle_special_atts(user_att_set, user_att, value)
return
# change both att-set and att
spc_att_set_att_list = self.__get_special_set_att(user_att_set, user_att)
if spc_att_set_att_list and check_special:
for new_pair in spc_att_set_att_list:
att_set = new_pair[0]
att = new_pair[1]
self.__add_attribute(att_set, att, value )
return
att_set = self.__get_shortcut_att_set(user_att_set)
fo_element = att_set_dict.get(att_set)
spc_att_val_list = special_att_value_dict.get((user_att, value))
if spc_att_val_list and fo_element:
for new_pair in spc_att_val_list:
att = new_pair[0]
value = new_pair[1]
self.__add_attribute(att_set, att, value )
return
elif fo_element: # found a valid att-set
att = custom_atts.get(user_att)
if not att: # valid attriubute, according to FO standards
att = user_att
if att not in which_list.get(fo_element):
self.__error('%s not a valid value for att-set %s' % (user_att, user_att_set))
else:
self.__add_attribute(att_set, att, value )
else:
self.__error('%s not a valid attribute-set' % (user_att_set))
def __check_value(self, att, value):
special = special_values_dict.get(value)
if special:
if special[0] == att:
return special[1]
else:
return value
else:
return value
def __add_attribute(self, att_set, att, value):
att_exists = self.__attribute_sets.get(att_set)
if not att_exists:
self.__attribute_sets[att_set] = {}
value = self.__check_value(att, value)
self.__attribute_sets[att_set][att] = value
def __error(self, msg):
# sys.stderr.write(msg)
# sys.stderr.write('\n')
raise FOConfigFileException(msg)
def __get_config_option(self, option, section = 'FO', default = None):
try:
option = self.__config.get('FO', 'option-list.format')
return option
except ConfigParser.NoOptionError:
return default
def __handle_special_atts(self, user_att_set, user_att, value):
# for opt list as list
att_list_dict = {'space-from-option_off':'provisional-distance-between-starts'}
# for opt list as def
att_def_dict = {'':''}
att_def_not_allowed = ['space-from-option_', 'space-from-label_']
if user_att_set == 'option-list':
format = self.__get_config_option(option = 'option-list.format', default = 'list')
if format == 'list':
changed_att = att_list_dict.get(user_att)
if changed_att:
user_att = changed_att
self.__handle_attributes('option-list', user_att, value, check_special = False)
elif format == 'definition':
if user_att in att_def_not_allowed:
self.__error('%s.%s = %s not a valid attribute property\n' %
(user_att_set, user_att, value))
self.__handle_attributes('option-list-definition-block',
user_att, value, check_special = False)
else:
self.__error('%s.%s = %s not a valid attribute property\n' % (user_att_set, user_att, value))
def __handle_param(self, param, value):
to_test_dict = param_dict_test.get(param)
if to_test_dict:
correct_value = to_test_dict.get(value)
if correct_value:
self.__params[param] = correct_value
else:
self.__error('%s = %s not a valid command\n' % (param, value))
else:
self.__params[param] = value
def make_stylesheet(self):
self.parse_config_files()
self.__post_process()
ss_string = self.write_config_file()
return ss_string
# self.print_att_list()
if __name__ == '__main__':
read_config_obj = ReadConfig(import_ss = '/Users/cynthia/tmp/paultremblay/xsl_fo/docutils_to_fo.xsl' )
ss_string = read_config_obj.main()
print ss_string