def __add_detail_features(self, layer, detail_layer, lowres_features): #===================================================================== extra_details = [] for feature in lowres_features: self.__map_properties.update_feature_properties(feature) hires_layer_id = feature.get_property('details') hires_layer = self.__layer_dict.get(hires_layer_id) if hires_layer is None: log.warn("Cannot find details layer '{}'".format( feature.get_property('details'))) continue boundary_feature = hires_layer.boundary_feature if boundary_feature is None: raise KeyError("Cannot find boundary of '{}' layer".format( hires_layer.id)) # Calculate transformation to map source shapes to the destination # NOTE: We reorder the coordinates of the bounding rectangles so that the first # coordinate is the top left-most one. This should ensure that the source # and destination rectangles align as intended, without output features # being rotated by some multiple of 90 degrees. src = np.array(normalised_coords( boundary_feature.geometry.minimum_rotated_rectangle), dtype="float32") dst = np.array(normalised_coords( feature.geometry.minimum_rotated_rectangle), dtype="float32") transform = Transform(cv2.getPerspectiveTransform(src, dst)) minzoom = feature.get_property('maxzoom') + 1 if feature.get_property('type') != 'nerve': # Set the feature's geometry to that of the high-resolution outline feature.geometry = transform.transform_geometry( boundary_feature.geometry) else: # nerve feature.del_property('maxzoom') if hires_layer.source.raster_source is not None: extent = transform.transform_extent(hires_layer.source.extent) layer.add_raster_layer('{}_{}'.format(detail_layer.id, hires_layer.id), extent, hires_layer.source, minzoom, local_world_to_base=transform) # The detail layer gets a scaled copy of each high-resolution feature for hires_feature in hires_layer.features: new_feature = self.__new_detail_feature( layer.id, detail_layer, minzoom, transform.transform_geometry(hires_feature.geometry), hires_feature.properties) if new_feature.has_property('details'): extra_details.append(new_feature) # If hires features that we've just added also have details then add them # to the detail layer if extra_details: self.__add_detail_features(layer, detail_layer, extra_details)
def __process_shape_list(self, shapes, transform, show_progress=False): #====================================================================== progress_bar = ProgressBar(show=show_progress, total=len(shapes), unit='shp', ncols=40, bar_format='{l_bar}{bar}| {n_fmt}/{total_fmt}') features = [] for shape in shapes: properties = {'tile-layer': 'features'} # Passed through to map viewer properties.update(self.source.properties_from_markup(shape.name)) if 'error' in properties: pass elif 'path' in properties: pass elif (shape.shape_type == MSO_SHAPE_TYPE.AUTO_SHAPE or shape.shape_type == MSO_SHAPE_TYPE.FREEFORM or isinstance(shape, pptx.shapes.connector.Connector)): geometry = self.__get_geometry(shape, properties, transform) feature = self.flatmap.new_feature(geometry, properties) features.append(feature) elif shape.shape_type == MSO_SHAPE_TYPE.GROUP: grouped_feature = self.__process_group(shape, properties, transform) if grouped_feature is not None: features.append(grouped_feature) elif (shape.shape_type == MSO_SHAPE_TYPE.TEXT_BOX or shape.shape_type == MSO_SHAPE_TYPE.PICTURE): pass else: log.warn('"{}" {} not processed...'.format(shape.name, str(shape.shape_type))) progress_bar.update(1) progress_bar.close() return features
def __process_sources(self): #=========================== # Make sure ``base`` and ``slides`` source kinds are processed first def kind_order(source): kind = source.get('kind', '') return ('0' if kind in ['base', 'slides'] else '1') + kind for layer_number, source in enumerate( sorted(self.__manifest.sources, key=kind_order)): id = source.get('id') kind = source.get('kind') href = source['href'] if kind == 'slides': source_layer = PowerpointSource(self.__flatmap, id, href) elif kind == 'image': if layer_number > 0 and 'boundary' not in source: raise ValueError('An image source must specify a boundary') source_layer = MBFSource(self.__flatmap, id, href, boundary_id=source.get('boundary'), exported=(layer_number == 0)) elif kind in ['base', 'details']: source_layer = SVGSource(self.__flatmap, id, href, kind) else: raise ValueError('Unsupported source kind: {}'.format(kind)) source_layer.process() for (kind, msg) in source_layer.errors: if kind == 'error': log.error(msg) else: log.warn(msg) self.__flatmap.add_source_layers(layer_number, source_layer) if len(self.__flatmap) == 0: raise ValueError('No map layers in sources...')
def __init__(self): self.__unknown_entities = [] self.__scigraph_key = os.environ.get('SCICRUNCH_API_KEY') if self.__scigraph_key is None: log.warn( 'Undefined SCICRUNCH_API_KEY: SciCrunch knowledge will not be looked up' )
def __line_from_edge(self, edge): node_0 = self.__graph.nodes[edge[0]] node_1 = self.__graph.nodes[edge[1]] if 'geometry' not in node_0 or 'geometry' not in node_1: log.warn('Edge {} nodes have no geometry'.format(edge)) else: return shapely.geometry.LineString( [node_0['geometry'].centroid, node_1['geometry'].centroid])
def __find_feature(self, id): #============================ features = self.__feature_map.features(id) if len(features) == 1: return features[0] elif len(features) == 0: log.warn('Unknown network feature: {}'.format(id)) else: log.warn('Multiple network features for: {}'.format(id)) return None
def __resolve_nodes_for_path(self, path_id, nodes): node_ids = [] for id in nodes: node_count = 0 for feature in self.__feature_map.features(id): if not feature.get_property('exclude'): node_id = feature.feature_id feature.set_property('nodeId', node_id) self.__node_paths[node_id].add(path_id) node_ids.append(node_id) node_count += 1 if node_count == 0: log.warn('Cannot find feature for node: {}'.format(id)) return node_ids
def process(self): #================= for n in range(len(self.__slides)): slide = self.__slides[n] slide_number = n + 1 slide_layer = PowerpointSlide(self, slide, slide_number) log('Slide {}, {}'.format(slide_number, slide_layer.id)) if settings.get('saveDrawML'): xml = open(os.path.join(self.flatmap.map_directory, '{}.xml'.format(slide_layer.id)), 'w') xml.write(slide.element.xml) xml.close() slide_layer.process() for error in self.errors: log.warn(error) else: self.add_layer(slide_layer)
def get_knowledge(self, entity): knowledge = {} if self.__scigraph_key is not None: params = { 'api_key': self.__scigraph_key, 'limit': 9999, } ontology = entity.split(':')[0] if ontology in INTERLEX_ONTOLOGIES: data = request_json( SCICRUNCH_INTERLEX_VOCAB.format(TERM=entity), params=params) if data is not None: knowledge['label'] = data.get('data', {}).get('label', entity) elif ontology in CONNECTIVITY_ONTOLOGIES: data = request_json( SCICRUNCH_CONNECTIVITY_NEURONS.format(NEURON_ID=entity), params=params) if data is not None: knowledge = Apinatomy.neuron_knowledge(entity, data) elif ontology in SPARC_ONTOLOGIES: data = request_json(SCICRUNCH_SPARC_VOCAB.format(TERM=entity), params=params) if data is not None: knowledge['label'] = data.get('labels', [entity])[0] elif entity.startswith(APINATOMY_MODEL_PREFIX): params['cypherQuery'] = Apinatomy.neurons_for_model_cypher( entity) data = request_json(SCICRUNCH_SPARC_CYPHER, params=params) if data is not None: knowledge = Apinatomy.model_knowledge(entity, data) if len(knowledge) == 0 and entity not in self.__unknown_entities: log.warn('Unknown anatomical entity: {}'.format(entity)) self.__unknown_entities.append(entity) return knowledge
def __init__(self, network, external_properties): self.__id = network.get('id') self.__centreline_graph = nx.Graph() self.__centreline_ids = [] self.__containing_features = defaultdict( set ) #! Centreline id --> set of feature ids that contain centreline self.__contained_centrelines = defaultdict( list) #! Feature id --> centrelines contained in feature self.__feature_map = None #! Assigned after ``maker`` has processed sources for centreline in network.get('centrelines', []): id = centreline.get('id') if id is None: log.error( f'Centreline in network {self.__id} does not have an id') elif id in self.__containing_features: log.error( f'Centreline {id} in network {self.__id} has a duplicated id' ) else: nodes = centreline.get('connects', []) if len(nodes) < 2: log.warn( f'Centreline {id} in network {self.__id} has too few nodes' ) else: self.__centreline_ids.append(id) edge_properties = {'id': id} if len(nodes) > 2: edge_properties['intermediates'] = nodes[1:-1] self.__containing_features[id] = set( centreline.get('containedIn', [])) for container_id in self.__containing_features[id]: self.__contained_centrelines[container_id].append(id) if external_properties.get_property( container_id, 'type') == 'nerve': edge_properties['nerve'] = container_id self.__centreline_graph.add_edge(nodes[0], nodes[-1], **edge_properties)
def __process_element(self, wrapped_element, transform, features, parent_properties, parent_style): #================================================================================================== element = wrapped_element.etree_element element_style = self.__style_matcher.element_style(wrapped_element, parent_style) properties = parent_properties.copy() if 'id' in properties: # We don't inherit `id` (or do we have a list of inheritable properties??) del properties['id'] properties.update(self.source.properties_from_markup(adobe_decode_markup(element))) if 'error' in properties: pass elif 'path' in properties: pass elif 'styling' in properties: pass elif element.tag in [SVG_NS('circle'), SVG_NS('ellipse'), SVG_NS('image'), SVG_NS('line'), SVG_NS('path'), SVG_NS('polyline'), SVG_NS('polygon'), SVG_NS('rect')]: geometry = self.__get_geometry(element, properties, transform) if geometry is None: return # Ignore element if fill is none and no stroke is specified if (element_style.get('fill', '#FFF') == 'none' and element_style.get('stroke', 'none') == 'none'): return feature = self.flatmap.new_feature(geometry, properties) features.append(feature) elif element.tag == SVG_NS('g'): grouped_feature = self.__process_group(wrapped_element, properties, transform, parent_style) if grouped_feature is not None: features.append(grouped_feature) elif element.tag in IGNORED_SVG_TAGS: pass else: log.warn('"{}" {} not processed...'.format(markup, element.tag))
def create_geometry(self, feature_map): #====================================== self.__feature_map = feature_map for edge in self.__centreline_graph.edges( data='id'): # Returns triples: (node, node, id) for node_id in edge[0:2]: self.__set_node_properties( self.__centreline_graph.nodes[node_id], node_id) feature = self.__find_feature(edge[2]) if feature is not None: bezier_path = feature.get_property('bezier-path') if bezier_path is None: log.warn('Centreline {} has no Bezier path'.format( feature.id)) else: bezier_start = bezier_path.pointAtTime(0) start_point = shapely.geometry.Point( bezier_start.x, bezier_start.y) end_node_0 = self.__centreline_graph.nodes[edge[0]].get( 'geometry') end_node_1 = self.__centreline_graph.nodes[edge[1]].get( 'geometry') if end_node_0 is not None and end_node_1 is not None: self.__centreline_graph.edges[ edge[0:2]]['geometry'] = bezier_path if start_point.distance( end_node_0) < start_point.distance(end_node_1): self.__centreline_graph.edges[ edge[0:2]]['start-node'] = edge[0] else: self.__centreline_graph.edges[ edge[0:2]]['start-node'] = edge[1] if settings.get('pathLayout', 'automatic') == 'automatic': # Construct the centreline scaffold for the network ##self.__centreline_scaffold = Sheath(self.__id, self.__graph) pass
def __get_geometry(self, element, properties, transform): #======================================================= ## ## Returns path element as a `shapely` object. ## coordinates = [] bezier_segments = [] moved = False first_point = None current_point = None closed = False path_tokens = [] T = transform@SVGTransform(element.attrib.get('transform')) if element.tag == SVG_NS('path'): path_tokens = list(parse_svg_path(element.attrib.get('d', ''))) elif element.tag == SVG_NS('rect'): x = length_as_pixels(element.attrib.get('x', 0)) y = length_as_pixels(element.attrib.get('y', 0)) width = length_as_pixels(element.attrib.get('width', 0)) height = length_as_pixels(element.attrib.get('height', 0)) rx = length_as_pixels(element.attrib.get('rx')) ry = length_as_pixels(element.attrib.get('ry')) if width == 0 or height == 0: return None if rx is None and ry is None: rx = ry = 0 elif ry is None: ry = rx elif rx is None: rx = ry rx = min(rx, width/2) ry = min(ry, height/2) if rx == 0 and ry == 0: path_tokens = ['M', x, y, 'H', x+width, 'V', y+height, 'H', x, 'V', y, 'Z'] else: path_tokens = ['M', x+rx, y, 'H', x+width-rx, 'A', rx, ry, 0, 0, 1, x+width, y+ry, 'V', y+height-ry, 'A', rx, ry, 0, 0, 1, x+width-rx, y+height, 'H', x+rx, 'A', rx, ry, 0, 0, 1, x, y+height-ry, 'V', y+ry, 'A', rx, ry, 0, 0, 1, x+rx, y, 'Z'] elif element.tag == SVG_NS('line'): x1 = length_as_pixels(element.attrib.get('x1', 0)) y1 = length_as_pixels(element.attrib.get('y1', 0)) x2 = length_as_pixels(element.attrib.get('x2', 0)) y2 = length_as_pixels(element.attrib.get('y2', 0)) path_tokens = ['M', x1, y1, x2, y2] elif element.tag == SVG_NS('polyline'): points = element.attrib.get('points', '').replace(',', ' ').split() path_tokens = ['M'] + points elif element.tag == SVG_NS('polygon'): points = element.attrib.get('points', '').replace(',', ' ').split() path_tokens = ['M'] + points + ['Z'] elif element.tag == SVG_NS('circle'): cx = length_as_pixels(element.attrib.get('cx', 0)) cy = length_as_pixels(element.attrib.get('cy', 0)) r = length_as_pixels(element.attrib.get('r', 0)) if r == 0: return None path_tokens = ['M', cx+r, cy, 'A', r, r, 0, 0, 0, cx, cy-r, 'A', r, r, 0, 0, 0, cx-r, cy, 'A', r, r, 0, 0, 0, cx, cy+r, 'A', r, r, 0, 0, 0, cx+r, cy, 'Z'] elif element.tag == SVG_NS('ellipse'): cx = length_as_pixels(element.attrib.get('cx', 0)) cy = length_as_pixels(element.attrib.get('cy', 0)) rx = length_as_pixels(element.attrib.get('rx', 0)) ry = length_as_pixels(element.attrib.get('ry', 0)) if rx == 0 or ry == 0: return None path_tokens = ['M', cx+rx, cy, 'A', rx, ry, 0, 0, 0, cx, cy-ry, 'A', rx, ry, 0, 0, 0, cx-rx, cy, 'A', rx, ry, 0, 0, 0, cx, cy+ry, 'A', rx, ry, 0, 0, 0, cx+rx, cy, 'Z'] elif element.tag == SVG_NS('image'): if 'id' in properties or 'class' in properties: width = length_as_pixels(element.attrib.get('width', 0)) height = length_as_pixels(element.attrib.get('height', 0)) path_tokens = ['M', 0, 0, 'H', width, 'V', height, 'H', 0, 'V', 0, 'Z'] pos = 0 while pos < len(path_tokens): if isinstance(path_tokens[pos], str) and path_tokens[pos].isalpha(): cmd = path_tokens[pos] pos += 1 # Else repeat previous command with new coordinates # with `moveTo` becoming `lineTo` elif cmd == 'M': cmd = 'L' elif cmd == 'm': cmd = 'l' if cmd not in ['s', 'S']: second_cubic_control = None if cmd not in ['t', 'T']: second_quad_control = None if cmd in ['a', 'A']: params = [float(x) for x in path_tokens[pos:pos+7]] pos += 7 pt = params[5:7] if cmd == 'a': pt[0] += current_point[0] pt[1] += current_point[1] phi = radians(params[2]) path = bezier_path_from_arc_endpoints(tuple2(*params[0:2]), phi, *params[3:5], tuple2(*current_point), tuple2(*pt), T) bezier_segments.extend(path.asSegments()) coordinates.extend(bezier_sample(path)) current_point = pt elif cmd in ['c', 'C', 's', 'S']: coords = [BezierPoint(*T.transform_point(current_point))] if cmd in ['c', 'C']: n_params = 6 else: n_params = 4 if second_cubic_control is None: coords.append(BezierPoint(*T.transform_point(current_point))) else: coords.append(BezierPoint(*T.transform_point( reflect_point(second_cubic_control, current_point)))) params = [float(x) for x in path_tokens[pos:pos+n_params]] pos += n_params for n in range(0, n_params, 2): pt = params[n:n+2] if cmd.islower(): pt[0] += current_point[0] pt[1] += current_point[1] if n == (n_params - 4): second_cubic_control = pt coords.append(BezierPoint(*T.transform_point(pt))) bz = CubicBezier(*coords) bezier_segments.append(bz) coordinates.extend(bezier_sample(bz)) current_point = pt elif cmd in ['l', 'L', 'h', 'H', 'v', 'V']: if cmd in ['l', 'L']: params = [float(x) for x in path_tokens[pos:pos+2]] pos += 2 pt = params[0:2] if cmd == 'l': pt[0] += current_point[0] pt[1] += current_point[1] else: param = float(path_tokens[pos]) pos += 1 if cmd == 'h': param += current_point[0] elif cmd == 'v': param += current_point[1] if cmd in ['h', 'H']: pt = [param, current_point[1]] else: pt = [current_point[0], param] if moved: coordinates.append(T.transform_point(current_point)) moved = False coordinates.append(T.transform_point(pt)) current_point = pt elif cmd in ['m', 'M']: params = [float(x) for x in path_tokens[pos:pos+2]] pos += 2 pt = params[0:2] if first_point is None: # First `m` in a path is treated as `M` first_point = pt else: if cmd == 'm': pt[0] += current_point[0] pt[1] += current_point[1] current_point = pt moved = True elif cmd in ['q', 'Q', 't', 'T']: coords = [BezierPoint(*T.transform_point(current_point))] if cmd in ['q', 'Q']: n_params = 4 else: n_params = 2 if second_quad_control is None: coords.append(BezierPoint(*T.transform_point(current_point))) else: coords.append(BezierPoint(*T.transform_point( reflect_point(second_quad_control, current_point)))) params = [float(x) for x in path_tokens[pos:pos+n_params]] pos += n_params for n in range(0, n_params, 2): pt = params[n:n+2] if cmd.islower(): pt[0] += current_point[0] pt[1] += current_point[1] if n == (n_params - 4): second_quad_control = pt coords.append(BezierPoint(*T.transform_point(pt))) bz = QuadraticBezier(*coords) bezier_segments.append(bz) coordinates.extend(bezier_sample(bz)) current_point = pt elif cmd in ['z', 'Z']: if first_point is not None and current_point != first_point: coordinates.append(T.transform_point(first_point)) closed = True first_point = None else: log.warn('Unknown path command: {}'.format(cmd)) if len(bezier_segments) > 0: properties['bezier-path'] = BezierPath.fromSegments(bezier_segments) if closed and len(coordinates) >= 3: geometry = shapely.geometry.Polygon(coordinates) elif properties.get('closed', False) and len(coordinates) >= 3: # Return a polygon if flagged as `closed` coordinates.append(coordinates[0]) geometry = shapely.geometry.Polygon(coordinates) elif len(coordinates) >= 2: geometry = shapely.geometry.LineString(coordinates) else: geometry = None return geometry
def __get_geometry(self, shape, properties, transform): #====================================================== ## ## Returns shape's geometry as `shapely` object. ## coordinates = [] bezier_segments = [] pptx_geometry = Geometry(shape) for path in pptx_geometry.path_list: bbox = (shape.width, shape.height) if path.w is None or path.h is None else (path.w, path.h) T = transform@DrawMLTransform(shape, bbox) moved = False first_point = None current_point = None closed = False for c in path.getchildren(): if c.tag == DML('arcTo'): (wR, hR) = ((pptx_geometry.attrib_value(c, 'wR'), pptx_geometry.attrib_value(c, 'hR'))) stAng = radians(pptx_geometry.attrib_value(c, 'stAng')) swAng = radians(pptx_geometry.attrib_value(c, 'swAng')) p1 = ellipse_point(wR, hR, stAng) p2 = ellipse_point(wR, hR, stAng + swAng) pt = (current_point[0] - p1[0] + p2[0], current_point[1] - p1[1] + p2[1]) large_arc_flag = 1 if swAng >= math.pi else 0 path = bezier_path_from_arc_endpoints(tuple2(wR, hR), 0, large_arc_flag, 1, tuple2(*current_point), tuple2(*pt), T) bezier_segments.extend(path.asSegments()) coordinates.extend(bezier_sample(path)) current_point = pt elif c.tag == DML('close'): if first_point is not None and current_point != first_point: coordinates.append(T.transform_point(first_point)) closed = True first_point = None # Close current pptx_geometry and start a new one... elif c.tag == DML('cubicBezTo'): coords = [BezierPoint(*T.transform_point(current_point))] for p in c.getchildren(): pt = pptx_geometry.point(p) coords.append(BezierPoint(*T.transform_point(pt))) current_point = pt bz = CubicBezier(*coords) bezier_segments.append(bz) coordinates.extend(bezier_sample(bz)) elif c.tag == DML('lnTo'): pt = pptx_geometry.point(c.pt) if moved: coordinates.append(T.transform_point(current_point)) moved = False coordinates.append(T.transform_point(pt)) current_point = pt elif c.tag == DML('moveTo'): pt = pptx_geometry.point(c.pt) if first_point is None: first_point = pt current_point = pt moved = True elif c.tag == DML('quadBezTo'): coords = [BezierPoint(*T.transform_point(current_point))] for p in c.getchildren(): pt = pptx_geometry.point(p) coords.append(BezierPoint(*T.transform_point(pt))) current_point = pt bz = QuadraticBezier(*coords) bezier_segments.append(bz) coordinates.extend(bezier_sample(bz)) else: log.warn('Unknown path element: {}'.format(c.tag)) if len(bezier_segments) > 0: properties['bezier-path'] = BezierPath.fromSegments(bezier_segments) if closed: geometry = shapely.geometry.Polygon(coordinates) else: geometry = shapely.geometry.LineString(coordinates) if properties.get('closed', False): # Return a polygon if flagged as `closed` coordinates.append(coordinates[0]) return shapely.geometry.Polygon(coordinates) return geometry
def __path_from_tokens(tokens): #============================== moved = False first_point = None current_point = None closed = False path = skia.Path() pos = 0 while pos < len(tokens): if isinstance(tokens[pos], str) and tokens[pos].isalpha(): cmd = tokens[pos] pos += 1 # Else repeat previous command with new coordinates # with `moveTo` becoming `lineTo` elif cmd == 'M': cmd = 'L' elif cmd == 'm': cmd = 'l' if cmd not in ['s', 'S']: second_cubic_control = None if cmd not in ['t', 'T']: second_quad_control = None if cmd in ['a', 'A']: params = [float(x) for x in tokens[pos:pos+7]] pos += 7 pt = params[5:7] if cmd == 'a': pt[0] += current_point[0] pt[1] += current_point[1] phi = radians(params[2]) if moved: path.moveTo(*current_point) moved = False (rx, ry) = (params[0], params[1]) path.arcTo(rx, ry, degrees(phi), skia.Path.ArcSize.kSmall_ArcSize if params[3] == 0 else skia.Path.ArcSize.kLarge_ArcSize, skia.PathDirection.kCCW if params[4] == 0 else skia.PathDirection.kCW, *pt) current_point = pt elif cmd in ['c', 'C', 's', 'S']: if moved: path.moveTo(*current_point) moved = False if cmd in ['c', 'C']: n_params = 6 coords = [] else: n_params = 4 if second_cubic_control is None: coords = list(current_point) else: coords = list(reflect_point(second_cubic_control, current_point)) params = [float(x) for x in tokens[pos:pos+n_params]] pos += n_params for n in range(0, n_params, 2): pt = params[n:n+2] if cmd.islower(): pt[0] += current_point[0] pt[1] += current_point[1] if n == (n_params - 4): second_cubic_control = pt coords.extend(pt) path.cubicTo(*coords) current_point = pt elif cmd in ['l', 'L', 'h', 'H', 'v', 'V']: if cmd in ['l', 'L']: params = [float(x) for x in tokens[pos:pos+2]] pos += 2 pt = params[0:2] if cmd == 'l': pt[0] += current_point[0] pt[1] += current_point[1] else: param = float(tokens[pos]) pos += 1 if cmd == 'h': param += current_point[0] elif cmd == 'v': param += current_point[1] if cmd in ['h', 'H']: pt = [param, current_point[1]] else: pt = [current_point[0], param] if moved: path.moveTo(*current_point) moved = False path.lineTo(*pt) current_point = pt elif cmd in ['m', 'M']: params = [float(x) for x in tokens[pos:pos+2]] pos += 2 pt = params[0:2] if first_point is None: # First `m` in a path is treated as `M` first_point = pt else: if cmd == 'm': pt[0] += current_point[0] pt[1] += current_point[1] current_point = pt moved = True elif cmd in ['q', 'Q', 't', 'T']: if moved: path.moveTo(*current_point) moved = False if cmd in ['t', 'T']: n_params = 4 coords = [] else: n_params = 2 if second_quad_control is None: coords = list(current_point) else: coords = list(reflect_point(second_quad_control, current_point)) params = [float(x) for x in tokens[pos:pos+n_params]] pos += n_params for n in range(0, n_params, 2): pt = params[n:n+2] if cmd.islower(): pt[0] += current_point[0] pt[1] += current_point[1] if n == (n_params - 4): second_quad_control = pt coords.extend(pt) path.quadTo(*coords) current_point = pt elif cmd in ['z', 'Z']: if first_point is not None and current_point != first_point: pass #path.close() closed = True first_point = None else: log.warn('Unknown path command: {}'.format(cmd)) return path
def __draw_element(self, wrapped_element, parent_transform, parent_style): #========================================================================= drawing_objects = [] element = wrapped_element.etree_element element_style = self.__style_matcher.element_style(wrapped_element, parent_style) if element.tag == SVG_NS('g'): canvas_group = self.__draw_group(wrapped_element, parent_transform, parent_style) if canvas_group.is_valid: drawing_objects.append(canvas_group) elif element.tag in [SVG_NS('circle'), SVG_NS('ellipse'), SVG_NS('line'), SVG_NS('path'), SVG_NS('polyline'), SVG_NS('polygon'), SVG_NS('rect')]: path = SVGTiler.__get_graphics_path(element) if path is None: return [] fill = element_style.get('fill', '#FFF') if fill != 'none': path.setFillType(skia.PathFillType.kWinding) opacity = float(element_style.get('opacity', 1.0)) paint = skia.Paint(AntiAlias=True) if fill.startswith('url('): gradient = self.__definitions.get_by_url(fill) if gradient is None: fill = '#800' # Something's wrong so show show in image... opacity = 0.5 elif gradient.tag == SVG_NS('linearGradient'): gradient_stops = GradientStops(gradient) points = [(float(gradient.attrib.get('x1', 0.0)), float(gradient.attrib.get('y1', 0.0))), (float(gradient.attrib.get('x2', 1.0)), float(gradient.attrib.get('y2', 0.0)))] paint.setShader(skia.GradientShader.MakeLinear( points=points, positions=gradient_stops.offsets, colors=gradient_stops.colours, localMatrix=SVGTiler.__gradient_matrix(gradient, path) )) elif gradient.tag == SVG_NS('radialGradient'): gradient_stops = GradientStops(gradient) centre = (float(gradient.attrib.get('cx')), float(gradient.attrib.get('cy'))) radius = float(gradient.attrib.get('r')) # TODO: fx, fy # This will need a two point conical shader # -- see chromium/blink sources paint.setShader(skia.GradientShader.MakeRadial( center=centre, radius=radius, positions=gradient_stops.offsets, colors=gradient_stops.colours, localMatrix=SVGTiler.__gradient_matrix(gradient, path) )) else: fill = '#008' # Something's wrong so show show in image... opacity = 0.5 if fill.startswith('url('): if opacity < 1.0: paint.setAlphaf(opacity) else: paint.setColor(make_colour(fill, opacity)) drawing_objects.append(CanvasPath(path, paint, parent_transform, element.attrib.get('transform'), self.__clip_paths.get_by_url(element_style.get('clip-path')) )) stroke = element_style.get('stroke', 'none') stroked = (stroke != 'none') if stroked: markup = adobe_decode_markup(element) if markup.startswith('.'): properties = parse_markup(markup) if 'id' in properties or 'class' in properties: stroked = False if stroked: opacity = float(element_style.get('stroke-opacity', 1.0)) paint = skia.Paint(AntiAlias=True, Style=skia.Paint.kStroke_Style, Color=make_colour(stroke, opacity), StrokeWidth=float(element_style.get('stroke-width', 1.0)), ) stroke_linejoin = element_style.get('stroke-linejoin') if stroke_linejoin == 'bevel': paint.setStrokeJoin(skia.Paint.Join.kBevel_Join) elif stroke_linejoin == 'miter': paint.setStrokeJoin(skia.Paint.Join.kMiter_Join) elif stroke_linejoin == 'round': paint.setStrokeJoin(skia.Paint.Join.kRound_Join) stroke_linecap = element_style.get('stroke-linecap') if stroke_linecap == 'butt': paint.setStrokeCap(skia.Paint.Cap.kButt_Cap) elif stroke_linecap == 'round': paint.setStrokeCap(skia.Paint.Cap.kRound_Cap) elif stroke_linecap == 'square': paint.setStrokeCap(skia.Paint.Cap.kSquare_Cap) stroke_miterlimit = element_style.get('stroke-miterlimit') if stroke_miterlimit is not None: paint.setStrokeMiter(float(stroke_miterlimit)) drawing_objects.append(CanvasPath(path, paint, parent_transform, element.attrib.get('transform'), self.__clip_paths.get_by_url(element.attrib.get('clip-path')) )) elif element.tag == SVG_NS('image'): image_href = element.attrib.get(XLINK_HREF) pixel_bytes = None if image_href is not None: if image_href.startswith('data:'): parts = image_href[5:].split(',', 1) if parts[0].endswith(';base64'): media_type = parts[0].split(';', 1)[0] if media_type in IMAGE_MEDIA_TYPES: pixel_bytes = base64.b64decode(parts[1]) else: pixel_bytes = self.__source.join_path(image_href).get_data() if pixel_bytes is not None: pixel_array = np.frombuffer(pixel_bytes, dtype=np.uint8) pixels = cv2.imdecode(pixel_array, cv2.IMREAD_UNCHANGED) if pixels.shape[2] == 3: pixels = cv2.cvtColor(pixels, cv2.COLOR_RGB2RGBA) image = skia.Image.fromarray(pixels, colorType=skia.kBGRA_8888_ColorType) width = int(element.attrib.get('width', image.width())) height = int(element.attrib.get('height', image.height())) if width != image.width() or height != image.height(): image = image.resize(width, height, skia.FilterQuality.kHigh_FilterQuality) paint = skia.Paint() opacity = float(element_style.get('opacity', 1.0)) paint.setAlpha(round(opacity * 255)) drawing_objects.append(CanvasImage(image, paint, parent_transform, element.attrib.get('transform'), self.__clip_paths.get_by_url(element_style.get('clip-path')) )) elif element.tag not in [SVG_NS('style'), SVG_NS('text')]: log.warn("'{}' not supported...".format(element.tag)) return drawing_objects