| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| from __future__ import absolute_import |
| from __future__ import print_function |
| from __future__ import unicode_literals |
| from itertools import chain |
| from xml.sax.saxutils import escape |
| from xml.sax.saxutils import quoteattr |
| import logging |
| import sys |
|
|
| from .filestructure import VERSION |
| from .dataio import typed_struct_attributes |
| from .dataio import Struct |
| from .dataio import StructType |
| from .dataio import ArrayType |
| from .dataio import FlagsType |
| from .dataio import EnumType |
| from .dataio import WCHAR |
| from .dataio import HWPUNIT |
| from .dataio import HWPUNIT16 |
| from .dataio import SHWPUNIT |
| from .binmodel import COLORREF |
| from .binmodel import BinStorageId |
| from .binmodel import Margin |
| from .binmodel import Text |
| from .treeop import STARTEVENT |
| from .treeop import ENDEVENT |
|
|
|
|
| PY3 = sys.version_info.major == 3 |
| if PY3: |
| basestring = str |
| unichr = chr |
|
|
|
|
| logger = logging.getLogger(__name__) |
|
|
|
|
| def xmlattrval(value): |
| if isinstance(value, basestring): |
| return value |
| elif isinstance(value, float): |
| |
| return repr(value) |
| elif isinstance(type(value), EnumType): |
| return value.name.lower() if value.name else str(int(value)) |
| elif isinstance(value, type): |
| return value.__name__ |
| else: |
| return str(value) |
|
|
|
|
| def expanded_xmlattribute(ntv): |
| name, (t, value) = ntv |
| if isinstance(t, FlagsType): |
| fmt = '%0' |
| fmt += '%d' % (t.basetype.fixed_size * 2) |
| fmt += 'X' |
| yield name, fmt % int(value) |
| for k, v in t.dictvalue(t(value)).items(): |
| yield k, xmlattrval(v) |
| elif t is Margin: |
| for pos in ('left', 'right', 'top', 'bottom'): |
| yield '-'.join([name, pos]), xmlattrval(value.get(pos)) |
| elif t is COLORREF: |
| yield name, xmlattrval(t(value)) |
| elif t is VERSION: |
| yield name, '.'.join(str(x) for x in value) |
| elif t in (HWPUNIT, SHWPUNIT, HWPUNIT16): |
| yield name, str(value) |
| elif t is WCHAR: |
| if value == 0: |
| yield name, u'' |
| else: |
| if value in PUA_SYMBOLS: |
| yield name, PUA_SYMBOLS[value] |
| else: |
| yield name, unichr(value) |
| elif t is BinStorageId: |
| yield name, 'BIN%04X' % value |
| else: |
| yield name, xmlattrval(value) |
|
|
|
|
| |
| PUA_SYMBOLS = { |
| 0xF046: u'☞', |
| 0xF06C: u'●', |
| |
| 0xF09F: u'•', |
| 0xF0A1: u'○', |
| |
| |
| |
| 0xF06E: u'■', |
| 0xF0A7: u'▪', |
| 0xF06F: u'☐', |
| |
| 0xF075: u'◆', |
| 0xF077: u'⬩', |
| |
| |
| 0xF076: u'❖', |
| 0xF0A4: u'◉', |
| |
| 0xF0AB: u'★', |
| 0xF0Fc: u'✓', |
| 0xF0FE: u'☑', |
| } |
|
|
|
|
| def xmlattr_dashednames(attrs): |
| for k, v in attrs: |
| yield k.replace('_', '-'), v |
|
|
|
|
| def xmlattr_uniqnames(attrs): |
| names = set([]) |
| for k, v in attrs: |
| assert k not in names, 'name clashes: %s' % k |
| yield k, v |
| names.add(k) |
|
|
|
|
| def xmlattributes_for_plainvalues(context, plainvalues): |
| ntvs = plainvalues.items() |
| ntvs = chain(*(expanded_xmlattribute(ntv) for ntv in ntvs)) |
| return dict(xmlattr_uniqnames(xmlattr_dashednames(ntvs))) |
|
|
|
|
| def is_complex_type(type, value): |
| if isinstance(value, dict): |
| return True |
| elif isinstance(type, ArrayType) and issubclass(type.itemtype, Struct): |
| return True |
| elif isinstance(type, ArrayType) and issubclass(type.itemtype, COLORREF): |
| return True |
| else: |
| return False |
|
|
|
|
| def separate_plainvalues(typed_attributes): |
| d = [] |
| p = dict() |
| for named_item in typed_attributes: |
| name, item = named_item |
| t, value = item |
| try: |
| if t is Margin: |
| p[name] = item |
| elif is_complex_type(t, value): |
| d.append(named_item) |
| else: |
| p[name] = item |
| except Exception as e: |
| logger.error('%s', (name, t, value)) |
| logger.error('%s', t.__dict__) |
| logger.exception(e) |
| raise e |
| return d, p |
|
|
|
|
| def startelement(context, ma): |
| model, attributes = ma |
| if isinstance(model, StructType): |
| typed_attributes = ((v['name'], (v['type'], v['value'])) |
| for v in typed_struct_attributes(model, attributes, |
| context)) |
| else: |
| typed_attributes = ((k, (type(v), v)) |
| for k, v in attributes.items()) |
|
|
| typed_attributes, plainvalues = separate_plainvalues(typed_attributes) |
|
|
| if model is Text: |
| text = plainvalues.pop('text')[1] |
| elif '<text>' in plainvalues: |
| text = plainvalues.pop('<text>')[1] |
| else: |
| text = None |
|
|
| yield STARTEVENT, (model.__name__, |
| xmlattributes_for_plainvalues(context, plainvalues)) |
| if text: |
| yield Text, text |
|
|
| for _name, (_type, _value) in typed_attributes: |
| if isinstance(_value, dict): |
| assert isinstance(_value, dict) |
| _value = dict(_value) |
| _value['attribute-name'] = _name |
| for x in element(context, (_type, _value)): |
| yield x |
| else: |
| assert isinstance(_value, (tuple, list)), (_value, _type) |
| |
| if issubclass(_type.itemtype, Struct): |
| yield STARTEVENT, ('Array', {'name': _name}) |
| for _itemvalue in _value: |
| for x in element(context, (_type.itemtype, _itemvalue)): |
| yield x |
| yield ENDEVENT, 'Array' |
| elif issubclass(_type.itemtype, COLORREF): |
| for _itemvalue in _value: |
| yield STARTEVENT, (_name, { |
| 'r': '%d' % ((_itemvalue >> 0) & 0xff), |
| 'g': '%d' % ((_itemvalue >> 8) & 0xff), |
| 'b': '%d' % ((_itemvalue >> 16) & 0xff), |
| 'alpha': '%d' % ((_itemvalue >> 24) & 0xff), |
| 'hex': xmlattrval(_type.itemtype(_itemvalue)) |
| }) |
| yield ENDEVENT, _name |
| else: |
| assert False, (_value, _type) |
|
|
|
|
| def element(context, ma): |
| model, attributes = ma |
| for x in startelement(context, ma): |
| yield x |
| yield ENDEVENT, model.__name__ |
|
|
|
|
| def xmlevents_to_bytechunks(xmlevents, encoding='utf-8'): |
| for textchunk in xmlevents_to_textchunks(xmlevents): |
| yield textchunk.encode(encoding) |
|
|
|
|
| def xmlevents_to_textchunks(xmlevents): |
| entities = {'\r': ' ', |
| '\n': ' ', |
| '\t': '	'} |
| for event, item in xmlevents: |
| if event is STARTEVENT: |
| yield '<' |
| yield item[0] |
| for n, v in item[1].items(): |
| yield ' ' |
| yield n |
| yield '=' |
| v = quoteattr(v, entities) |
| v = v.replace('\x00', '') |
| yield v |
| yield '>' |
| elif event is Text: |
| text = escape(item) |
| text = text.replace('\x00', '') |
| yield text |
| elif event is ENDEVENT: |
| yield '</' |
| yield item |
| yield '>' |
|
|