From a0d3ac2cd399ebfdba2ce42b3a64b100f61fea83 Mon Sep 17 00:00:00 2001 From: RayBipse Date: Mon, 18 Dec 2023 12:02:12 -0800 Subject: [PATCH] Added separate text support --- lib/src/middleware/export_svg.dart | 64 +++++++++++++++++++--- lib/src/view/design_main_dna_sequence.dart | 2 +- 2 files changed, 56 insertions(+), 10 deletions(-) diff --git a/lib/src/middleware/export_svg.dart b/lib/src/middleware/export_svg.dart index 83d3f863..1d9a6142 100644 --- a/lib/src/middleware/export_svg.dart +++ b/lib/src/middleware/export_svg.dart @@ -2,8 +2,10 @@ import 'dart:html'; import 'dart:svg' as svg; import 'dart:svg'; +import 'package:over_react/over_react.dart'; import 'package:redux/redux.dart'; import 'package:scadnano/src/middleware/system_clipboard.dart'; +import 'package:scadnano/src/view/design_main_dna_sequence.dart'; import '../app.dart'; import '../state/app_state.dart'; @@ -45,11 +47,16 @@ export_svg_middleware(Store store, dynamic action, NextDispatcher next window.alert("No strands are selected, so there is nothing to export.\n" "Please select some strands before choosing this option."); } else { - var cloned_svg_element_with_style = get_cloned_svg_element_with_style(selected_elts); + var cloned_svg_element_with_style = get_cloned_svg_element_with_style( + selected_elts, store.state.ui_state.export_svg_text_separately); _export_from_element(cloned_svg_element_with_style, 'selected'); } - } else + } else { + if (store.state.ui_state.export_svg_text_separately) { + elt = separate_if_svg_text(clone_and_apply_style(elt)); + } _export_from_element(elt, 'main'); + } } if (action.type == actions.ExportSvgType.side || action.type == actions.ExportSvgType.both) { var elt = document.getElementById("side-view-svg"); @@ -67,6 +74,37 @@ export_svg_middleware(Store store, dynamic action, NextDispatcher next } } +// this directly modifies ele +Node separate_if_svg_text(Node ele) { + if (ele is TextElement && ele is SvgElement) { + double letterSpacing = double.tryParse(ele.getAttribute('letter-spacing') ?? "null"); + if (letterSpacing != null) { + List children = []; + List dna_seq = ele.text.split(""); + double x = double.parse(ele.getAttribute('x')); + for (var i = 0; i < dna_seq.length; ++i) { + var child = clone_and_apply_style(ele) + ..id = ele.id + '-n-${i}' + ..text = dna_seq[i]; + child.setAttribute('x', x.toString()); + child.setAttribute('y', ele.getAttribute('y')); + child.setAttribute('dominant-baseline', 'text-top'); + child.classes.add(DesignMainDNASequenceComponent.classname_dna_sequence); + children.add(child); + x += letterSpacing + DesignMainDNASequenceComponent.charWidth; + } + return SvgElement.tag("g")..children = children; + } + } + if (ele is Element) { + if (ele.hasChildNodes()) { + List nodes = ele.nodes.map(separate_if_svg_text).toList(); + ele.nodes = nodes; + } + } + return ele; +} + List get_selected_strands(Store store) { var selected_strands = store.state.ui_state.selectables_store.selected_strands; List selected_elts = []; @@ -81,9 +119,12 @@ List get_selected_strands(Store store) { return selected_elts; } -SvgSvgElement get_cloned_svg_element_with_style(List selected_elts) { +SvgSvgElement get_cloned_svg_element_with_style(List selected_elts, bool separate_text) { var cloned_svg_element_with_style = SvgSvgElement() ..children = selected_elts.map(clone_and_apply_style).toList(); + if (separate_text) { + selected_elts = selected_elts.map(separate_if_svg_text).map((x) => x as Element).toList(); + } // we can't get bbox without it being added to the DOM first document.body.append(cloned_svg_element_with_style); @@ -125,7 +166,7 @@ _export_svg(svg.SvgSvgElement svg_element, String filename_append) { } _copy_from_elements(List svg_elements) { - var cloned_svg_element_with_style = get_cloned_svg_element_with_style(svg_elements); + var cloned_svg_element_with_style = get_cloned_svg_element_with_style(svg_elements, false); util.copy_svg_as_png(cloned_svg_element_with_style); } @@ -196,6 +237,7 @@ Element clone_and_apply_style(Element elt_orig) { } clone_and_apply_style_rec(Element elt_styled, Element elt_orig, {int depth = 0}) { + // print('elt_styled ${elt_styled.id} and elt_orig ${elt_orig.id}'); // Set children_styled_to_remove = {}; var tag_name = elt_styled.tagName; @@ -208,7 +250,8 @@ clone_and_apply_style_rec(Element elt_styled, Element elt_orig, {int depth = 0}) if (relevant_styles.keys.contains(tag_name)) { var style_def = elt_orig.getComputedStyle(); - + // print( + // 'id ${elt_styled.id} fill ${style_def.getPropertyValue("fill")} relevant_styles ${relevant_styles[tag_name]}'); //TODO: figure out how to remove nodes that aren't visible; // getting error "Unsupported operation: Cannot setRange on filtered list" when removing children // if (style_def.visibility == 'hidden') { @@ -222,14 +265,17 @@ clone_and_apply_style_rec(Element elt_styled, Element elt_orig, {int depth = 0}) // correcting for this bug in InkScape that causes it to render hidden SVG objects: // https://bugs.launchpad.net/inkscape/+bug/1577763 if (style_name == 'visibility' && style_value == 'hidden') { - style_strings.add('display: none'); + // style_strings.add('display: none'); + elt_styled.style.setProperty('display', 'none'); } - style_strings.add('${style_name}: ${style_value}'); + elt_styled.style.setProperty(style_name, style_value); + // style_strings.add('${style_name}: ${style_value};'); } } - var style_string = style_strings.join('; ') + ';'; + // var style_string = style_strings.join(' '); - elt_styled.setAttribute("style", style_string); + // elt_styled.setAttribute("style", style_string); + // print(elt_styled.styleMap); // print('${' ' * depth * 2} ${tag_name} ${elt_orig.classes.toList()} style: $style_string'); } diff --git a/lib/src/view/design_main_dna_sequence.dart b/lib/src/view/design_main_dna_sequence.dart index a77d0a2b..792e4e20 100644 --- a/lib/src/view/design_main_dna_sequence.dart +++ b/lib/src/view/design_main_dna_sequence.dart @@ -92,6 +92,7 @@ class DesignMainDNASequenceComponent extends UiComponent2