def test_svg_figure_writes_width_height_and_view_box(): svg_fig = transform.SVGFigure(Unit("400mm"), Unit("300mm")) with NamedTemporaryFile() as f1: svg_fig.save(f1.name) with open(f1.name) as f2: written_content = f2.read() assert 'width="400.0mm"' in written_content assert 'height="300.0mm"' in written_content assert 'viewBox="0 0 400.0 300.0"' in written_content
def test_create_svg_figure(): svg_fig = transform.SVGFigure() assert "svg" in svg_fig.to_str().decode("ascii") svg_fig = transform.SVGFigure(Unit("2px"), Unit("2px")) assert "svg" in svg_fig.to_str().decode("ascii") svg_fig = transform.SVGFigure(2, 3) assert "svg" in svg_fig.to_str().decode("ascii") svg_fig = transform.SVGFigure("2", "3") assert "svg" in svg_fig.to_str().decode("ascii")
def plot_point_bar_figure(figure_one_path, figure_two_path): """ Combines the pointplot and bargraph together using svg magic Arguments: figure_one_path - The pointplot figure figure_two_path - The barplot figure """ fig1 = sg.fromfile(figure_one_path) fig2 = sg.fromfile(figure_two_path) fig1_width_size = np.round(float(fig1.root.attrib["width"][:-2]) * 1.33, 0) fig1_height_size = np.round( float(fig1.root.attrib["height"][:-2]) * 1.33, 0) fig2_width_size = np.round(float(fig2.root.attrib["width"][:-2]) * 1.33, 0) fig2_height_size = np.round( float(fig2.root.attrib["height"][:-2]) * 1.33, 0) fig = sg.SVGFigure( Unit((fig1_width_size + fig2_width_size) - 360), Unit(min(fig1_height_size, fig2_height_size) - 50), ) fig.append([ etree.Element("rect", { "width": "100%", "height": "100%", "fill": "white" }) ]) plot1 = fig1.getroot() plot1.moveto(10, 30) plot2 = fig2.getroot() plot2.moveto(fig1_width_size - 160, 12) text_A = sg.TextElement(10, 30, "A", size=18, weight="bold") text_B = sg.TextElement(fig1_width_size - 160, 30, "B", size=18, weight="bold") fig.append([plot1, plot2, text_A, text_B]) return fig
print(f"original: {panel_five_size}") print(f"scaled:{(panel_five_size[0]*scale_x, panel_five_size[1]*scale_y)}") panel_five = panel_five.getroot() panel_five.scale(x=scale_x, y=scale_y) panel_five.moveto(1070, 1670) # - panel_one_label = sg.TextElement(30, 30, "A", size=30, weight="bold") panel_two_label = sg.TextElement(10, 800, "B", size=30, weight="bold") panel_three_label = sg.TextElement(1100, 800, "C", size=30, weight="bold") panel_four_label = sg.TextElement(30, 1670, "D", size=30, weight="bold") panel_five_label = sg.TextElement(1100, 1670, "E", size=30, weight="bold") # + figure_one = sg.SVGFigure(Unit(2127), Unit(2433)) figure_one.append([ etree.Element("rect", { "width": "100%", "height": "100%", "fill": "white" }), panel_one, panel_two, panel_three, panel_four, panel_five, panel_one_label, panel_two_label, panel_three_label,
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 unit(val, to='px'): assert type(val) == str return Unit(val).to(to).value
def _compose_view(bg_svgs, fg_svgs, ref=0): from svgutils.compose import Unit from svgutils.transform import SVGFigure, GroupElement 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(Unit(f"{width}px"), Unit(f"{heights[:nsvgs].sum()}px")) yoffset = 0 for i, r in enumerate(roots): r.moveto(0, yoffset, scale_x=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", None) fig.root.attrib.pop("height", None) 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 plot_scatter_clouds( scatter_plot_path, word_cloud_x_path, word_cloud_y_path, final_figure_path="output/pca_plots/figures/final_figure.png", ): # Save the matplotlfigures scatter_plot = Path(scatter_plot_path) word_cloud_x = Path(word_cloud_x_path) word_cloud_y = Path(word_cloud_y_path) # create new SVG figure fig = sg.SVGFigure() fig.width = Unit("1280") fig.height = Unit("768") fig.append([ etree.Element("rect", { "width": "100%", "height": "100%", "fill": "white" }) ]) # load matpotlib-generated figures fig1 = load_clouds(str(word_cloud_y)) fig2 = sg.fromfile(scatter_plot) fig3 = load_clouds(str(word_cloud_x)) # get the plot objects plot1 = fig1.getroot() plot1.moveto(30, 30, scale_x=1, scale_y=1) plot2 = fig2.getroot() plot2.moveto(650, 0, scale_x=1) plot3 = fig3.getroot() plot3.moveto(650, 384, scale_x=1, scale_y=1) # append plots and labels to figure fig.append([plot2, plot3, plot1]) text_A = sg.TextElement(10, 30, "A", size=22, weight="bold") text_B = sg.TextElement(620, 30, "B", size=22, weight="bold") text_C = sg.TextElement(620, 390, "C", size=22, weight="bold") fig.append([text_A, text_B, text_C]) second_pc = int(re.search(r"pca_(\d+)", word_cloud_y.stem).group(1)) first_pc = int(re.search(r"pca_(\d+)", word_cloud_x.stem).group(1)) word_cloud_title_1 = sg.TextElement(225, 400, f"PC{second_pc}", size=22, weight="bold") word_cloud_title_2 = sg.TextElement(850, 760, f"PC{first_pc}", size=22, weight="bold") fig.append([word_cloud_title_1, word_cloud_title_2]) # save generated SVG files svg2png(bytestring=fig.to_str(), write_to=final_figure_path, dpi=250) return Image(final_figure_path)
# + fig1 = sg.fromfile( "output/figures/version_count_vs_publication_time_violin_filtered.svg" ) fig2 = sg.fromfile( "output/figures/article_distance_vs_publication_time_hex_filtered.svg" ) fig1_width_size = np.round(float(fig1.root.attrib["width"][:-2]) * 1.33, 0) fig1_height_size = np.round(float(fig1.root.attrib["height"][:-2]) * 1.33, 0) fig2_width_size = np.round(float(fig2.root.attrib["width"][:-2]) * 1.33, 0) fig2_height_size = np.round(float(fig2.root.attrib["height"][:-2]) * 1.33, 0) fig = sg.SVGFigure( Unit((fig1_width_size + fig2_width_size) - 360), Unit(min(fig1_height_size, fig2_height_size) - 50), ) fig.append( [etree.Element("rect", {"width": "100%", "height": "100%", "fill": "white"})] ) plot1 = fig1.getroot() plot1.moveto(10, 30) plot2 = fig2.getroot() plot2.moveto(fig1_width_size - 160, 12) text_A = sg.TextElement(10, 30, "A", size=22, weight="bold") text_B = sg.TextElement(fig1_width_size - 160, 30, "B", size=22, weight="bold")
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)