1import struct 2 3from fontTools.misc import etree 4from fontTools.misc.testTools import getXML, parseXML 5from fontTools.ttLib import TTFont 6from fontTools.ttLib.tables.S_V_G_ import table_S_V_G_ 7 8import pytest 9 10 11def dump(table, ttFont=None): 12 print("\n".join(getXML(table.toXML, ttFont))) 13 14 15def strip_xml_whitespace(xml_string): 16 def strip_or_none(text): 17 text = text.strip() if text else None 18 return text if text else None 19 20 tree = etree.fromstring(xml_string) 21 for e in tree.iter("*"): 22 e.text = strip_or_none(e.text) 23 e.tail = strip_or_none(e.tail) 24 return etree.tostring(tree, encoding="utf-8") 25 26 27SVG_DOCS = [ 28 strip_xml_whitespace(svg) 29 for svg in ( 30 b"""\ 31 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1"> 32 <defs> 33 <rect x="100" y="-200" width="300" height="400" id="p1"/> 34 </defs> 35 <g id="glyph1"> 36 <use xlink:href="#p1" fill="#red"/> 37 </g> 38 <g id="glyph2"> 39 <use xlink:href="#p1" fill="#blue"/> 40 </g> 41 <g id="glyph4"> 42 <use xlink:href="#p1" fill="#green"/> 43 </g> 44 </svg>""", 45 b"""\ 46 <svg xmlns="http://www.w3.org/2000/svg" version="1.1"> 47 <g id="glyph3"> 48 <path d="M0,0 L100,0 L50,100 Z"/> 49 </g> 50 </svg>""", 51 ) 52] 53 54 55OTSVG_DATA = b"".join( 56 [ 57 # SVG table header 58 b"\x00\x00" # version (0) 59 b"\x00\x00\x00\x0a" # offset to SVGDocumentList (10) 60 b"\x00\x00\x00\x00" # reserved (0) 61 # SVGDocumentList 62 b"\x00\x03" # number of SVGDocumentRecords (3) 63 # SVGDocumentRecord[0] 64 b"\x00\x01" # startGlyphID (1) 65 b"\x00\x02" # endGlyphID (2) 66 b"\x00\x00\x00\x26" # svgDocOffset (2 + 12*3 == 38 == 0x26) 67 + struct.pack(">L", len(SVG_DOCS[0])) # svgDocLength 68 # SVGDocumentRecord[1] 69 + b"\x00\x03" # startGlyphID (3) 70 b"\x00\x03" # endGlyphID (3) 71 + struct.pack(">L", 0x26 + len(SVG_DOCS[0])) # svgDocOffset 72 + struct.pack(">L", len(SVG_DOCS[1])) # svgDocLength 73 # SVGDocumentRecord[2] 74 + b"\x00\x04" # startGlyphID (4) 75 b"\x00\x04" # endGlyphID (4) 76 b"\x00\x00\x00\x26" # svgDocOffset (38); records 0 and 2 point to same SVG doc 77 + struct.pack(">L", len(SVG_DOCS[0])) # svgDocLength 78 ] 79 + SVG_DOCS 80) 81 82OTSVG_TTX = [ 83 '<svgDoc endGlyphID="2" startGlyphID="1">', 84 f" <![CDATA[{SVG_DOCS[0].decode()}]]>", 85 "</svgDoc>", 86 '<svgDoc endGlyphID="3" startGlyphID="3">', 87 f" <![CDATA[{SVG_DOCS[1].decode()}]]>", 88 "</svgDoc>", 89 '<svgDoc endGlyphID="4" startGlyphID="4">', 90 f" <![CDATA[{SVG_DOCS[0].decode()}]]>", 91 "</svgDoc>", 92] 93 94 95@pytest.fixture 96def font(): 97 font = TTFont() 98 font.setGlyphOrder([".notdef"] + ["glyph%05d" % i for i in range(1, 30)]) 99 return font 100 101 102def test_decompile_and_compile(font): 103 table = table_S_V_G_() 104 table.decompile(OTSVG_DATA, font) 105 assert table.compile(font) == OTSVG_DATA 106 107 108def test_decompile_and_dump_ttx(font): 109 table = table_S_V_G_() 110 table.decompile(OTSVG_DATA, font) 111 112 dump(table, font) 113 assert getXML(table.toXML, font) == OTSVG_TTX 114 115 116def test_load_from_ttx_and_compile(font): 117 table = table_S_V_G_() 118 for name, attrs, content in parseXML(OTSVG_TTX): 119 table.fromXML(name, attrs, content, font) 120 assert table.compile(font) == OTSVG_DATA 121 122 123def test_round_trip_ttx(font): 124 table = table_S_V_G_() 125 for name, attrs, content in parseXML(OTSVG_TTX): 126 table.fromXML(name, attrs, content, font) 127 compiled = table.compile(font) 128 129 table = table_S_V_G_() 130 table.decompile(compiled, font) 131 assert getXML(table.toXML, font) == OTSVG_TTX 132