1*e1fe3e4aSElliott Hughesfrom string import ascii_letters 2*e1fe3e4aSElliott Hughesimport textwrap 3*e1fe3e4aSElliott Hughes 4*e1fe3e4aSElliott Hughesfrom fontTools.misc.testTools import getXML 5*e1fe3e4aSElliott Hughesfrom fontTools import subset 6*e1fe3e4aSElliott Hughesfrom fontTools.fontBuilder import FontBuilder 7*e1fe3e4aSElliott Hughesfrom fontTools.pens.ttGlyphPen import TTGlyphPen 8*e1fe3e4aSElliott Hughesfrom fontTools.ttLib import TTFont, newTable 9*e1fe3e4aSElliott Hughesfrom fontTools.subset.svg import NAMESPACES, ranges 10*e1fe3e4aSElliott Hughes 11*e1fe3e4aSElliott Hughesimport pytest 12*e1fe3e4aSElliott Hughes 13*e1fe3e4aSElliott Hughesetree = pytest.importorskip("lxml.etree") 14*e1fe3e4aSElliott Hughes 15*e1fe3e4aSElliott Hughes 16*e1fe3e4aSElliott Hughes@pytest.fixture 17*e1fe3e4aSElliott Hughesdef empty_svg_font(): 18*e1fe3e4aSElliott Hughes glyph_order = [".notdef"] + list(ascii_letters) 19*e1fe3e4aSElliott Hughes 20*e1fe3e4aSElliott Hughes pen = TTGlyphPen(glyphSet=None) 21*e1fe3e4aSElliott Hughes pen.moveTo((0, 0)) 22*e1fe3e4aSElliott Hughes pen.lineTo((0, 500)) 23*e1fe3e4aSElliott Hughes pen.lineTo((500, 500)) 24*e1fe3e4aSElliott Hughes pen.lineTo((500, 0)) 25*e1fe3e4aSElliott Hughes pen.closePath() 26*e1fe3e4aSElliott Hughes glyph = pen.glyph() 27*e1fe3e4aSElliott Hughes glyphs = {g: glyph for g in glyph_order} 28*e1fe3e4aSElliott Hughes 29*e1fe3e4aSElliott Hughes fb = FontBuilder(unitsPerEm=1024, isTTF=True) 30*e1fe3e4aSElliott Hughes fb.setupGlyphOrder(glyph_order) 31*e1fe3e4aSElliott Hughes fb.setupCharacterMap({ord(c): c for c in ascii_letters}) 32*e1fe3e4aSElliott Hughes fb.setupGlyf(glyphs) 33*e1fe3e4aSElliott Hughes fb.setupHorizontalMetrics({g: (500, 0) for g in glyph_order}) 34*e1fe3e4aSElliott Hughes fb.setupHorizontalHeader() 35*e1fe3e4aSElliott Hughes fb.setupOS2() 36*e1fe3e4aSElliott Hughes fb.setupPost() 37*e1fe3e4aSElliott Hughes fb.setupNameTable({"familyName": "TestSVG", "styleName": "Regular"}) 38*e1fe3e4aSElliott Hughes 39*e1fe3e4aSElliott Hughes svg_table = newTable("SVG ") 40*e1fe3e4aSElliott Hughes svg_table.docList = [] 41*e1fe3e4aSElliott Hughes fb.font["SVG "] = svg_table 42*e1fe3e4aSElliott Hughes 43*e1fe3e4aSElliott Hughes return fb.font 44*e1fe3e4aSElliott Hughes 45*e1fe3e4aSElliott Hughes 46*e1fe3e4aSElliott Hughes# 'simple' here means one svg document per glyph. The required 'id' attribute 47*e1fe3e4aSElliott Hughes# containing the 'glyphXXX' indices can be either on a child of the root <svg> 48*e1fe3e4aSElliott Hughes# or on the <svg> root itself, so we test with both. 49*e1fe3e4aSElliott Hughes# see https://github.com/fonttools/fonttools/issues/2548 50*e1fe3e4aSElliott Hughes 51*e1fe3e4aSElliott Hughes 52*e1fe3e4aSElliott Hughesdef simple_svg_table_glyph_ids_on_children(empty_svg_font): 53*e1fe3e4aSElliott Hughes font = empty_svg_font 54*e1fe3e4aSElliott Hughes svg_docs = font["SVG "].docList 55*e1fe3e4aSElliott Hughes for i in range(1, 11): 56*e1fe3e4aSElliott Hughes svg = new_svg() 57*e1fe3e4aSElliott Hughes etree.SubElement(svg, "path", {"id": f"glyph{i}", "d": f"M{i},{i}"}) 58*e1fe3e4aSElliott Hughes svg_docs.append((etree.tostring(svg).decode(), i, i)) 59*e1fe3e4aSElliott Hughes return font 60*e1fe3e4aSElliott Hughes 61*e1fe3e4aSElliott Hughes 62*e1fe3e4aSElliott Hughesdef simple_svg_table_glyph_ids_on_roots(empty_svg_font): 63*e1fe3e4aSElliott Hughes font = empty_svg_font 64*e1fe3e4aSElliott Hughes svg_docs = font["SVG "].docList 65*e1fe3e4aSElliott Hughes for i in range(1, 11): 66*e1fe3e4aSElliott Hughes svg = new_svg(id=f"glyph{i}") 67*e1fe3e4aSElliott Hughes etree.SubElement(svg, "path", {"d": f"M{i},{i}"}) 68*e1fe3e4aSElliott Hughes svg_docs.append((etree.tostring(svg).decode(), i, i)) 69*e1fe3e4aSElliott Hughes return font 70*e1fe3e4aSElliott Hughes 71*e1fe3e4aSElliott Hughes 72*e1fe3e4aSElliott Hughesdef new_svg(**attrs): 73*e1fe3e4aSElliott Hughes return etree.Element("svg", {"xmlns": NAMESPACES["svg"], **attrs}) 74*e1fe3e4aSElliott Hughes 75*e1fe3e4aSElliott Hughes 76*e1fe3e4aSElliott Hughesdef _lines(s): 77*e1fe3e4aSElliott Hughes return textwrap.dedent(s).splitlines() 78*e1fe3e4aSElliott Hughes 79*e1fe3e4aSElliott Hughes 80*e1fe3e4aSElliott Hughes@pytest.mark.parametrize( 81*e1fe3e4aSElliott Hughes "add_svg_table, gids, retain_gids, expected_xml", 82*e1fe3e4aSElliott Hughes [ 83*e1fe3e4aSElliott Hughes # keep four glyphs in total, don't retain gids, which thus get remapped 84*e1fe3e4aSElliott Hughes ( 85*e1fe3e4aSElliott Hughes simple_svg_table_glyph_ids_on_children, 86*e1fe3e4aSElliott Hughes "2,4-6", 87*e1fe3e4aSElliott Hughes False, 88*e1fe3e4aSElliott Hughes _lines( 89*e1fe3e4aSElliott Hughes """\ 90*e1fe3e4aSElliott Hughes <svgDoc endGlyphID="1" startGlyphID="1"> 91*e1fe3e4aSElliott Hughes <![CDATA[<svg xmlns="http://www.w3.org/2000/svg"><path id="glyph1" d="M2,2"/></svg>]]> 92*e1fe3e4aSElliott Hughes </svgDoc> 93*e1fe3e4aSElliott Hughes <svgDoc endGlyphID="2" startGlyphID="2"> 94*e1fe3e4aSElliott Hughes <![CDATA[<svg xmlns="http://www.w3.org/2000/svg"><path id="glyph2" d="M4,4"/></svg>]]> 95*e1fe3e4aSElliott Hughes </svgDoc> 96*e1fe3e4aSElliott Hughes <svgDoc endGlyphID="3" startGlyphID="3"> 97*e1fe3e4aSElliott Hughes <![CDATA[<svg xmlns="http://www.w3.org/2000/svg"><path id="glyph3" d="M5,5"/></svg>]]> 98*e1fe3e4aSElliott Hughes </svgDoc> 99*e1fe3e4aSElliott Hughes <svgDoc endGlyphID="4" startGlyphID="4"> 100*e1fe3e4aSElliott Hughes <![CDATA[<svg xmlns="http://www.w3.org/2000/svg"><path id="glyph4" d="M6,6"/></svg>]]> 101*e1fe3e4aSElliott Hughes </svgDoc> 102*e1fe3e4aSElliott Hughes """ 103*e1fe3e4aSElliott Hughes ), 104*e1fe3e4aSElliott Hughes ), 105*e1fe3e4aSElliott Hughes # same as above but with glyph id attribute in the root <svg> element itself 106*e1fe3e4aSElliott Hughes # https://github.com/fonttools/fonttools/issues/2548 107*e1fe3e4aSElliott Hughes ( 108*e1fe3e4aSElliott Hughes simple_svg_table_glyph_ids_on_roots, 109*e1fe3e4aSElliott Hughes "2,4-6", 110*e1fe3e4aSElliott Hughes False, 111*e1fe3e4aSElliott Hughes _lines( 112*e1fe3e4aSElliott Hughes """\ 113*e1fe3e4aSElliott Hughes <svgDoc endGlyphID="1" startGlyphID="1"> 114*e1fe3e4aSElliott Hughes <![CDATA[<svg xmlns="http://www.w3.org/2000/svg" id="glyph1"><path d="M2,2"/></svg>]]> 115*e1fe3e4aSElliott Hughes </svgDoc> 116*e1fe3e4aSElliott Hughes <svgDoc endGlyphID="2" startGlyphID="2"> 117*e1fe3e4aSElliott Hughes <![CDATA[<svg xmlns="http://www.w3.org/2000/svg" id="glyph2"><path d="M4,4"/></svg>]]> 118*e1fe3e4aSElliott Hughes </svgDoc> 119*e1fe3e4aSElliott Hughes <svgDoc endGlyphID="3" startGlyphID="3"> 120*e1fe3e4aSElliott Hughes <![CDATA[<svg xmlns="http://www.w3.org/2000/svg" id="glyph3"><path d="M5,5"/></svg>]]> 121*e1fe3e4aSElliott Hughes </svgDoc> 122*e1fe3e4aSElliott Hughes <svgDoc endGlyphID="4" startGlyphID="4"> 123*e1fe3e4aSElliott Hughes <![CDATA[<svg xmlns="http://www.w3.org/2000/svg" id="glyph4"><path d="M6,6"/></svg>]]> 124*e1fe3e4aSElliott Hughes </svgDoc> 125*e1fe3e4aSElliott Hughes """ 126*e1fe3e4aSElliott Hughes ), 127*e1fe3e4aSElliott Hughes ), 128*e1fe3e4aSElliott Hughes # same four glyphs, but we now retain gids 129*e1fe3e4aSElliott Hughes ( 130*e1fe3e4aSElliott Hughes simple_svg_table_glyph_ids_on_children, 131*e1fe3e4aSElliott Hughes "2,4-6", 132*e1fe3e4aSElliott Hughes True, 133*e1fe3e4aSElliott Hughes _lines( 134*e1fe3e4aSElliott Hughes """\ 135*e1fe3e4aSElliott Hughes <svgDoc endGlyphID="2" startGlyphID="2"> 136*e1fe3e4aSElliott Hughes <![CDATA[<svg xmlns="http://www.w3.org/2000/svg"><path id="glyph2" d="M2,2"/></svg>]]> 137*e1fe3e4aSElliott Hughes </svgDoc> 138*e1fe3e4aSElliott Hughes <svgDoc endGlyphID="4" startGlyphID="4"> 139*e1fe3e4aSElliott Hughes <![CDATA[<svg xmlns="http://www.w3.org/2000/svg"><path id="glyph4" d="M4,4"/></svg>]]> 140*e1fe3e4aSElliott Hughes </svgDoc> 141*e1fe3e4aSElliott Hughes <svgDoc endGlyphID="5" startGlyphID="5"> 142*e1fe3e4aSElliott Hughes <![CDATA[<svg xmlns="http://www.w3.org/2000/svg"><path id="glyph5" d="M5,5"/></svg>]]> 143*e1fe3e4aSElliott Hughes </svgDoc> 144*e1fe3e4aSElliott Hughes <svgDoc endGlyphID="6" startGlyphID="6"> 145*e1fe3e4aSElliott Hughes <![CDATA[<svg xmlns="http://www.w3.org/2000/svg"><path id="glyph6" d="M6,6"/></svg>]]> 146*e1fe3e4aSElliott Hughes </svgDoc> 147*e1fe3e4aSElliott Hughes """ 148*e1fe3e4aSElliott Hughes ), 149*e1fe3e4aSElliott Hughes ), 150*e1fe3e4aSElliott Hughes # retain gids like above but with glyph id attribute in the root <svg> element itself 151*e1fe3e4aSElliott Hughes # https://github.com/fonttools/fonttools/issues/2548 152*e1fe3e4aSElliott Hughes ( 153*e1fe3e4aSElliott Hughes simple_svg_table_glyph_ids_on_roots, 154*e1fe3e4aSElliott Hughes "2,4-6", 155*e1fe3e4aSElliott Hughes True, 156*e1fe3e4aSElliott Hughes _lines( 157*e1fe3e4aSElliott Hughes """\ 158*e1fe3e4aSElliott Hughes <svgDoc endGlyphID="2" startGlyphID="2"> 159*e1fe3e4aSElliott Hughes <![CDATA[<svg xmlns="http://www.w3.org/2000/svg" id="glyph2"><path d="M2,2"/></svg>]]> 160*e1fe3e4aSElliott Hughes </svgDoc> 161*e1fe3e4aSElliott Hughes <svgDoc endGlyphID="4" startGlyphID="4"> 162*e1fe3e4aSElliott Hughes <![CDATA[<svg xmlns="http://www.w3.org/2000/svg" id="glyph4"><path d="M4,4"/></svg>]]> 163*e1fe3e4aSElliott Hughes </svgDoc> 164*e1fe3e4aSElliott Hughes <svgDoc endGlyphID="5" startGlyphID="5"> 165*e1fe3e4aSElliott Hughes <![CDATA[<svg xmlns="http://www.w3.org/2000/svg" id="glyph5"><path d="M5,5"/></svg>]]> 166*e1fe3e4aSElliott Hughes </svgDoc> 167*e1fe3e4aSElliott Hughes <svgDoc endGlyphID="6" startGlyphID="6"> 168*e1fe3e4aSElliott Hughes <![CDATA[<svg xmlns="http://www.w3.org/2000/svg" id="glyph6"><path d="M6,6"/></svg>]]> 169*e1fe3e4aSElliott Hughes </svgDoc> 170*e1fe3e4aSElliott Hughes """ 171*e1fe3e4aSElliott Hughes ), 172*e1fe3e4aSElliott Hughes ), 173*e1fe3e4aSElliott Hughes ], 174*e1fe3e4aSElliott Hughes) 175*e1fe3e4aSElliott Hughesdef test_subset_single_glyph_per_svg( 176*e1fe3e4aSElliott Hughes empty_svg_font, add_svg_table, tmp_path, gids, retain_gids, expected_xml 177*e1fe3e4aSElliott Hughes): 178*e1fe3e4aSElliott Hughes font = add_svg_table(empty_svg_font) 179*e1fe3e4aSElliott Hughes 180*e1fe3e4aSElliott Hughes svg_font_path = tmp_path / "TestSVG.ttf" 181*e1fe3e4aSElliott Hughes font.save(svg_font_path) 182*e1fe3e4aSElliott Hughes 183*e1fe3e4aSElliott Hughes subset_path = svg_font_path.with_suffix(".subset.ttf") 184*e1fe3e4aSElliott Hughes 185*e1fe3e4aSElliott Hughes subset.main( 186*e1fe3e4aSElliott Hughes [ 187*e1fe3e4aSElliott Hughes str(svg_font_path), 188*e1fe3e4aSElliott Hughes f"--output-file={subset_path}", 189*e1fe3e4aSElliott Hughes f"--gids={gids}", 190*e1fe3e4aSElliott Hughes "--retain_gids" if retain_gids else "--no-retain_gids", 191*e1fe3e4aSElliott Hughes ] 192*e1fe3e4aSElliott Hughes ) 193*e1fe3e4aSElliott Hughes subset_font = TTFont(subset_path) 194*e1fe3e4aSElliott Hughes 195*e1fe3e4aSElliott Hughes assert getXML(subset_font["SVG "].toXML, subset_font) == expected_xml 196*e1fe3e4aSElliott Hughes 197*e1fe3e4aSElliott Hughes 198*e1fe3e4aSElliott Hughes# This contains a bunch of cross-references between glyphs, paths, gradients, etc. 199*e1fe3e4aSElliott Hughes# Note the path coordinates are completely made up and not meant to be rendered. 200*e1fe3e4aSElliott Hughes# We only care about the tree structure, not it's visual content. 201*e1fe3e4aSElliott HughesCOMPLEX_SVG = """\ 202*e1fe3e4aSElliott Hughes<svg xmlns="http://www.w3.org/2000/svg" 203*e1fe3e4aSElliott Hughes xmlns:xlink="http://www.w3.org/1999/xlink"> 204*e1fe3e4aSElliott Hughes <defs> 205*e1fe3e4aSElliott Hughes <linearGradient id="lg1" x1="50" x2="50" y1="80" y2="80" gradientUnits="userSpaceOnUse"> 206*e1fe3e4aSElliott Hughes <stop stop-color="#A47B62" offset="0"/> 207*e1fe3e4aSElliott Hughes <stop stop-color="#AD8264" offset="1.0"/> 208*e1fe3e4aSElliott Hughes </linearGradient> 209*e1fe3e4aSElliott Hughes <radialGradient id="rg2" cx="50" cy="50" r="10" gradientUnits="userSpaceOnUse"> 210*e1fe3e4aSElliott Hughes <stop stop-color="#A47B62" offset="0"/> 211*e1fe3e4aSElliott Hughes <stop stop-color="#AD8264" offset="1.0"/> 212*e1fe3e4aSElliott Hughes </radialGradient> 213*e1fe3e4aSElliott Hughes <radialGradient id="rg3" xlink:href="#rg2" r="20"/> 214*e1fe3e4aSElliott Hughes <radialGradient id="rg4" xlink:href="#rg3" cy="100"/> 215*e1fe3e4aSElliott Hughes <path id="p1" d="M3,3"/> 216*e1fe3e4aSElliott Hughes <clipPath id="c1"> 217*e1fe3e4aSElliott Hughes <circle cx="10" cy="10" r="1"/> 218*e1fe3e4aSElliott Hughes </clipPath> 219*e1fe3e4aSElliott Hughes </defs> 220*e1fe3e4aSElliott Hughes <g id="glyph1"> 221*e1fe3e4aSElliott Hughes <g id="glyph2"> 222*e1fe3e4aSElliott Hughes <path d="M0,0"/> 223*e1fe3e4aSElliott Hughes </g> 224*e1fe3e4aSElliott Hughes <g> 225*e1fe3e4aSElliott Hughes <path d="M1,1" fill="url(#lg1)"/> 226*e1fe3e4aSElliott Hughes <path d="M2,2"/> 227*e1fe3e4aSElliott Hughes </g> 228*e1fe3e4aSElliott Hughes </g> 229*e1fe3e4aSElliott Hughes <g id="glyph3"> 230*e1fe3e4aSElliott Hughes <use xlink:href="#p1"/> 231*e1fe3e4aSElliott Hughes </g> 232*e1fe3e4aSElliott Hughes <use id="glyph4" xlink:href="#glyph1" x="10"/> 233*e1fe3e4aSElliott Hughes <use id="glyph5" xlink:href="#glyph2" y="-10"/> 234*e1fe3e4aSElliott Hughes <g id="glyph6"> 235*e1fe3e4aSElliott Hughes <use xlink:href="#p1" transform="scale(2, 1)"/> 236*e1fe3e4aSElliott Hughes </g> 237*e1fe3e4aSElliott Hughes <g id="group1"> 238*e1fe3e4aSElliott Hughes <g id="glyph7"> 239*e1fe3e4aSElliott Hughes <path id="p2" d="M4,4"/> 240*e1fe3e4aSElliott Hughes </g> 241*e1fe3e4aSElliott Hughes <g id=".glyph7"> 242*e1fe3e4aSElliott Hughes <path d="M4,4"/> 243*e1fe3e4aSElliott Hughes </g> 244*e1fe3e4aSElliott Hughes <g id="glyph8"> 245*e1fe3e4aSElliott Hughes <g id=".glyph8"> 246*e1fe3e4aSElliott Hughes <path id="p3" d="M5,5"/> 247*e1fe3e4aSElliott Hughes <path id="M6,6"/> 248*e1fe3e4aSElliott Hughes </g> 249*e1fe3e4aSElliott Hughes <path d="M7,7"/> 250*e1fe3e4aSElliott Hughes </g> 251*e1fe3e4aSElliott Hughes <g id="glyph9"> 252*e1fe3e4aSElliott Hughes <use xlink:href="#p2"/> 253*e1fe3e4aSElliott Hughes </g> 254*e1fe3e4aSElliott Hughes <g id="glyph10"> 255*e1fe3e4aSElliott Hughes <use xlink:href="#p3"/> 256*e1fe3e4aSElliott Hughes </g> 257*e1fe3e4aSElliott Hughes </g> 258*e1fe3e4aSElliott Hughes <g id="glyph11"> 259*e1fe3e4aSElliott Hughes <path d="M7,7" fill="url(#rg4)"/> 260*e1fe3e4aSElliott Hughes </g> 261*e1fe3e4aSElliott Hughes <g id="glyph12"> 262*e1fe3e4aSElliott Hughes <path d="M7,7" style="fill:url(#lg1);stroke:red;clip-path:url(#c1)"/> 263*e1fe3e4aSElliott Hughes </g> 264*e1fe3e4aSElliott Hughes</svg> 265*e1fe3e4aSElliott Hughes""" 266*e1fe3e4aSElliott Hughes 267*e1fe3e4aSElliott Hughes 268*e1fe3e4aSElliott Hughes@pytest.mark.parametrize( 269*e1fe3e4aSElliott Hughes "subset_gids, expected_xml", 270*e1fe3e4aSElliott Hughes [ 271*e1fe3e4aSElliott Hughes # we only keep gid=2, with 'glyph2' defined inside 'glyph1': 'glyph2' 272*e1fe3e4aSElliott Hughes # is renamed 'glyph1' to match the new subset indices, and the old 'glyph1' 273*e1fe3e4aSElliott Hughes # is kept (as it contains 'glyph2') but renamed '.glyph1' to avoid clash 274*e1fe3e4aSElliott Hughes ( 275*e1fe3e4aSElliott Hughes "2", 276*e1fe3e4aSElliott Hughes _lines( 277*e1fe3e4aSElliott Hughes """\ 278*e1fe3e4aSElliott Hughes <svgDoc endGlyphID="1" startGlyphID="1"> 279*e1fe3e4aSElliott Hughes <![CDATA[<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> 280*e1fe3e4aSElliott Hughes <g id=".glyph1"> 281*e1fe3e4aSElliott Hughes <g id="glyph1"> 282*e1fe3e4aSElliott Hughes <path d="M0,0"/> 283*e1fe3e4aSElliott Hughes </g> 284*e1fe3e4aSElliott Hughes </g> 285*e1fe3e4aSElliott Hughes </svg> 286*e1fe3e4aSElliott Hughes ]]> 287*e1fe3e4aSElliott Hughes </svgDoc> 288*e1fe3e4aSElliott Hughes """ 289*e1fe3e4aSElliott Hughes ), 290*e1fe3e4aSElliott Hughes ), 291*e1fe3e4aSElliott Hughes # we keep both gid 1 and 2: the glyph elements' ids stay as they are (only the 292*e1fe3e4aSElliott Hughes # range endGlyphID change); a gradient is kept since it's referenced by glyph1 293*e1fe3e4aSElliott Hughes ( 294*e1fe3e4aSElliott Hughes "1,2", 295*e1fe3e4aSElliott Hughes _lines( 296*e1fe3e4aSElliott Hughes """\ 297*e1fe3e4aSElliott Hughes <svgDoc endGlyphID="2" startGlyphID="1"> 298*e1fe3e4aSElliott Hughes <![CDATA[<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> 299*e1fe3e4aSElliott Hughes <defs> 300*e1fe3e4aSElliott Hughes <linearGradient id="lg1" x1="50" x2="50" y1="80" y2="80" gradientUnits="userSpaceOnUse"> 301*e1fe3e4aSElliott Hughes <stop stop-color="#A47B62" offset="0"/> 302*e1fe3e4aSElliott Hughes <stop stop-color="#AD8264" offset="1.0"/> 303*e1fe3e4aSElliott Hughes </linearGradient> 304*e1fe3e4aSElliott Hughes </defs> 305*e1fe3e4aSElliott Hughes <g id="glyph1"> 306*e1fe3e4aSElliott Hughes <g id="glyph2"> 307*e1fe3e4aSElliott Hughes <path d="M0,0"/> 308*e1fe3e4aSElliott Hughes </g> 309*e1fe3e4aSElliott Hughes <g> 310*e1fe3e4aSElliott Hughes <path d="M1,1" fill="url(#lg1)"/> 311*e1fe3e4aSElliott Hughes <path d="M2,2"/> 312*e1fe3e4aSElliott Hughes </g> 313*e1fe3e4aSElliott Hughes </g> 314*e1fe3e4aSElliott Hughes </svg> 315*e1fe3e4aSElliott Hughes ]]> 316*e1fe3e4aSElliott Hughes </svgDoc> 317*e1fe3e4aSElliott Hughes """ 318*e1fe3e4aSElliott Hughes ), 319*e1fe3e4aSElliott Hughes ), 320*e1fe3e4aSElliott Hughes ( 321*e1fe3e4aSElliott Hughes # both gid 3 and 6 refer (via <use xlink:href="#...") to path 'p1', which 322*e1fe3e4aSElliott Hughes # is thus kept in <defs>; the glyph ids and range start/end are renumbered. 323*e1fe3e4aSElliott Hughes "3,6", 324*e1fe3e4aSElliott Hughes _lines( 325*e1fe3e4aSElliott Hughes """\ 326*e1fe3e4aSElliott Hughes <svgDoc endGlyphID="2" startGlyphID="1"> 327*e1fe3e4aSElliott Hughes <![CDATA[<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> 328*e1fe3e4aSElliott Hughes <defs> 329*e1fe3e4aSElliott Hughes <path id="p1" d="M3,3"/> 330*e1fe3e4aSElliott Hughes </defs> 331*e1fe3e4aSElliott Hughes <g id="glyph1"> 332*e1fe3e4aSElliott Hughes <use xlink:href="#p1"/> 333*e1fe3e4aSElliott Hughes </g> 334*e1fe3e4aSElliott Hughes <g id="glyph2"> 335*e1fe3e4aSElliott Hughes <use xlink:href="#p1" transform="scale(2, 1)"/> 336*e1fe3e4aSElliott Hughes </g> 337*e1fe3e4aSElliott Hughes </svg> 338*e1fe3e4aSElliott Hughes ]]> 339*e1fe3e4aSElliott Hughes </svgDoc> 340*e1fe3e4aSElliott Hughes """ 341*e1fe3e4aSElliott Hughes ), 342*e1fe3e4aSElliott Hughes ), 343*e1fe3e4aSElliott Hughes ( 344*e1fe3e4aSElliott Hughes # 'glyph4' uses the whole 'glyph1' element (translated); we keep the latter 345*e1fe3e4aSElliott Hughes # renamed to avoid clashes with new gids 346*e1fe3e4aSElliott Hughes "3-4", 347*e1fe3e4aSElliott Hughes _lines( 348*e1fe3e4aSElliott Hughes """\ 349*e1fe3e4aSElliott Hughes <svgDoc endGlyphID="2" startGlyphID="1"> 350*e1fe3e4aSElliott Hughes <![CDATA[<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> 351*e1fe3e4aSElliott Hughes <defs> 352*e1fe3e4aSElliott Hughes <linearGradient id="lg1" x1="50" x2="50" y1="80" y2="80" gradientUnits="userSpaceOnUse"> 353*e1fe3e4aSElliott Hughes <stop stop-color="#A47B62" offset="0"/> 354*e1fe3e4aSElliott Hughes <stop stop-color="#AD8264" offset="1.0"/> 355*e1fe3e4aSElliott Hughes </linearGradient> 356*e1fe3e4aSElliott Hughes <path id="p1" d="M3,3"/> 357*e1fe3e4aSElliott Hughes </defs> 358*e1fe3e4aSElliott Hughes <g id=".glyph1"> 359*e1fe3e4aSElliott Hughes <g id=".glyph2"> 360*e1fe3e4aSElliott Hughes <path d="M0,0"/> 361*e1fe3e4aSElliott Hughes </g> 362*e1fe3e4aSElliott Hughes <g> 363*e1fe3e4aSElliott Hughes <path d="M1,1" fill="url(#lg1)"/> 364*e1fe3e4aSElliott Hughes <path d="M2,2"/> 365*e1fe3e4aSElliott Hughes </g> 366*e1fe3e4aSElliott Hughes </g> 367*e1fe3e4aSElliott Hughes <g id="glyph1"> 368*e1fe3e4aSElliott Hughes <use xlink:href="#p1"/> 369*e1fe3e4aSElliott Hughes </g> 370*e1fe3e4aSElliott Hughes <use id="glyph2" xlink:href="#.glyph1" x="10"/> 371*e1fe3e4aSElliott Hughes </svg> 372*e1fe3e4aSElliott Hughes ]]> 373*e1fe3e4aSElliott Hughes </svgDoc> 374*e1fe3e4aSElliott Hughes """ 375*e1fe3e4aSElliott Hughes ), 376*e1fe3e4aSElliott Hughes ), 377*e1fe3e4aSElliott Hughes ( 378*e1fe3e4aSElliott Hughes # 'glyph9' uses a path 'p2' defined inside 'glyph7', the latter is excluded 379*e1fe3e4aSElliott Hughes # from our subset, thus gets renamed '.glyph7'; an unrelated element with 380*e1fe3e4aSElliott Hughes # same id=".glyph7" doesn't clash because it was dropped. 381*e1fe3e4aSElliott Hughes # Similarly 'glyph10' uses path 'p3' defined inside 'glyph8', also excluded 382*e1fe3e4aSElliott Hughes # from subset and prefixed with '.'. But since an id=".glyph8" is already 383*e1fe3e4aSElliott Hughes # used in the doc, we append a .{digit} suffix to disambiguate. 384*e1fe3e4aSElliott Hughes "9,10", 385*e1fe3e4aSElliott Hughes _lines( 386*e1fe3e4aSElliott Hughes """\ 387*e1fe3e4aSElliott Hughes <svgDoc endGlyphID="2" startGlyphID="1"> 388*e1fe3e4aSElliott Hughes <![CDATA[<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> 389*e1fe3e4aSElliott Hughes <g id="group1"> 390*e1fe3e4aSElliott Hughes <g id=".glyph7"> 391*e1fe3e4aSElliott Hughes <path id="p2" d="M4,4"/> 392*e1fe3e4aSElliott Hughes </g> 393*e1fe3e4aSElliott Hughes <g id=".glyph8.1"> 394*e1fe3e4aSElliott Hughes <g id=".glyph8"> 395*e1fe3e4aSElliott Hughes <path id="p3" d="M5,5"/> 396*e1fe3e4aSElliott Hughes </g> 397*e1fe3e4aSElliott Hughes </g> 398*e1fe3e4aSElliott Hughes <g id="glyph1"> 399*e1fe3e4aSElliott Hughes <use xlink:href="#p2"/> 400*e1fe3e4aSElliott Hughes </g> 401*e1fe3e4aSElliott Hughes <g id="glyph2"> 402*e1fe3e4aSElliott Hughes <use xlink:href="#p3"/> 403*e1fe3e4aSElliott Hughes </g> 404*e1fe3e4aSElliott Hughes </g> 405*e1fe3e4aSElliott Hughes </svg> 406*e1fe3e4aSElliott Hughes ]]> 407*e1fe3e4aSElliott Hughes </svgDoc> 408*e1fe3e4aSElliott Hughes """ 409*e1fe3e4aSElliott Hughes ), 410*e1fe3e4aSElliott Hughes ), 411*e1fe3e4aSElliott Hughes ( 412*e1fe3e4aSElliott Hughes # 'glyph11' uses gradient 'rg4' which inherits from 'rg3', which inherits 413*e1fe3e4aSElliott Hughes # from 'rg2', etc. 414*e1fe3e4aSElliott Hughes "11", 415*e1fe3e4aSElliott Hughes _lines( 416*e1fe3e4aSElliott Hughes """\ 417*e1fe3e4aSElliott Hughes <svgDoc endGlyphID="1" startGlyphID="1"> 418*e1fe3e4aSElliott Hughes <![CDATA[<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> 419*e1fe3e4aSElliott Hughes <defs> 420*e1fe3e4aSElliott Hughes <radialGradient id="rg2" cx="50" cy="50" r="10" gradientUnits="userSpaceOnUse"> 421*e1fe3e4aSElliott Hughes <stop stop-color="#A47B62" offset="0"/> 422*e1fe3e4aSElliott Hughes <stop stop-color="#AD8264" offset="1.0"/> 423*e1fe3e4aSElliott Hughes </radialGradient> 424*e1fe3e4aSElliott Hughes <radialGradient id="rg3" xlink:href="#rg2" r="20"/> 425*e1fe3e4aSElliott Hughes <radialGradient id="rg4" xlink:href="#rg3" cy="100"/> 426*e1fe3e4aSElliott Hughes </defs> 427*e1fe3e4aSElliott Hughes <g id="glyph1"> 428*e1fe3e4aSElliott Hughes <path d="M7,7" fill="url(#rg4)"/> 429*e1fe3e4aSElliott Hughes </g> 430*e1fe3e4aSElliott Hughes </svg> 431*e1fe3e4aSElliott Hughes ]]> 432*e1fe3e4aSElliott Hughes </svgDoc> 433*e1fe3e4aSElliott Hughes """ 434*e1fe3e4aSElliott Hughes ), 435*e1fe3e4aSElliott Hughes ), 436*e1fe3e4aSElliott Hughes ( 437*e1fe3e4aSElliott Hughes # 'glyph12' contains a style attribute with inline CSS declarations that 438*e1fe3e4aSElliott Hughes # contains references to a gradient fill and a clipPath: we keep those 439*e1fe3e4aSElliott Hughes "12", 440*e1fe3e4aSElliott Hughes _lines( 441*e1fe3e4aSElliott Hughes """\ 442*e1fe3e4aSElliott Hughes <svgDoc endGlyphID="1" startGlyphID="1"> 443*e1fe3e4aSElliott Hughes <![CDATA[<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> 444*e1fe3e4aSElliott Hughes <defs> 445*e1fe3e4aSElliott Hughes <linearGradient id="lg1" x1="50" x2="50" y1="80" y2="80" gradientUnits="userSpaceOnUse"> 446*e1fe3e4aSElliott Hughes <stop stop-color="#A47B62" offset="0"/> 447*e1fe3e4aSElliott Hughes <stop stop-color="#AD8264" offset="1.0"/> 448*e1fe3e4aSElliott Hughes </linearGradient> 449*e1fe3e4aSElliott Hughes <clipPath id="c1"> 450*e1fe3e4aSElliott Hughes <circle cx="10" cy="10" r="1"/> 451*e1fe3e4aSElliott Hughes </clipPath> 452*e1fe3e4aSElliott Hughes </defs> 453*e1fe3e4aSElliott Hughes <g id="glyph1"> 454*e1fe3e4aSElliott Hughes <path d="M7,7" style="fill:url(#lg1);stroke:red;clip-path:url(#c1)"/> 455*e1fe3e4aSElliott Hughes </g> 456*e1fe3e4aSElliott Hughes </svg> 457*e1fe3e4aSElliott Hughes ]]> 458*e1fe3e4aSElliott Hughes </svgDoc> 459*e1fe3e4aSElliott Hughes """ 460*e1fe3e4aSElliott Hughes ), 461*e1fe3e4aSElliott Hughes ), 462*e1fe3e4aSElliott Hughes ], 463*e1fe3e4aSElliott Hughes) 464*e1fe3e4aSElliott Hughesdef test_subset_svg_with_references( 465*e1fe3e4aSElliott Hughes empty_svg_font, tmp_path, subset_gids, expected_xml 466*e1fe3e4aSElliott Hughes): 467*e1fe3e4aSElliott Hughes font = empty_svg_font 468*e1fe3e4aSElliott Hughes 469*e1fe3e4aSElliott Hughes font["SVG "].docList.append((COMPLEX_SVG, 1, 12)) 470*e1fe3e4aSElliott Hughes svg_font_path = tmp_path / "TestSVG.ttf" 471*e1fe3e4aSElliott Hughes font.save(svg_font_path) 472*e1fe3e4aSElliott Hughes subset_path = svg_font_path.with_suffix(".subset.ttf") 473*e1fe3e4aSElliott Hughes 474*e1fe3e4aSElliott Hughes subset.main( 475*e1fe3e4aSElliott Hughes [ 476*e1fe3e4aSElliott Hughes str(svg_font_path), 477*e1fe3e4aSElliott Hughes f"--output-file={subset_path}", 478*e1fe3e4aSElliott Hughes f"--gids={subset_gids}", 479*e1fe3e4aSElliott Hughes "--pretty-svg", 480*e1fe3e4aSElliott Hughes ] 481*e1fe3e4aSElliott Hughes ) 482*e1fe3e4aSElliott Hughes subset_font = TTFont(subset_path) 483*e1fe3e4aSElliott Hughes 484*e1fe3e4aSElliott Hughes if expected_xml is not None: 485*e1fe3e4aSElliott Hughes assert getXML(subset_font["SVG "].toXML, subset_font) == expected_xml 486*e1fe3e4aSElliott Hughes else: 487*e1fe3e4aSElliott Hughes assert "SVG " not in subset_font 488*e1fe3e4aSElliott Hughes 489*e1fe3e4aSElliott Hughes 490*e1fe3e4aSElliott Hughesdef test_subset_svg_empty_table(empty_svg_font, tmp_path): 491*e1fe3e4aSElliott Hughes font = empty_svg_font 492*e1fe3e4aSElliott Hughes 493*e1fe3e4aSElliott Hughes svg = new_svg() 494*e1fe3e4aSElliott Hughes etree.SubElement(svg, "rect", {"id": "glyph1", "x": "1", "y": "2"}) 495*e1fe3e4aSElliott Hughes font["SVG "].docList.append((etree.tostring(svg).decode(), 1, 1)) 496*e1fe3e4aSElliott Hughes 497*e1fe3e4aSElliott Hughes svg_font_path = tmp_path / "TestSVG.ttf" 498*e1fe3e4aSElliott Hughes font.save(svg_font_path) 499*e1fe3e4aSElliott Hughes subset_path = svg_font_path.with_suffix(".subset.ttf") 500*e1fe3e4aSElliott Hughes 501*e1fe3e4aSElliott Hughes # there's no gid=2 in SVG table, drop the empty table 502*e1fe3e4aSElliott Hughes subset.main([str(svg_font_path), f"--output-file={subset_path}", f"--gids=2"]) 503*e1fe3e4aSElliott Hughes 504*e1fe3e4aSElliott Hughes assert "SVG " not in TTFont(subset_path) 505*e1fe3e4aSElliott Hughes 506*e1fe3e4aSElliott Hughes 507*e1fe3e4aSElliott Hughesdef test_subset_svg_missing_glyph(empty_svg_font, tmp_path): 508*e1fe3e4aSElliott Hughes font = empty_svg_font 509*e1fe3e4aSElliott Hughes 510*e1fe3e4aSElliott Hughes svg = new_svg() 511*e1fe3e4aSElliott Hughes etree.SubElement(svg, "rect", {"id": "glyph1", "x": "1", "y": "2"}) 512*e1fe3e4aSElliott Hughes font["SVG "].docList.append( 513*e1fe3e4aSElliott Hughes ( 514*e1fe3e4aSElliott Hughes etree.tostring(svg).decode(), 515*e1fe3e4aSElliott Hughes 1, 516*e1fe3e4aSElliott Hughes # the range endGlyphID=2 declares two glyphs however our svg contains 517*e1fe3e4aSElliott Hughes # only one glyph element with id="glyph1", the "glyph2" one is absent. 518*e1fe3e4aSElliott Hughes # Techically this would be invalid according to the OT-SVG spec. 519*e1fe3e4aSElliott Hughes 2, 520*e1fe3e4aSElliott Hughes ) 521*e1fe3e4aSElliott Hughes ) 522*e1fe3e4aSElliott Hughes svg_font_path = tmp_path / "TestSVG.ttf" 523*e1fe3e4aSElliott Hughes font.save(svg_font_path) 524*e1fe3e4aSElliott Hughes subset_path = svg_font_path.with_suffix(".subset.ttf") 525*e1fe3e4aSElliott Hughes 526*e1fe3e4aSElliott Hughes # make sure we don't crash when we don't find the expected "glyph2" element 527*e1fe3e4aSElliott Hughes subset.main([str(svg_font_path), f"--output-file={subset_path}", f"--gids=1"]) 528*e1fe3e4aSElliott Hughes 529*e1fe3e4aSElliott Hughes subset_font = TTFont(subset_path) 530*e1fe3e4aSElliott Hughes assert getXML(subset_font["SVG "].toXML, subset_font) == [ 531*e1fe3e4aSElliott Hughes '<svgDoc endGlyphID="1" startGlyphID="1">', 532*e1fe3e4aSElliott Hughes ' <![CDATA[<svg xmlns="http://www.w3.org/2000/svg"><rect id="glyph1" x="1" y="2"/></svg>]]>', 533*e1fe3e4aSElliott Hughes "</svgDoc>", 534*e1fe3e4aSElliott Hughes ] 535*e1fe3e4aSElliott Hughes 536*e1fe3e4aSElliott Hughes # ignore the missing gid even if included in the subset; in this test case we 537*e1fe3e4aSElliott Hughes # end up with an empty svg document--which is dropped, along with the empty table 538*e1fe3e4aSElliott Hughes subset.main([str(svg_font_path), f"--output-file={subset_path}", f"--gids=2"]) 539*e1fe3e4aSElliott Hughes 540*e1fe3e4aSElliott Hughes assert "SVG " not in TTFont(subset_path) 541*e1fe3e4aSElliott Hughes 542*e1fe3e4aSElliott Hughes 543*e1fe3e4aSElliott Hughes@pytest.mark.parametrize( 544*e1fe3e4aSElliott Hughes "ints, expected_ranges", 545*e1fe3e4aSElliott Hughes [ 546*e1fe3e4aSElliott Hughes ((), []), 547*e1fe3e4aSElliott Hughes ((0,), [(0, 0)]), 548*e1fe3e4aSElliott Hughes ((0, 1), [(0, 1)]), 549*e1fe3e4aSElliott Hughes ((1, 1, 1, 1), [(1, 1)]), 550*e1fe3e4aSElliott Hughes ((1, 3), [(1, 1), (3, 3)]), 551*e1fe3e4aSElliott Hughes ((4, 2, 1, 3), [(1, 4)]), 552*e1fe3e4aSElliott Hughes ((1, 2, 4, 5, 6, 9, 13, 14, 15), [(1, 2), (4, 6), (9, 9), (13, 15)]), 553*e1fe3e4aSElliott Hughes ], 554*e1fe3e4aSElliott Hughes) 555*e1fe3e4aSElliott Hughesdef test_ranges(ints, expected_ranges): 556*e1fe3e4aSElliott Hughes assert list(ranges(ints)) == expected_ranges 557