'''
Adobe Indesign (Datasheet) Register Table and Hyperlink Generator
=================================================================
'''
import sys
import os
import xml.etree.ElementTree as ET
import csv
from PyICe import lab_utils
[docs]class inDesignOutput(object):
'''shared methods between XML conversion program and CSV conversion program'''
def __init__(self, source_revision="", source_date=""):
self.xml_declaration = '<?xml version="1.0" standalone="no"?>\n'
file_path = os.path.abspath(__file__)
dtd_path = "/".join(file_path.replace("\\", "/").split("/")[:-1] + ["link_inserter/register_map_InDesign.dtd"])
self.doctype = '<!DOCTYPE register_map SYSTEM "{}">\n'.format(dtd_path)
#ETree.register_namespace(prefix, uri)
self.table = ET.Element("register_map")
# self.table = ET.SubElement(self.xml_out, "Table", attrib={'content':'register_map'})
self.table.set("xmlns:aid", "http://ns.adobe.com/AdobeInDesign/4.0/")
# self.table.set("xmlns:aid5", "http://ns.adobe.com/AdobeInDesign/5.0/")
self.table.set("aid:table", "table")
# self.table.set("aid:trows","1")
self.table.set("aid:tcols","6")
self.table.set("revision",source_revision)
self.table.set("date",source_date)
# self.table.set("revision","$Revision: $")
# self.table.set("date","$Date: $")
self.trows = 0
id_element = {'symbol_name':'header', 'command_code':'header', 'access':'header', 'default':'header', 'subordinate':'header', 'presets':'header'}
id_cell = {'aid:table':'cell', 'aid:theader':'', 'aid:crows':'1', 'aid:ccols':'1'}
self.add_symbol("Symbol Name","Command Code","Access","Bit Range","Default","Description","",id_element,id_cell)
def add_symbol(self, symbol_name, command_code, access, bit_range, default, description, presets, id_element = None, id_cell = None, subordinate = False):
if id_element is None:
id_element = {'symbol_name':symbol_name, 'command_code':command_code, 'access':access, 'default':default, 'subordinate':str(subordinate), 'presets':presets}
if id_cell is None:
id_cell = {'aid:table':'cell', 'aid:crows':'1', 'aid:ccols':'1'}
if subordinate:
command_code = ''
access = ''
#symbol_name = ' ' + symbol_name
id_cell['aid:ccolwidth']='80' #symbol_name column width
symbol_name_tag = ET.SubElement(ET.SubElement(self.table, "Cell", attrib = id_cell), "symbol_name", attrib = id_element).text = symbol_name
id_cell['aid:ccolwidth']='45' #command_code column width
command_code_tag = ET.SubElement(ET.SubElement(self.table, "Cell", attrib = id_cell), "command_code", attrib = id_element).text = command_code
id_cell['aid:ccolwidth']='35' #access column width
access_tag = ET.SubElement(ET.SubElement(self.table, "Cell", attrib = id_cell), "access", attrib = id_element).text = access
id_cell['aid:ccolwidth']='35' #bit_range column width
bit_range_tag = ET.SubElement(ET.SubElement(self.table, "Cell", attrib = id_cell), "bit_range", attrib = id_element).text = bit_range
id_cell['aid:ccolwidth']='35' #default column width
default_tag = ET.SubElement(ET.SubElement(self.table, "Cell", attrib = id_cell), "default", attrib = id_element).text = default
id_cell['aid:ccolwidth']='285' #description column width
description_tag = ET.SubElement(ET.SubElement(self.table, "Cell", attrib = id_cell), "description", attrib = id_element).text = description
self.trows += 1
def add_xml_revision(self, revision_string):
#aid:pstyle could maybe be used to right-align text instead of javascript?
revision_tag = ET.SubElement(ET.SubElement(self.table, "Cell", attrib = {'aid:table':'cell', 'aid:crows':'1', 'aid:ccols':'6', 'aid:ccolwidth':'515'}), "revision").text = revision_string
self.trows += 1
[docs] def bit_range_str(self, size=None, lsb=None, msb=None):
'''specify two of the three arguments to convert symbol size description to string output format'''
if size is None:
lsb = int(lsb)
msb = int(msb)
size = msb-lsb+1
if lsb is None:
msb = int(msb)
size = int(size)
lsb = msb-size+1
if msb is None:
lsb = int(lsb)
size = int(size)
msb = lsb+size-1
else:
msb = int(msb)
lsb = int(lsb)
size = int(size)
assert msb == lsb+size-1
if size == 1:
return "[{}]".format(lsb)
else:
return "[{}:{}]".format(msb, lsb)
[docs] def access_str(self, readable, writable):
'''convert readable and writable booleans to string output format'''
if readable and writable:
return "R/W"
elif readable:
return "R"
elif writable:
return "W"
else:
return ""
def write(self, xmlfilename):
self.table.set("aid:trows","{}".format(self.trows))
with open(xmlfilename, 'w') as f:
f.write(self.xml_declaration)
f.write(self.doctype)
ET.ElementTree(self.table).write(f, encoding='utf-8')
class xml_converter(inDesignOutput):
def __init__(self, xmlfilename, use_case, access_list):
self.xml = ET.parse(xmlfilename)
self.root = self.xml.getroot()
self.chip = self.root.find('.//chip')
self.use = self.root.find('./use[@name="{}"]'.format(use_case))
source_revision = self.root.attrib['revision'].strip().strip("$").strip() #remove SVN escape to avoid re-writing source version information.
source_date = self.root.attrib['date'].strip().strip("$").strip()
inDesignOutput.__init__(self, source_revision, source_date)
self.categories = []
for category in self.use.findall('category'):
self.categories.append(category.text)
self.access_list = access_list
self._make_regtable()
self.add_xml_revision('{} {}'.format(source_revision,source_date))
def _make_regtable(self):
for command_code in self.chip.findall('./command_code'):
cc_val = command_code.attrib['value']
bit_fields_tmp = command_code.findall('./bit_field')
bit_fields = []
for bit_field in bit_fields_tmp:
if bit_field.attrib['category'] in self.categories:
bit_fields.append(bit_field)
if not len(bit_fields):
continue
#REGISTER ACCESS
read = False
write = False
for xmlaccess in command_code.findall('./access'):
for access in self.access_list:
if xmlaccess.attrib["mode"] == access and xmlaccess.attrib["type"] == "read":
read = True
if xmlaccess.attrib["mode"] == access and xmlaccess.attrib["type"] == "write":
write = True
access_mode = self.access_str(read, write)
#COMPUTE WHOLE-REGISTER DEFAULT
try:
reg_default = 0
for bf in bit_fields:
reg_default += lab_utils.str2num(bf.find("./default").text) << lab_utils.str2num(bf.attrib['offset'])
reg_default = str(reg_default)
except:
reg_default = "N/A"
#COMPUTE WHOLE-REGISTER BIT-RANGE
reg_lsb = None
reg_msb = None
for bf in bit_fields:
bf_lsb = lab_utils.str2num(bf.attrib['offset'])
bf_msb = bf_lsb + lab_utils.str2num(bf.attrib['size']) - 1
if bf_msb > reg_msb:
reg_msb = bf_msb
if bf_lsb < reg_lsb or reg_lsb is None:
reg_lsb = bf_lsb
reg_bit_range = self.bit_range_str(lsb=reg_lsb, msb=reg_msb)
#CHECK REGISTER DESCRIPTION
if command_code.find("./description") is not None:
reg_description = command_code.find("./description").text
else:
reg_description = "" #no description!
if len(bit_fields) == 1: #Special case of single bit_field register
subordinate = False
else:
self.add_symbol(symbol_name = command_code.attrib['name'],
command_code = cc_val,
access = access_mode,
bit_range = reg_bit_range,
default = reg_default,
description = reg_description,
presets = '',
id_element=None,
id_cell=None,
subordinate = False)
subordinate = True
for bit_field in bit_fields:
presets = []
presets_attrib = ''
for preset in bit_field.findall('./preset'):
presets.append("{} = {}".format(preset.attrib['name'], preset.attrib['value']))
presets_attrib += '{},'.format(preset.attrib['name'])
presets_attrib = presets_attrib[:-1] # dump last comma.
bit_range = self.bit_range_str(lsb=bit_field.attrib['offset'], size= bit_field.attrib['size'])
if bit_field.find("./default") is not None:
default = bit_field.find("./default").text #default_cell
else:
default = "N/A" #default_cell
description = bit_field.find("./description").text
if len(presets) == 1:
description += "\nEnum: {}".format(presets[0])
elif len(presets) >= 1:
description += "\nEnums: "
for preset in presets:
description += "\t{},\n".format(preset)
description = description[:-2] # remove last comma
self.add_symbol(symbol_name = bit_field.attrib['name'],
command_code = cc_val,
access = access_mode,
bit_range = bit_range,
default = default,
description = description,
presets = presets_attrib,
id_element=None,
id_cell=None,
subordinate = subordinate)
[docs]class csv_converter(inDesignOutput):
'''converts csv input file to appropriate XML format for import to InDesign
Expects 6 columns to be ordered: SYMBOL_NAME, COMMAND_CODE, ACCESS ex(R/W), BIT_RANGE ex(15:0), DEFAULT, DESCRIPTION
'''
def __init__(self, csvfilename, **kwargs):
inDesignOutput.__init__(self)
with open(csvfilename, 'rb') as csvfile:
csvreader = csv.reader(csvfile, **kwargs) #pass delimiter, quotechar, escapechar, lineterminator, etc here if necessary (https://docs.python.org/2/library/csv.html#csv-fmt-params)
for row in csvreader:
self.add_symbol(symbol_name = row[0], #converter functions available above to help form access, bit_range strings!
command_code = row[1],
access = row[2],
bit_range = row[3],
default = row[4],
description = row[5],
presets = '',
id_element = None,
id_cell = None,
subordinate = None)
self.add_xml_revision("CSV revision placeholder")
if __name__ == '__main__':
# Get Variables
import os
try:
DESIGN = '{}'.format(os.environ['DESIGN'])
except:
raise Exception('ERROR: DESIGN variable not defined!')
try:
REG_XML = '{}'.format(os.environ['REG_XML'])
except:
raise Exception('ERROR: REG_XML variable not defined!')
try:
SYN_INDESIGN = '{}'.format(os.environ['SYN_INDESIGN'])
except:
raise Exception('ERROR: SYN_INDESIGN variable not defined!')
try:
DATASHEET_XML_USE = '{}'.format(os.environ['DATASHEET_XML_USE'])
except:
raise Exception('ERROR: DATASHEET_XML_USE variable not defined!')
try:
access = '{}'.format(os.environ['DATASHEET_XML_ACCESS'])
access = access.replace(' ', '')
DATASHEET_XML_ACCESS = access.split(',')
except:
raise Exception('ERROR: DATASHEET_XML_ACCESS variable not defined!')
datasheet_xml = xml_converter ( xmlfilename = './{}'.format(REG_XML) ,
use_case = DATASHEET_XML_USE ,
access_list = DATASHEET_XML_ACCESS )
datasheet_xml.write('{}/{}_indesign.xml'.format(SYN_INDESIGN, DESIGN))
datasheet_xml.write('{}/{}_indesign.xml_temp'.format(SYN_INDESIGN, DESIGN))
# Don't use --format with xmllint, Adobe inDesign wants 'linearized' output, no extra spaces.
# print
# print "**********************************************************************************************"
# print "* InDesign requires linearized output (no extra white-space) - use notepad++ XMLTools plugin *"
# print "**********************************************************************************************"
# print