77
88
99from math import floor , ceil , log10 , sin , cos , pi , sqrt , atan2 , degrees , radians , exp
10- import json
1110import base64
1211from itertools import chain
1312
3332 system_symbols_dict ,
3433 from_python ,
3534)
35+ from mathics .core .formatter import lookup_method
36+
3637from mathics .builtin .drawing .colors import convert as convert_color
3738from mathics .core .numbers import machine_epsilon
3839
@@ -323,7 +324,11 @@ def _extract_graphics(graphics, format, evaluation):
323324 if format == "asy" :
324325 code = "\n " .join (element .to_asy () for element in elements .elements )
325326 elif format == "svg" :
326- code = elements .to_svg ()
327+ format_fn = lookup_method (elements , "svg" )
328+ if format_fn is not None :
329+ code = format_fn (elements )
330+ else :
331+ code = elements .to_svg ()
327332 else :
328333 raise NotImplementedError
329334
@@ -1231,26 +1236,6 @@ def extent(self):
12311236 )
12321237 return result
12331238
1234- def to_svg (self , offset = None ):
1235- l = self .style .get_line_width (face_element = True )
1236- x1 , y1 = self .p1 .pos ()
1237- x2 , y2 = self .p2 .pos ()
1238- xmin = min (x1 , x2 )
1239- ymin = min (y1 , y2 )
1240- w = max (x1 , x2 ) - xmin
1241- h = max (y1 , y2 ) - ymin
1242- if offset :
1243- x1 , x2 = x1 + offset [0 ], x2 + offset [0 ]
1244- y1 , y2 = y1 + offset [1 ], y2 + offset [1 ]
1245- style = create_css (self .edge_color , self .face_color , l )
1246- return '<rect x="%f" y="%f" width="%f" height="%f" style="%s" />' % (
1247- xmin ,
1248- ymin ,
1249- w ,
1250- h ,
1251- style ,
1252- )
1253-
12541239 def to_asy (self ):
12551240 l = self .style .get_line_width (face_element = True )
12561241 x1 , y1 = self .p1 .pos ()
@@ -1302,21 +1287,6 @@ def extent(self):
13021287 ry += l
13031288 return [(x - rx , y - ry ), (x - rx , y + ry ), (x + rx , y - ry ), (x + rx , y + ry )]
13041289
1305- def to_svg (self , offset = None ):
1306- x , y = self .c .pos ()
1307- rx , ry = self .r .pos ()
1308- rx -= x
1309- ry = y - ry
1310- l = self .style .get_line_width (face_element = self .face_element )
1311- style = create_css (self .edge_color , self .face_color , stroke_width = l )
1312- return '<ellipse cx="%f" cy="%f" rx="%f" ry="%f" style="%s" />' % (
1313- x ,
1314- y ,
1315- rx ,
1316- ry ,
1317- style ,
1318- )
1319-
13201290 def to_asy (self ):
13211291 x , y = self .c .pos ()
13221292 rx , ry = self .r .pos ()
@@ -1390,11 +1360,15 @@ def _arc_params(self):
13901360 return x , y , abs (rx ), abs (ry ), sx , sy , ex , ey , large_arc
13911361
13921362 def to_svg (self , offset = None ):
1363+ # FIXME: figure out how to put in svg.py
13931364 if self .arc is None :
13941365 return super (_ArcBox , self ).to_svg (offset )
13951366
13961367 x , y , rx , ry , sx , sy , ex , ey , large_arc = self ._arc_params ()
13971368
1369+ format_fn = lookup_method (self , "svg" )
1370+ if format_fn is not None :
1371+ return format_fn (self , offset )
13981372 def path (closed ):
13991373 if closed :
14001374 yield "M %f,%f" % (x , y )
@@ -1522,26 +1496,6 @@ def init(self, graphics, style, item=None):
15221496 else :
15231497 raise BoxConstructError
15241498
1525- def to_svg (self , offset = None ):
1526- point_size , _ = self .style .get_style (PointSize , face_element = False )
1527- if point_size is None :
1528- point_size = PointSize (self .graphics , value = 0.005 )
1529- size = point_size .get_size ()
1530-
1531- style = create_css (
1532- edge_color = self .edge_color , stroke_width = 0 , face_color = self .face_color
1533- )
1534- svg = ""
1535- for line in self .lines :
1536- for coords in line :
1537- svg += '<circle cx="%f" cy="%f" r="%f" style="%s" />' % (
1538- coords .pos ()[0 ],
1539- coords .pos ()[1 ],
1540- size ,
1541- style ,
1542- )
1543- return svg
1544-
15451499 def to_asy (self ):
15461500 pen = create_pens (face_color = self .face_color , is_face_element = False )
15471501
@@ -1586,17 +1540,6 @@ def init(self, graphics, style, item=None, lines=None):
15861540 else :
15871541 raise BoxConstructError
15881542
1589- def to_svg (self , offset = None ):
1590- l = self .style .get_line_width (face_element = False )
1591- style = create_css (edge_color = self .edge_color , stroke_width = l )
1592- svg = ""
1593- for line in self .lines :
1594- svg += '<polyline points="%s" style="%s" />' % (
1595- " " .join (["%f,%f" % coords .pos () for coords in line ]),
1596- style ,
1597- )
1598- return svg
1599-
16001543 def to_asy (self ):
16011544 l = self .style .get_line_width (face_element = False )
16021545 pen = create_pens (edge_color = self .edge_color , stroke_width = l )
@@ -1741,16 +1684,6 @@ def init(self, graphics, style, item, options):
17411684 raise BoxConstructError
17421685 self .spline_degree = spline_degree .get_int_value ()
17431686
1744- def to_svg (self , offset = None ):
1745- l = self .style .get_line_width (face_element = False )
1746- style = create_css (edge_color = self .edge_color , stroke_width = l )
1747-
1748- svg = ""
1749- for line in self .lines :
1750- s = " " .join (_svg_bezier ((self .spline_degree , [xy .pos () for xy in line ])))
1751- svg += '<path d="%s" style="%s"/>' % (s , style )
1752- return svg
1753-
17541687 def to_asy (self ):
17551688 l = self .style .get_line_width (face_element = False )
17561689 pen = create_pens (edge_color = self .edge_color , stroke_width = l )
@@ -1828,22 +1761,6 @@ def parse_component(segments):
18281761 else :
18291762 raise BoxConstructError
18301763
1831- def to_svg (self , offset = None ):
1832- l = self .style .get_line_width (face_element = False )
1833- style = create_css (
1834- edge_color = self .edge_color , face_color = self .face_color , stroke_width = l
1835- )
1836-
1837- def components ():
1838- for component in self .components :
1839- transformed = [(k , [xy .pos () for xy in p ]) for k , p in component ]
1840- yield " " .join (_svg_bezier (* transformed )) + " Z"
1841-
1842- return '<path d="%s" style="%s" fill-rule="evenodd"/>' % (
1843- " " .join (components ()),
1844- style ,
1845- )
1846-
18471764 def to_asy (self ):
18481765 l = self .style .get_line_width (face_element = False )
18491766 pen = create_pens (edge_color = self .edge_color , stroke_width = l )
@@ -1933,32 +1850,6 @@ def process_option(self, name, value):
19331850 else :
19341851 raise BoxConstructError
19351852
1936- def to_svg (self , offset = None ):
1937- l = self .style .get_line_width (face_element = True )
1938- if self .vertex_colors is None :
1939- face_color = self .face_color
1940- else :
1941- face_color = None
1942- style = create_css (
1943- edge_color = self .edge_color , face_color = face_color , stroke_width = l
1944- )
1945- svg = ""
1946- if self .vertex_colors is not None :
1947- mesh = []
1948- for index , line in enumerate (self .lines ):
1949- data = [
1950- [coords .pos (), color .to_js ()]
1951- for coords , color in zip (line , self .vertex_colors [index ])
1952- ]
1953- mesh .append (data )
1954- svg += '<meshgradient data="%s" />' % json .dumps (mesh )
1955- for line in self .lines :
1956- svg += '<polygon points="%s" style="%s" />' % (
1957- " " .join ("%f,%f" % coords .pos () for coords in line ),
1958- style ,
1959- )
1960- return svg
1961-
19621853 def to_asy (self ):
19631854 l = self .style .get_line_width (face_element = True )
19641855 if self .vertex_colors is None :
@@ -2514,23 +2405,6 @@ def draw(px, py, vx, vy, t1, s):
25142405
25152406 return make
25162407
2517- def to_svg (self , offset = None ):
2518- width = self .style .get_line_width (face_element = False )
2519- style = create_css (edge_color = self .edge_color , stroke_width = width )
2520- polyline = self .curve .make_draw_svg (style )
2521-
2522- arrow_style = create_css (face_color = self .edge_color , stroke_width = width )
2523-
2524- def polygon (points ):
2525- yield '<polygon points="'
2526- yield " " .join ("%f,%f" % xy for xy in points )
2527- yield '" style="%s" />' % arrow_style
2528-
2529- extent = self .graphics .view_width or 0
2530- default_arrow = self ._default_arrow (polygon )
2531- custom_arrow = self ._custom_arrow ("svg" , _SVGTransform )
2532- return "" .join (self ._draw (polyline , default_arrow , custom_arrow , extent ))
2533-
25342408 def to_asy (self ):
25352409 width = self .style .get_line_width (face_element = False )
25362410 pen = create_pens (edge_color = self .edge_color , stroke_width = width )
@@ -2617,35 +2491,6 @@ def extent(self):
26172491 y = p [1 ] - h / 2.0 + opos [1 ] * h / 2.0
26182492 return [(x , y ), (x + w , y + h )]
26192493
2620- def to_svg (self , offset = None ):
2621- x , y = self .pos .pos ()
2622- if offset :
2623- x = x + offset [0 ]
2624- y = y + offset [1 ]
2625-
2626- if hasattr (self .content , "to_svg" ):
2627- content = self .content .to_svg (noheader = True , offset = (x , y ))
2628- svg = "\n " + content + "\n "
2629- else :
2630- css_style = create_css (
2631- font_color = self .color ,
2632- edge_color = self .color ,
2633- face_color = self .color ,
2634- opacity = self .opacity ,
2635- )
2636- text_pos_opts = f'x="{ x } " y="{ y } " ox="{ self .opos [0 ]} " oy="{ self .opos [1 ]} "'
2637- # FIXME: don't hard code text_style_opts, but allow these to be adjustable.
2638- text_style_opts = "text-anchor:middle; dominant-baseline:middle;"
2639- content = self .content .boxes_to_text (evaluation = self .graphics .evaluation )
2640- svg = f'<text { text_pos_opts } style="{ text_style_opts } { css_style } ">{ content } </text>'
2641-
2642- # content = self.content.boxes_to_mathml(evaluation=self.graphics.evaluation)
2643- # style = create_css(font_color=self.color)
2644- # svg = (
2645- # '<foreignObject x="%f" y="%f" ox="%f" oy="%f" style="%s">'
2646- # "<math>%s</math></foreignObject>")
2647-
2648- return svg
26492494
26502495 def to_asy (self ):
26512496 x , y = self .pos .pos ()
@@ -2960,9 +2805,6 @@ def extent(self, completely_visible_only=False):
29602805 ymax *= 2
29612806 return xmin , xmax , ymin , ymax
29622807
2963- def to_svg (self , offset = None ):
2964- return "\n " .join (element .to_svg (offset ) for element in self .elements )
2965-
29662808 def to_asy (self ):
29672809 return "\n " .join (element .to_asy () for element in self .elements )
29682810
@@ -3250,53 +3092,6 @@ def boxes_to_tex(self, leaves=None, **options):
32503092
32513093 return tex
32523094
3253- def to_svg (self , leaves = None , ** options ):
3254- if not leaves :
3255- leaves = self ._leaves
3256-
3257- data = options .get ("data" , None )
3258- if data :
3259- elements , xmin , xmax , ymin , ymax , w , h , width , height = data
3260- else :
3261- elements , calc_dimensions = self ._prepare_elements (
3262- leaves , options , neg_y = True
3263- )
3264- xmin , xmax , ymin , ymax , w , h , width , height = calc_dimensions ()
3265-
3266- elements .view_width = w
3267-
3268- svg = elements .to_svg (offset = options .get ("offset" , None ))
3269-
3270- if self .background_color is not None :
3271- svg = '<rect x="%f" y="%f" width="%f" height="%f" style="fill:%s"/>%s' % (
3272- xmin ,
3273- ymin ,
3274- w ,
3275- h ,
3276- self .background_color .to_css ()[0 ],
3277- svg ,
3278- )
3279-
3280- xmin -= 1
3281- ymin -= 1
3282- w += 2
3283- h += 2
3284-
3285- if options .get ("noheader" , False ):
3286- return svg
3287- svg_xml = """
3288- <svg xmlns:svg="http://www.w3.org/2000/svg"
3289- xmlns="http://www.w3.org/2000/svg"
3290- version="1.1"
3291- viewBox="%s">
3292- %s
3293- </svg>
3294- """ % (
3295- " " .join ("%f" % t for t in (xmin , ymin , w , h )),
3296- svg ,
3297- )
3298- return svg_xml # , width, height
3299-
33003095 def boxes_to_mathml (self , leaves = None , ** options ):
33013096 if not leaves :
33023097 leaves = self ._leaves
@@ -3305,7 +3100,13 @@ def boxes_to_mathml(self, leaves=None, **options):
33053100 xmin , xmax , ymin , ymax , w , h , width , height = calc_dimensions ()
33063101 data = (elements , xmin , xmax , ymin , ymax , w , h , width , height )
33073102
3308- svg_xml = self .to_svg (leaves , data = data , ** options )
3103+ format_fn = lookup_method (elements , "svg" )
3104+ if format_fn is not None :
3105+ svg = format_fn (leaves , data = data , ** options )
3106+ else :
3107+ svg = self .to_svg (leaves , data = data , ** options )
3108+
3109+
33093110 # mglyph, which is what we have been using, is bad because MathML standard changed.
33103111 # metext does not work because the way in which we produce the svg images is also based on this outdated mglyph behaviour.
33113112 # template = '<mtext width="%dpx" height="%dpx"><img width="%dpx" height="%dpx" src="data:image/svg+xml;base64,%s"/></mtext>'
@@ -3318,7 +3119,7 @@ def boxes_to_mathml(self, leaves=None, **options):
33183119 # int(height),
33193120 int (width ),
33203121 int (height ),
3321- base64 .b64encode (svg_xml .encode ("utf8" )).decode ("utf8" ),
3122+ base64 .b64encode (svg .encode ("utf8" )).decode ("utf8" ),
33223123 )
33233124
33243125 def axis_ticks (self , xmin , xmax ):
0 commit comments