def make_symbol(container: sg.SVGFigure, name, filepath): icon = sg.fromfile(filepath) size = get_view_box(icon) symbol = sg.fromstring( symbol_tpl.format(size['x'], size['y'], size['width'], size['height'], name)) symbol.append(icon.getroot()) container.append(symbol)
def _compose_view(bg_svgs, fg_svgs, ref=0): if fg_svgs is None: fg_svgs = [] # Merge SVGs and get roots svgs = bg_svgs + fg_svgs roots = [f.getroot() for f in svgs] # Query the size of each sizes = [] for f in svgs: viewbox = [float(v) for v in f.root.get("viewBox").split(" ")] width = int(viewbox[2]) height = int(viewbox[3]) sizes.append((width, height)) nsvgs = len(bg_svgs) sizes = np.array(sizes) # Calculate the scale to fit all widths width = sizes[ref, 0] scales = width / sizes[:, 0] heights = sizes[:, 1] * scales # Compose the views panel: total size is the width of # any element (used the first here) and the sum of heights fig = SVGFigure(width, heights[:nsvgs].sum()) yoffset = 0 for i, r in enumerate(roots): r.moveto(0, yoffset, scale=scales[i]) if i == (nsvgs - 1): yoffset = 0 else: yoffset += heights[i] # Group background and foreground panels in two groups if fg_svgs: newroots = [ GroupElement(roots[:nsvgs], {"class": "background-svg"}), GroupElement(roots[nsvgs:], {"class": "foreground-svg"}), ] else: newroots = roots fig.append(newroots) fig.root.attrib.pop("width") fig.root.attrib.pop("height") fig.root.set("preserveAspectRatio", "xMidYMid meet") with TemporaryDirectory() as tmpdirname: out_file = Path(tmpdirname) / "tmp.svg" fig.save(str(out_file)) # Post processing svg = out_file.read_text().splitlines() # Remove <?xml... line if svg[0].startswith("<?xml"): svg = svg[1:] # Add styles for the flicker animation if fg_svgs: svg.insert( 2, """\ <style type="text/css"> @keyframes flickerAnimation%s { 0%% {opacity: 1;} 100%% { opacity: 0; }} .foreground-svg { animation: 1s ease-in-out 0s alternate none infinite paused flickerAnimation%s;} .foreground-svg:hover { animation-play-state: running;} </style>""" % tuple([uuid4()] * 2), ) return svg
def compose_svg(atoms_to_put, options): board_svg_filename = os.path.join(options.board_theme_dir, _BOARD_SVG_BASENAME) board_ini_filename = os.path.join(options.board_theme_dir, _BOARD_INI_BASENAME) # Check for existance ourselves since configparser would throw NoSectionError # at us for a missing file. if not os.path.exists(board_ini_filename): raise OSError(errno.ENOENT, "No such file or directory: '%s'" % board_ini_filename) config = configparser.RawConfigParser(defaults={'river': 0.0}) config.read(board_ini_filename) output_board_offset_left_pixel = config.getfloat(_BOARD_CONFIG_SECTION, 'left') output_board_offset_top_pixel = config.getfloat(_BOARD_CONFIG_SECTION, 'top') output_board_width_pixel = config.getfloat(_BOARD_CONFIG_SECTION, 'width') output_board_height_pixel = config.getfloat(_BOARD_CONFIG_SECTION, 'height') output_board_river_height_pixel = config.getfloat(_BOARD_CONFIG_SECTION, 'river') # Regular pieces are at level 0; level 1 and above is drawn on top of (i.e. after) # the pieces while -1 and below is drawn below (i.e. before) the pieces. jobs_at_z_index = defaultdict(list) annotation_theme_config_filename = os.path.join( options.annotation_theme_dir, 'config.yml') with open(annotation_theme_config_filename) as f: annotation_theme_config = yaml.safe_load(f) for put_atom in atoms_to_put: if isinstance(put_atom, PutAnnotation): put_annotation: PutAnnotation = put_atom x_rel = float(put_annotation.x) / _MAX_X y_rel = float(_MAX_Y - put_annotation.y) / _MAX_Y filename = os.path.join(options.annotation_theme_dir, f'{put_annotation.annotation_name}.svg') annotation_scale = options.annotation_scale if annotation_theme_config[ 'allow_scaling'][put_annotation.annotation_name] else 1.0 atom_z_index = int(annotation_theme_config['z_index'][ put_annotation.annotation_name]) jobs_at_z_index[atom_z_index].append( (x_rel, y_rel, filename, annotation_scale)) else: assert isinstance(put_atom, PutPiece) put_piece: PutPiece = put_atom x_rel = float(put_piece.x) / _MAX_X y_rel = float(_MAX_Y - put_piece.y) / _MAX_Y basename = _FILENAME_OF_PARTY_PIECE[put_piece.party][ put_piece.piece] filename = os.path.join(options.piece_theme_dir, basename) jobs_at_z_index[_Z_INDEX_PIECE_LEVEL].append( (x_rel, y_rel, filename, options.piece_scale)) if options.debug: for x_rel in (0.0, 1.0): for y_rel in (0.0, 1.0): jobs_at_z_index[_Z_INDEX_DEBUG_DIAMOND].append( (x_rel, y_rel, os.path.join(options.piece_theme_dir, _DIAMOND_FILE_NAME), options.piece_scale)) # Read board board_fig = fromfile(board_svg_filename) board_root = board_fig.getroot() # Scale board to output board_width_pixel, board_height_pixel = _pixel_viewbox_of_figure( board_fig, options.resolution_dpi)[2:] height_factor = board_height_pixel / float(board_width_pixel) board_scale = options.width_pixel / board_width_pixel board_root.moveto(0, 0, scale_x=board_scale, scale_y=board_scale) output_board_offset_left_pixel *= board_scale output_board_offset_top_pixel *= board_scale output_board_width_pixel *= board_scale output_board_height_pixel *= board_scale output_board_river_height_pixel *= board_scale # Initialize output figure output_fig = SVGFigure(Unit(f'{options.width_pixel}px'), Unit(f'{options.width_pixel * height_factor}px')) output_fig.append([ board_root, ]) for _z_index, jobs in sorted(jobs_at_z_index.items()): for (x_rel, y_rel, filename, element_scale) in jobs: piece_fig = fromfile(filename) piece_root = piece_fig.getroot() original_piece_width_pixel, original_piece_height_pixel = \ _pixel_viewbox_of_figure(piece_fig, options.resolution_dpi)[2:] # Scale and put piece onto board center_x_pixel = output_board_offset_left_pixel + output_board_width_pixel * x_rel center_y_pixel = output_board_offset_top_pixel + (output_board_height_pixel - output_board_river_height_pixel) * y_rel \ + (output_board_river_height_pixel if (y_rel >= 0.5) else 0.0) maximum_future_piece_width_pixel = output_board_width_pixel / _MAX_X * element_scale maximum_future_piece_height_pixel = output_board_height_pixel / _MAX_Y * element_scale scale = min( maximum_future_piece_width_pixel / original_piece_width_pixel, maximum_future_piece_height_pixel / original_piece_height_pixel) future_piece_width_pixel = original_piece_width_pixel * scale future_piece_height_pixel = original_piece_height_pixel * scale x_pixel = center_x_pixel - future_piece_width_pixel / 2.0 y_pixel = center_y_pixel - future_piece_height_pixel / 2.0 piece_root.moveto(x_pixel, y_pixel, scale_x=scale, scale_y=scale) output_fig.append([ piece_root, ]) output_fig.save(options.output_file)
def write_svg(self, filename): WIDTH = self._outer_board_width_pixel() HEIGHT = self._outer_board_height_pixel() output_fig = SVGFigure(Unit(f'{WIDTH}px'), Unit(f'{HEIGHT}px')) output_fig.append(self._lines) output_fig.save(filename)