"Fossies" - the Fresh Open Source Software Archive

Member "sk1-2.0rc4/src/uc2/formats/svg/svg_utils.py" (25 May 2019, 18441 Bytes) of package /linux/misc/sk1-2.0rc4.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "svg_utils.py" see the Fossies "Dox" file reference documentation.

    1 # -*- coding: utf-8 -*-
    2 #
    3 #  Copyright (C) 2016 by Igor E. Novikov
    4 #
    5 #  This program is free software: you can redistribute it and/or modify
    6 #  it under the terms of the GNU General Public License as published by
    7 #  the Free Software Foundation, either version 3 of the License, or
    8 #  (at your option) any later version.
    9 #
   10 #  This program is distributed in the hope that it will be useful,
   11 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
   12 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   13 #  GNU General Public License for more details.
   14 #
   15 #  You should have received a copy of the GNU General Public License
   16 #  along with this program.  If not, see <https://www.gnu.org/licenses/>.
   17 
   18 import logging
   19 import math
   20 import re
   21 from copy import deepcopy
   22 
   23 from uc2 import uc2const, libgeom, cms, sk2const
   24 from uc2.formats.svg import svg_colors
   25 from uc2.formats.xml_.xml_model import XMLObject, XmlContentText
   26 from uc2.libgeom import add_points, sub_points, mult_point
   27 
   28 PATH_STUB = [[], [], sk2const.CURVE_OPENED]
   29 F13 = 1.0 / 3.0
   30 F23 = 2.0 / 3.0
   31 LOG = logging.getLogger(__name__)
   32 
   33 
   34 def check_svg_attr(svg_obj, attr, value=None):
   35     if value is None: return attr in svg_obj.attrs
   36     if attr in svg_obj.attrs and svg_obj.attrs[attr] == value:
   37         return True
   38     return False
   39 
   40 
   41 def trafo_skewX(grad=0.0):
   42     angle = math.pi * grad / 180.0
   43     return [1.0, 0.0, math.tan(angle), 1.0, 0.0, 0.0]
   44 
   45 
   46 def trafo_skewY(grad=0.0):
   47     angle = math.pi * grad / 180.0
   48     return [1.0, math.tan(angle), 0.0, 1.0, 0.0, 0.0]
   49 
   50 
   51 def trafo_rotate(grad, cx=0.0, cy=0.0):
   52     return libgeom.trafo_rotate_grad(grad, cx, cy)
   53 
   54 
   55 def trafo_scale(m11, m22=None):
   56     if m22 is None: m22 = m11
   57     return [m11, 0.0, 0.0, m22, 0.0, 0.0]
   58 
   59 
   60 def trafo_translate(dx, dy=0.0):
   61     LOG.debug('translate %s', [1.0, 0.0, 0.0, 1.0, dx, dy])
   62     return [1.0, 0.0, 0.0, 1.0, dx, dy]
   63 
   64 
   65 def trafo_matrix(m11, m21, m12, m22, dx, dy):
   66     return [m11, m21, m12, m22, dx, dy]
   67 
   68 
   69 def get_svg_trafo(strafo):
   70     trafo = [] + libgeom.NORMAL_TRAFO
   71     trs = strafo.split(') ')
   72     trs.reverse()
   73     for tr in trs:
   74         tr += ')'
   75         tr = tr.replace(', ', ',').replace(' ', ',').replace('))', ')')
   76         try:
   77             code = compile('tr=trafo_' + tr, '<string>', 'exec')
   78             exec code
   79         except:
   80             continue
   81         trafo = libgeom.multiply_trafo(trafo, tr)
   82     return trafo
   83 
   84 
   85 def get_svg_level_trafo(svg_obj, trafo):
   86     tr = [] + libgeom.NORMAL_TRAFO
   87     if svg_obj.tag == 'use':
   88         if 'x' in svg_obj.attrs:
   89             tr[4] += float(svg_obj.attrs['x'])
   90         if 'y' in svg_obj.attrs:
   91             tr[5] += float(svg_obj.attrs['y'])
   92     if 'transform' in svg_obj.attrs:
   93         tr1 = get_svg_trafo(svg_obj.attrs['transform'])
   94         tr = libgeom.multiply_trafo(tr, tr1)
   95     tr = libgeom.multiply_trafo(tr, trafo)
   96     return tr
   97 
   98 
   99 def parse_svg_points(spoints):
  100     points = []
  101     spoints = re.sub('  *', ' ', spoints)
  102     spoints = spoints.replace('-', ',-').replace('e,-', 'e-')
  103     spoints = spoints.replace(', ,', ',').replace(' ', ',')
  104     pairs = spoints.replace(',,', ',').split(',')
  105     if not pairs[0]: pairs = pairs[1:]
  106     pairs = [pairs[i:i + 2] for i in range(0, len(pairs), 2)]
  107     for pair in pairs:
  108         try:
  109             points.append([float(pair[0]), float(pair[1])])
  110         except:
  111             continue
  112     return points
  113 
  114 
  115 def parse_svg_coords(scoords):
  116     scoords = scoords.strip().replace(',', ' ').replace('-', ' -')
  117     scoords = scoords.replace('e -', 'e-').strip()
  118     scoords = re.sub('  *', ' ', scoords)
  119     if scoords:
  120         processed_items = []
  121         for item in scoords.split(' '):
  122             count = item.count('.')
  123             if count > 1:
  124                 subitems = item.rsplit('.', count - 1)
  125                 processed_items.append(subitems[0])
  126                 processed_items += ['.' + s for s in subitems[1:]]
  127             else:
  128                 processed_items.append(item)
  129         return [float(item) for item in processed_items]
  130     return None
  131 
  132 
  133 def parse_svg_color(sclr, alpha=1.0, current_color=''):
  134     clr = deepcopy(svg_colors.SVG_COLORS['black'])
  135     clr[2] = alpha
  136     if sclr == 'currentColor' and current_color:
  137         sclr = current_color
  138     if sclr[0] == '#':
  139         if 'icc-color' in sclr:
  140             vals = sclr.split('icc-color(')[1].replace(')', '').split(',')
  141             if len(vals) == 5:
  142                 color_vals = []
  143                 try:
  144                     color_vals = [float(x) for x in vals[1:]]
  145                 except:
  146                     pass
  147                 if color_vals and len(color_vals) == 4:
  148                     return [uc2const.COLOR_CMYK, color_vals, alpha, '']
  149         elif 'device-cmyk' in sclr:
  150             vals = sclr.split('device-cmyk(')[1].replace(')', '').split(',')
  151             if len(vals) in (3, 4):
  152                 color_vals = []
  153                 try:
  154                     color_vals = [float(x) for x in vals[1:]]
  155                 except:
  156                     pass
  157                 if color_vals and len(color_vals) in (3, 4):
  158                     if len(color_vals) == 3: color_vals.append(0.0)
  159                     return [uc2const.COLOR_CMYK, color_vals, alpha, '']
  160 
  161         sclr = sclr.split(' ')[0]
  162         try:
  163             vals = cms.hexcolor_to_rgb(sclr)
  164             clr = [uc2const.COLOR_RGB, vals, alpha, '']
  165         except:
  166             pass
  167     elif sclr[:4] == 'rgb(':
  168         vals = sclr[4:].split(')')[0].split(',')
  169         if len(vals) == 3:
  170             decvals = []
  171             for val in vals:
  172                 val = val.strip()
  173                 if '%' in val:
  174                     decval = float(val.replace('%', ''))
  175                     if decval > 100.0: decval = 100.0
  176                     if decval < 0.0: decval = 0.0
  177                     decval = decval / 100.0
  178                 else:
  179                     decval = float(val)
  180                     if decval > 255.0: decval = 255.0
  181                     if decval < 0.0: decval = 0.0
  182                     decval = decval / 255.0
  183                 decvals.append(decval)
  184             clr = [uc2const.COLOR_RGB, decvals, alpha, '']
  185     else:
  186         if sclr in svg_colors.SVG_COLORS:
  187             clr = deepcopy(svg_colors.SVG_COLORS[sclr])
  188             clr[2] = alpha
  189     return clr
  190 
  191 
  192 def base_point(point):
  193     if len(point) == 2: return [] + point
  194     return [] + point[-1]
  195 
  196 
  197 def parse_svg_path_cmds(pathcmds):
  198     index = 0
  199     last = None
  200     last_index = 0
  201     cmds = []
  202     pathcmds = re.sub('  *', ' ', pathcmds)
  203     for item in pathcmds:
  204         if item in 'MmZzLlHhVvCcSsQqTtAa':
  205             if last:
  206                 coords = parse_svg_coords(pathcmds[last_index + 1:index])
  207                 cmds.append((last, coords))
  208             last = item
  209             last_index = index
  210         index += 1
  211 
  212     coords = parse_svg_coords(pathcmds[last_index + 1:index])
  213     cmds.append([last, coords])
  214 
  215     paths = []
  216     path = []
  217     cpoint = []
  218     rel_flag = False
  219     last_cmd = 'M'
  220     last_quad = None
  221 
  222     for cmd in cmds:
  223         if cmd[0] in 'Mm':
  224             if path: paths.append(path)
  225             path = deepcopy(PATH_STUB)
  226             rel_flag = cmd[0] == 'm'
  227             points = [cmd[1][i:i + 2] for i in range(0, len(cmd[1]), 2)]
  228             for point in points:
  229                 if cpoint and rel_flag:
  230                     point = add_points(base_point(cpoint), point)
  231                 if not path[0]:
  232                     path[0] = point
  233                 else:
  234                     path[1].append(point)
  235                 cpoint = point
  236         elif cmd[0] in 'Zz':
  237             p0 = [] + base_point(cpoint)
  238             p1 = [] + path[0]
  239             if not libgeom.is_equal_points(p0, p1, 8):
  240                 path[1].append([] + path[0])
  241             path[2] = sk2const.CURVE_CLOSED
  242             cpoint = [] + path[0]
  243         elif cmd[0] in 'Cc':
  244             rel_flag = cmd[0] == 'c'
  245             points = [cmd[1][i:i + 2] for i in range(0, len(cmd[1]), 2)]
  246             points = [points[i:i + 3] for i in range(0, len(points), 3)]
  247             for point in points:
  248                 if rel_flag:
  249                     point = [add_points(base_point(cpoint), point[0]),
  250                         add_points(base_point(cpoint), point[1]),
  251                         add_points(base_point(cpoint), point[2])]
  252                 qpoint = [] + point
  253                 qpoint.append(sk2const.NODE_CUSP)
  254                 path[1].append(qpoint)
  255                 cpoint = point
  256         elif cmd[0] in 'Ll':
  257             rel_flag = cmd[0] == 'l'
  258             points = [cmd[1][i:i + 2] for i in range(0, len(cmd[1]), 2)]
  259             for point in points:
  260                 if rel_flag:
  261                     point = add_points(base_point(cpoint), point)
  262                 path[1].append(point)
  263                 cpoint = point
  264         elif cmd[0] in 'Hh':
  265             rel_flag = cmd[0] == 'h'
  266             for x in cmd[1]:
  267                 dx, y = base_point(cpoint)
  268                 if rel_flag:
  269                     point = [x + dx, y]
  270                 else:
  271                     point = [x, y]
  272                 path[1].append(point)
  273                 cpoint = point
  274         elif cmd[0] in 'Vv':
  275             rel_flag = cmd[0] == 'v'
  276             for y in cmd[1]:
  277                 x, dy = base_point(cpoint)
  278                 if rel_flag:
  279                     point = [x, y + dy]
  280                 else:
  281                     point = [x, y]
  282                 path[1].append(point)
  283                 cpoint = point
  284         elif cmd[0] in 'Ss':
  285             rel_flag = cmd[0] == 's'
  286             points = [cmd[1][i:i + 2] for i in range(0, len(cmd[1]), 2)]
  287             points = [points[i:i + 2] for i in range(0, len(points), 2)]
  288             for point in points:
  289                 q = cpoint
  290                 p = cpoint
  291                 if len(cpoint) > 2:
  292                     q = cpoint[1]
  293                     p = cpoint[2]
  294                 p1 = sub_points(add_points(p, p), q)
  295                 if rel_flag:
  296                     p2 = add_points(base_point(cpoint), point[0])
  297                     p3 = add_points(base_point(cpoint), point[1])
  298                 else:
  299                     p2, p3 = point
  300                 point = [p1, p2, p3]
  301                 qpoint = [] + point
  302                 qpoint.append(sk2const.NODE_CUSP)
  303                 path[1].append(qpoint)
  304                 cpoint = point
  305 
  306         elif cmd[0] in 'Qq':
  307             rel_flag = cmd[0] == 'q'
  308             groups = [cmd[1][i:i + 4] for i in range(0, len(cmd[1]), 4)]
  309             for vals in groups:
  310                 p = base_point(cpoint)
  311                 if rel_flag:
  312                     q = add_points(p, [vals[0], vals[1]])
  313                     p3 = add_points(p, [vals[2], vals[3]])
  314                 else:
  315                     q = [vals[0], vals[1]]
  316                     p3 = [vals[2], vals[3]]
  317                 p1 = add_points(mult_point(p, F13), mult_point(q, F23))
  318                 p2 = add_points(mult_point(p3, F13), mult_point(q, F23))
  319 
  320                 point = [p1, p2, p3]
  321                 qpoint = [] + point
  322                 qpoint.append(sk2const.NODE_CUSP)
  323                 path[1].append(qpoint)
  324                 cpoint = point
  325                 last_quad = q
  326 
  327         elif cmd[0] in 'Tt':
  328             rel_flag = cmd[0] == 't'
  329             groups = [cmd[1][i:i + 2] for i in range(0, len(cmd[1]), 2)]
  330             if last_cmd not in 'QqTt' or last_quad is None:
  331                 last_quad = base_point(cpoint)
  332             for vals in groups:
  333                 p = base_point(cpoint)
  334                 q = sub_points(mult_point(p, 2.0), last_quad)
  335                 if rel_flag:
  336                     p3 = add_points(p, [vals[0], vals[1]])
  337                 else:
  338                     p3 = [vals[0], vals[1]]
  339                 p1 = add_points(mult_point(p, F13), mult_point(q, F23))
  340                 p2 = add_points(mult_point(p3, F13), mult_point(q, F23))
  341 
  342                 point = [p1, p2, p3]
  343                 qpoint = [] + point
  344                 qpoint.append(sk2const.NODE_CUSP)
  345                 path[1].append(qpoint)
  346                 cpoint = point
  347                 last_quad = q
  348 
  349         elif cmd[0] in 'Aa':
  350             rel_flag = cmd[0] == 'a'
  351             arcs = [cmd[1][i:i + 7] for i in range(0, len(cmd[1]), 7)]
  352 
  353             for arc in arcs:
  354                 cpoint = base_point(cpoint)
  355                 rev_flag = False
  356                 rx, ry, xrot, large_arc_flag, sweep_flag, x, y = arc
  357                 rx = abs(rx)
  358                 ry = abs(ry)
  359                 if rel_flag:
  360                     x += cpoint[0]
  361                     y += cpoint[1]
  362                 if cpoint == [x, y]: continue
  363                 if not rx or not ry:
  364                     path[1].append([x, y])
  365                     continue
  366 
  367                 vector = [[] + cpoint, [x, y]]
  368                 if sweep_flag:
  369                     vector = [[x, y], [] + cpoint]
  370                     rev_flag = True
  371                 cpoint = [x, y]
  372 
  373                 dir_tr = libgeom.trafo_rotate_grad(-xrot)
  374 
  375                 if rx > ry:
  376                     tr = [1.0, 0.0, 0.0, rx / ry, 0.0, 0.0]
  377                     r = rx
  378                 else:
  379                     tr = [ry / rx, 0.0, 0.0, 1.0, 0.0, 0.0]
  380                     r = ry
  381 
  382                 dir_tr = libgeom.multiply_trafo(dir_tr, tr)
  383                 vector = libgeom.apply_trafo_to_points(vector, dir_tr)
  384 
  385                 l = libgeom.distance(*vector)
  386 
  387                 if l > 2.0 * r: r = l / 2.0
  388 
  389                 mp = libgeom.midpoint(*vector)
  390 
  391                 tr0 = libgeom.trafo_rotate(math.pi / 2.0, mp[0], mp[1])
  392                 pvector = libgeom.apply_trafo_to_points(vector, tr0)
  393 
  394                 k = math.sqrt(r * r - l * l / 4.0)
  395                 if large_arc_flag:
  396                     center = libgeom.midpoint(mp,
  397                         pvector[1], 2.0 * k / l)
  398                 else:
  399                     center = libgeom.midpoint(mp,
  400                         pvector[0], 2.0 * k / l)
  401 
  402                 angle1 = libgeom.get_point_angle(vector[0], center)
  403                 angle2 = libgeom.get_point_angle(vector[1], center)
  404 
  405                 da = angle2 - angle1
  406                 start = angle1
  407                 end = angle2
  408                 if large_arc_flag:
  409                     if -math.pi >= da or da <= math.pi:
  410                         start = angle2
  411                         end = angle1
  412                         rev_flag = not rev_flag
  413                 else:
  414                     if -math.pi <= da or da >= math.pi:
  415                         start = angle2
  416                         end = angle1
  417                         rev_flag = not rev_flag
  418 
  419                 pth = libgeom.get_circle_paths(start, end,
  420                     sk2const.ARC_ARC)[0]
  421 
  422                 if rev_flag:
  423                     pth = libgeom.reverse_path(pth)
  424 
  425                 points = pth[1]
  426                 for point in points:
  427                     if len(point) == 3:
  428                         point.append(sk2const.NODE_CUSP)
  429 
  430                 tr0 = [1.0, 0.0, 0.0, 1.0, -0.5, -0.5]
  431                 points = libgeom.apply_trafo_to_points(points, tr0)
  432 
  433                 tr1 = [2.0 * r, 0.0, 0.0, 2.0 * r, 0.0, 0.0]
  434                 points = libgeom.apply_trafo_to_points(points, tr1)
  435 
  436                 tr2 = [1.0, 0.0, 0.0, 1.0, center[0], center[1]]
  437                 points = libgeom.apply_trafo_to_points(points, tr2)
  438 
  439                 tr3 = libgeom.invert_trafo(dir_tr)
  440                 points = libgeom.apply_trafo_to_points(points, tr3)
  441 
  442                 for point in points:
  443                     path[1].append(point)
  444 
  445         last_cmd = cmd[0]
  446 
  447     if path: paths.append(path)
  448     return paths
  449 
  450 
  451 def parse_svg_stops(stops, current_color):
  452     sk2_stops = []
  453     for stop in stops:
  454         if not stop.tag == 'stop': continue
  455         offset = stop.attrs['offset']
  456         if offset[-1] == '%':
  457             offset = float(offset[:-1]) / 100.0
  458         else:
  459             offset = float(offset)
  460 
  461         alpha = 1.0
  462         sclr = 'black'
  463         if 'stop-opacity' in stop.attrs:
  464             alpha = float(stop.attrs['stop-opacity'])
  465         if 'stop-color' in stop.attrs:
  466             sclr = stop.attrs['stop-color']
  467 
  468         if 'style' in stop.attrs:
  469             style = {}
  470             stls = stop.attrs['style'].split(';')
  471             for stl in stls:
  472                 vals = stl.split(':')
  473                 if len(vals) == 2:
  474                     style[vals[0].strip()] = vals[1].strip()
  475             if 'stop-opacity' in style:
  476                 alpha = float(style['stop-opacity'])
  477             if 'stop-color' in style:
  478                 sclr = style['stop-color'].strip()
  479 
  480         clr = parse_svg_color(sclr, alpha, current_color)
  481         sk2_stops.append([offset, clr])
  482     return sk2_stops
  483 
  484 
  485 def parse_svg_text(objs):
  486     ret = ''
  487     for obj in objs:
  488         if obj.is_content():
  489             text = obj.text.replace('\n', '').replace('\r', '')
  490             ret += re.sub('  *', ' ', text)
  491         elif obj.childs:
  492             ret += parse_svg_text(obj.childs)
  493             if 'sodipodi:role' in obj.attrs and \
  494                             obj.attrs['sodipodi:role'].strip() == 'line':
  495                 ret += '\n'
  496     ret = ret.lstrip()
  497     if ret and ret[-1] == '\n': ret = ret[:-1]
  498     return ret
  499 
  500 
  501 def create_xmlobj(tag, attrs={}):
  502     obj = XMLObject(tag)
  503     if attrs: obj.attrs = attrs
  504     return obj
  505 
  506 
  507 def create_nl():
  508     return create_spacer()
  509 
  510 
  511 def create_spacer(txt='\n'):
  512     return XmlContentText(txt)
  513 
  514 
  515 def create_rect(x, y, w, h):
  516     attrs = {
  517         'x': str(x),
  518         'y': str(y),
  519         'width': str(w),
  520         'height': str(h)
  521     }
  522     return create_xmlobj('rect', attrs)
  523 
  524 
  525 def translate_style_dict(style):
  526     ret = ''
  527     for item in style.keys():
  528         ret += '%s:%s;' % (item, style[item])
  529     return ret
  530 
  531 
  532 def point_to_str(point):
  533     return ' %s,%s' % (str(round(point[0], 4)), str(round(point[1], 4)))
  534 
  535 
  536 def translate_paths_to_d(paths):
  537     ret = ''
  538     for path in paths:
  539         cmd = 'M'
  540         ret += ' M' + point_to_str(path[0])
  541         for item in path[1]:
  542             if len(item) == 2:
  543                 if not cmd == 'L':
  544                     cmd = 'L'
  545                     ret += ' L'
  546                 ret += point_to_str(item)
  547             else:
  548                 if not cmd == 'C':
  549                     cmd = 'C'
  550                     ret += ' C'
  551                 ret += point_to_str(item[0])
  552                 ret += point_to_str(item[1])
  553                 ret += point_to_str(item[2])
  554         if path[2] == sk2const.CURVE_CLOSED:
  555             ret += ' Z'
  556     return ret.strip()