Ejemplo n.º 1
0
    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)
Ejemplo n.º 2
0
 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
Ejemplo n.º 3
0
    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...')
Ejemplo n.º 4
0
 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'
         )
Ejemplo n.º 5
0
 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])
Ejemplo n.º 6
0
 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
Ejemplo n.º 7
0
 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
Ejemplo n.º 8
0
 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)
Ejemplo n.º 9
0
    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
Ejemplo n.º 10
0
 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)
Ejemplo n.º 11
0
    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))
Ejemplo n.º 12
0
 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
Ejemplo n.º 13
0
    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
Ejemplo n.º 14
0
    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
Ejemplo n.º 15
0
    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
Ejemplo n.º 16
0
    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