WIP: open SVG files saved with the app

Progress!

print
This commit is contained in:
Isaiah Odhner 2023-05-17 10:04:29 -04:00
parent 55419d671e
commit 3892dc503e
3 changed files with 645 additions and 1 deletions

View File

@ -57,6 +57,7 @@
"pypixelart",
"pyright",
"pyxdg",
"rects",
"renderable",
"rmam",
"setterm",
@ -69,6 +70,8 @@
"textconv",
"tlaplus",
"truecolor",
"tspan",
"tspans",
"ufeff",
"undoable",
"undos",

View File

@ -0,0 +1,524 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="210mm"
height="297mm"
viewBox="0 0 210 297"
version="1.1"
id="svg5"
inkscape:version="1.2.2 (732a01da63, 2022-12-09, custom)"
sodipodi:docname="combative_character_grid.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#505050"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="2.9647944"
inkscape:cx="200.18252"
inkscape:cy="285.17998"
inkscape:current-layer="layer1" />
<defs
id="defs2" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:#666666;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stop-color:#000000"
id="rect1071"
width="59.459286"
height="54.615498"
x="23.159849"
y="44.881004" />
<rect
style="fill:#00ff00;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stop-color:#000000"
id="rect234"
width="7.1056752"
height="7.3895073"
x="29.769499"
y="51.718258" />
<rect
style="fill:#00ff00;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stop-color:#000000"
id="rect236"
width="7.187295"
height="7.2463527"
x="36.875175"
y="59.107765" />
<rect
style="fill:#00ff00;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stop-color:#000000"
id="rect238"
width="7.2217975"
height="7.1936398"
x="44.716133"
y="51.737007" />
<rect
style="fill:#00ff00;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stop-color:#000000"
id="rect240"
width="7.6014237"
height="7.6129446"
x="51.937931"
y="58.930649" />
<rect
style="fill:#00ff00;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stop-color:#000000"
id="rect242"
width="7.1834297"
height="7.024179"
x="60.015602"
y="51.594803" />
<rect
style="fill:#00ff00;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stop-color:#000000"
id="rect244"
width="8.8927784"
height="8.2646675"
x="67.199036"
y="58.61898" />
<rect
style="fill:#00ffff;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stop-color:#000000"
id="rect300"
width="7.187294"
height="7.3895073"
x="36.875175"
y="51.718258" />
<rect
style="fill:#00ffff;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stop-color:#000000"
id="rect302"
width="7.6014252"
height="7.1936412"
x="51.937931"
y="51.737007" />
<rect
style="fill:#00ffff;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stop-color:#000000"
id="rect306"
width="8.8927803"
height="5.4191537"
x="67.199036"
y="53.199825" />
<rect
style="fill:#00ffff;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stop-color:#000000"
id="rect308"
width="7.6099586"
height="6.5727797"
x="59.956795"
y="59.928776" />
<rect
style="fill:#00ffff;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stop-color:#000000"
id="rect310"
width="5.4237928"
height="5.424016"
x="45.091705"
y="59.720421" />
<rect
style="fill:#00ffff;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stop-color:#000000"
id="rect312"
width="6.9445157"
height="6.687995"
x="28.791224"
y="60.221149" />
<rect
style="fill:#008080;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stop-color:#000000"
id="rect366"
width="6.9887633"
height="7.5608659"
x="37.215893"
y="67.071541" />
<rect
style="fill:#008080;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stop-color:#000000"
id="rect368"
width="6.2547202"
height="6.9023681"
x="52.295708"
y="67.335579" />
<rect
style="fill:#008080;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stop-color:#000000"
id="rect370"
width="8.3901863"
height="7.4900045"
x="67.705612"
y="67.190552" />
<rect
style="fill:#008080;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stop-color:#000000"
id="rect374"
width="8.0209265"
height="8.4574337"
x="29.288734"
y="75.805641" />
<rect
style="fill:#008080;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stop-color:#000000"
id="rect376"
width="6.3965511"
height="8.722991"
x="44.968143"
y="74.749695" />
<rect
style="fill:#008080;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stop-color:#000000"
id="rect378"
width="8.4150238"
height="7.7497745"
x="58.147022"
y="74.808571" />
<rect
style="fill:#ff00ff;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stop-color:#000000"
id="rect432"
width="6.865623"
height="7.784771"
x="29.335079"
y="67.875359" />
<rect
style="fill:#ff00ff;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stop-color:#000000"
id="rect434"
width="5.8369441"
height="6.5972581"
x="44.66724"
y="67.808578" />
<rect
style="fill:#ff00ff;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stop-color:#000000"
id="rect436"
width="7.024261"
height="6.9038763"
x="59.493877"
y="67.325905" />
<rect
style="fill:#ff00ff;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stop-color:#000000"
id="rect438"
width="7.8678956"
height="7.5887809"
x="67.85334"
y="75.658279" />
<rect
style="fill:#ff00ff;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stop-color:#000000"
id="rect440"
width="5.583735"
height="7.7397962"
x="52.176022"
y="75.317574" />
<rect
style="fill:#ff00ff;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stop-color:#000000"
id="rect442"
width="7.6048656"
height="8.1720171"
x="37.447536"
y="75.883972" />
<text
xml:space="preserve"
style="font-size:10.5833px;line-height:1.25;font-family:'Cloister Black';-inkscape-font-specification:'Cloister Black, ';letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
x="31.517002"
y="57.453945"
id="text498"><tspan
sodipodi:role="line"
id="tspan496"
style="stroke-width:0.264583"
x="31.517002"
y="57.453945">a</tspan></text>
<text
xml:space="preserve"
style="font-size:10.5833px;line-height:1.25;font-family:'Cloister Black';-inkscape-font-specification:'Cloister Black, ';letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
x="46.675823"
y="56.624329"
id="text502"><tspan
sodipodi:role="line"
id="tspan500"
style="stroke-width:0.264583"
x="46.675823"
y="56.624329">c</tspan></text>
<text
xml:space="preserve"
style="font-size:10.5833px;line-height:1.25;font-family:'Cloister Black';-inkscape-font-specification:'Cloister Black, ';letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
x="62.156319"
y="57.102715"
id="text506"><tspan
sodipodi:role="line"
id="tspan504"
style="stroke-width:0.264583"
x="62.156319"
y="57.102715">e</tspan></text>
<text
xml:space="preserve"
style="font-size:10.5833px;line-height:1.25;font-family:'Cloister Black';-inkscape-font-specification:'Cloister Black, ';letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
x="38.029388"
y="58.176018"
id="text510"><tspan
sodipodi:role="line"
id="tspan508"
style="stroke-width:0.264583"
x="38.029388"
y="58.176018">b</tspan></text>
<text
xml:space="preserve"
style="font-size:10.5833px;line-height:1.25;font-family:'Cloister Black';-inkscape-font-specification:'Cloister Black, ';letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
x="53.024918"
y="57.744404"
id="text514"><tspan
sodipodi:role="line"
id="tspan512"
style="stroke-width:0.264583"
x="53.024918"
y="57.744404">d</tspan></text>
<text
xml:space="preserve"
style="font-size:10.5833px;line-height:1.25;font-family:'Cloister Black';-inkscape-font-specification:'Cloister Black, ';letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
x="68.862602"
y="57.419685"
id="text568"><tspan
sodipodi:role="line"
id="tspan566"
style="stroke-width:0.264583"
x="68.862602"
y="57.419685">f</tspan></text>
<text
xml:space="preserve"
style="font-size:10.5833px;line-height:1.25;font-family:'Cloister Black';-inkscape-font-specification:'Cloister Black, ';letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
x="31.138842"
y="65.673546"
id="text572"><tspan
sodipodi:role="line"
id="tspan570"
style="stroke-width:0.264583"
x="31.138842"
y="65.673546">1</tspan></text>
<text
xml:space="preserve"
style="font-size:10.5833px;line-height:1.25;font-family:'Cloister Black';-inkscape-font-specification:'Cloister Black, ';letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
x="38.994606"
y="64.295349"
id="text576"><tspan
sodipodi:role="line"
id="tspan574"
style="stroke-width:0.264583"
x="38.994606"
y="64.295349">2</tspan></text>
<text
xml:space="preserve"
style="font-size:10.5833px;line-height:1.25;font-family:'Cloister Black';-inkscape-font-specification:'Cloister Black, ';letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
x="46.905895"
y="64.697449"
id="text580"><tspan
sodipodi:role="line"
id="tspan578"
style="stroke-width:0.264583"
x="46.905895"
y="64.697449">3</tspan></text>
<text
xml:space="preserve"
style="font-size:10.5833px;line-height:1.25;font-family:'Cloister Black';-inkscape-font-specification:'Cloister Black, ';letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
x="54.023323"
y="65.124672"
id="text584"><tspan
sodipodi:role="line"
id="tspan582"
style="stroke-width:0.264583"
x="54.023323"
y="65.124672">4</tspan></text>
<text
xml:space="preserve"
style="font-size:10.5833px;line-height:1.25;font-family:'Cloister Black';-inkscape-font-specification:'Cloister Black, ';letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
x="62.525013"
y="65.455658"
id="text588"><tspan
sodipodi:role="line"
id="tspan586"
style="stroke-width:0.264583"
x="62.525013"
y="65.455658">5</tspan></text>
<text
xml:space="preserve"
style="font-size:10.5833px;line-height:1.25;font-family:'Cloister Black';-inkscape-font-specification:'Cloister Black, ';letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
x="69.084427"
y="64.884781"
id="text592"><tspan
sodipodi:role="line"
id="tspan590"
style="stroke-width:0.264583"
x="69.084427"
y="64.884781">6</tspan></text>
<text
xml:space="preserve"
style="font-size:10.5833px;line-height:1.25;font-family:'Cloister Black';-inkscape-font-specification:'Cloister Black, ';letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
x="30.446806"
y="73.221672"
id="text596"><tspan
sodipodi:role="line"
id="tspan594"
style="stroke-width:0.264583"
x="30.446806"
y="73.221672">!</tspan></text>
<text
xml:space="preserve"
style="font-size:10.5833px;line-height:1.25;font-family:'Cloister Black';-inkscape-font-specification:'Cloister Black, ';letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
x="38.135998"
y="73.597939"
id="text600"><tspan
sodipodi:role="line"
id="tspan598"
style="stroke-width:0.264583"
x="38.135998"
y="73.597939">@</tspan></text>
<text
xml:space="preserve"
style="font-size:10.5833px;line-height:1.25;font-family:'Cloister Black';-inkscape-font-specification:'Cloister Black, ';letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
x="52.994148"
y="72.917671"
id="text604"><tspan
sodipodi:role="line"
id="tspan602"
style="stroke-width:0.264583"
x="52.994148"
y="72.917671">%</tspan></text>
<text
xml:space="preserve"
style="font-size:10.5833px;line-height:1.25;font-family:'Cloister Black';-inkscape-font-specification:'Cloister Black, ';letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
x="45.951057"
y="74.073425"
id="text608"><tspan
sodipodi:role="line"
id="tspan606"
style="stroke-width:0.264583"
x="45.951057"
y="74.073425">#</tspan></text>
<text
xml:space="preserve"
style="font-size:10.5833px;line-height:1.25;font-family:'Cloister Black';-inkscape-font-specification:'Cloister Black, ';letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
x="60.751328"
y="73.908005"
id="text612"><tspan
sodipodi:role="line"
id="tspan610"
style="stroke-width:0.264583"
x="60.751328"
y="73.908005">^</tspan></text>
<text
xml:space="preserve"
style="font-size:10.5833px;line-height:1.25;font-family:'Cloister Black';-inkscape-font-specification:'Cloister Black, ';letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
x="69.583992"
y="73.499489"
id="text616"><tspan
sodipodi:role="line"
id="tspan614"
style="stroke-width:0.264583"
x="69.583992"
y="73.499489">&amp;</tspan></text>
<text
xml:space="preserve"
style="font-size:10.5833px;line-height:1.25;font-family:'Cloister Black';-inkscape-font-specification:'Cloister Black, ';letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
x="29.849113"
y="83.530884"
id="text620"><tspan
sodipodi:role="line"
id="tspan618"
style="stroke-width:0.264583"
x="29.849113"
y="83.530884">*</tspan></text>
<text
xml:space="preserve"
style="font-size:10.5833px;line-height:1.25;font-family:'Cloister Black';-inkscape-font-specification:'Cloister Black, ';letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
x="38.157631"
y="82.527267"
id="text624"><tspan
sodipodi:role="line"
id="tspan622"
style="stroke-width:0.264583"
x="38.157631"
y="82.527267">(</tspan></text>
<text
xml:space="preserve"
style="font-size:10.5833px;line-height:1.25;font-family:'Cloister Black';-inkscape-font-specification:'Cloister Black, ';letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
x="45.558525"
y="82.517113"
id="text628"><tspan
sodipodi:role="line"
id="tspan626"
style="stroke-width:0.264583"
x="45.558525"
y="82.517113">)</tspan></text>
<text
xml:space="preserve"
style="font-size:10.5833px;line-height:1.25;font-family:'Cloister Black';-inkscape-font-specification:'Cloister Black, ';letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
x="53.204704"
y="82.395538"
id="text632"><tspan
sodipodi:role="line"
id="tspan630"
style="stroke-width:0.264583"
x="53.204704"
y="82.395538">-</tspan></text>
<text
xml:space="preserve"
style="font-size:10.5833px;line-height:1.25;font-family:'Cloister Black';-inkscape-font-specification:'Cloister Black, ';letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
x="59.230049"
y="81.894844"
id="text636"><tspan
sodipodi:role="line"
id="tspan634"
style="stroke-width:0.264583"
x="59.230049"
y="81.894844">=</tspan></text>
<text
xml:space="preserve"
style="font-size:10.5833px;line-height:1.25;font-family:'Cloister Black';-inkscape-font-specification:'Cloister Black, ';letter-spacing:0px;word-spacing:0px;stroke-width:0.264583;fill:#d40000"
x="68.591667"
y="82.460602"
id="text640"><tspan
sodipodi:role="line"
id="tspan638"
style="stroke-width:0.264583;fill:#d40000"
x="68.591667"
y="82.460602">+</tspan></text>
<rect
style="fill:#d40000;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stop-color:#000000"
id="rect746"
width="6.7182059"
height="6.9547753"
x="29.621984"
y="85.841278" />
<rect
style="fill:#ffff00;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stop-color:#000000"
id="rect748"
width="6.0744557"
height="7.4931664"
x="45.746864"
y="85.3237" />
<text
xml:space="preserve"
style="font-size:10.5833px;line-height:1.25;font-family:'Cloister Black';-inkscape-font-specification:'Cloister Black, ';letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
x="60.07822"
y="91.148125"
id="text752"><tspan
sodipodi:role="line"
id="tspan750"
style="stroke-width:0.264583"
x="60.07822"
y="91.148125">text</tspan></text>
<text
xml:space="preserve"
style="font-size:10.5833px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;stroke-width:0.264583;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
x="46.475227"
y="91.889862"
id="text1699"><tspan
sodipodi:role="line"
id="tspan1697"
style="stroke-width:0.264583;-inkscape-font-specification:sans-serif;font-family:sans-serif;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
x="46.475227"
y="91.889862" /></text>
<text
xml:space="preserve"
style="font-size:10.5833px;line-height:1.25;font-family:'Cloister Black';-inkscape-font-specification:'Cloister Black, ';letter-spacing:0px;word-spacing:0px;stroke-width:0.264583"
x="45.396358"
y="93.368546"
id="text1703"><tspan
sodipodi:role="line"
id="tspan1701"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:sans-serif;-inkscape-font-specification:sans-serif;stroke-width:0.264583"
x="45.396358"
y="93.368546">✅</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -1166,6 +1166,121 @@ class AnsiArtDocument:
document.bg[y][x] = "#" + hex(r)[2:].zfill(2) + hex(g)[2:].zfill(2) + hex(b)[2:].zfill(2)
return document
@staticmethod
def from_svg(svg: str, default_bg: str = "#ffffff", default_fg: str = "#000000") -> 'AnsiArtDocument':
"""Creates a document from an SVG containing a character grid with rects for cell backgrounds.
- rect elements can be in any order.
- rect elements can even be in different groups, however transforms are not considered.
- rect elements can vary in size slightly.
- rect elements can be missing, in which case the default background is used.
- rects that are outlying in size are ignored.
- rects that are outlying in position, however, increase the document size.
- fill or style attributes are used to determine the background/foreground colors.
- text elements are assumed to be belonging to the cell their x/y is within,
without regard to their size/alignment.
- parsing stylesheets is not supported.
- not all CSS/SVG color formats are supported.
To test the flexibility of this loader, I created combative_character_grid.svg
It contains out-of-order unevenly sized rects, missing rects, a background rect, and an emoji.
It doesn't currently contain varying color formats, font sizes, alignments, or transforms,
and it only uses style rather than fill, and text with tspan rather than text without.
"""
import xml.etree.ElementTree as ET
root = ET.fromstring(svg)
# Search for rect elements to define the background, and the cell locations.
rects = root.findall(".//{http://www.w3.org/2000/svg}rect")
if len(rects) == 0:
raise ValueError("No rect elements found in SVG.")
# Find the cell size, removing outliers until all cells are within
# a certain relative difference from the average size.
max_relative_difference = 0.1
for attribute in ["width", "height"]:
settled = False
while not settled:
settled = True
avg = sum(float(rect.attrib[attribute]) for rect in rects) / len(rects)
for rect in rects:
if abs(float(rect.attrib[attribute]) - avg) / avg > max_relative_difference:
rects.remove(rect)
settled = False
print("Ignoring outlier rect: " + ET.tostring(rect, encoding="unicode"))
break
else:
break
# Find the cell size.
# TODO: use spacing, not average size.
cell_width = sum(float(rect.attrib["width"]) for rect in rects) / len(rects)
cell_height = sum(float(rect.attrib["height"]) for rect in rects) / len(rects)
# Find the document bounds.
min_x = min(float(rect.attrib["x"]) for rect in rects)
min_y = min(float(rect.attrib["y"]) for rect in rects)
max_x = max(float(rect.attrib["x"]) + float(rect.attrib["width"]) for rect in rects)
max_y = max(float(rect.attrib["y"]) + float(rect.attrib["height"]) for rect in rects)
width = int((max_x - min_x) / cell_width)
height = int((max_y - min_y) / cell_height)
# Create the document.
document = AnsiArtDocument(width, height, default_bg, default_fg)
# Fill the document with the background colors.
def get_fill(el: ET.Element) -> Optional[str]:
fill = None
try:
fill = el.attrib["fill"]
except KeyError:
try:
style = el.attrib["style"]
except KeyError:
print("Warning: element has no fill or style attribute: " + ET.tostring(el, encoding="unicode"))
else:
for style_part in style.split(";"):
if style_part.startswith("fill:"):
fill = style_part[len("fill:"):]
break
if fill is None:
print("Warning: element has no fill defined: " + ET.tostring(el, encoding="unicode"))
return None
r, g, b = Color.parse(fill).rgb
return "#" + hex(r)[2:].zfill(2) + hex(g)[2:].zfill(2) + hex(b)[2:].zfill(2)
for rect in rects:
x = int((float(rect.attrib["x"]) - min_x) / cell_width)
y = int((float(rect.attrib["y"]) - min_y) / cell_height)
fill = get_fill(rect)
if fill is not None:
document.bg[y][x] = fill
# Find text elements to define the foreground.
texts = root.findall(".//{http://www.w3.org/2000/svg}text")
if len(texts) == 0:
raise ValueError("No text elements found in SVG.")
for text in texts:
x = int((float(text.attrib["x"]) - min_x) / cell_width)
y = int((float(text.attrib["y"]) - min_y) / cell_height)
ch = text.text
if ch is None:
# look for tspan element(s)
tspans = text.findall(".//{http://www.w3.org/2000/svg}tspan")
if len(tspans) == 0:
print("Warning: text element has no text or tspan: " + ET.tostring(text, encoding="unicode"))
continue
ch = ""
for tspan in tspans:
if tspan.text is not None:
ch += tspan.text
else:
print("Warning: tspan element has no text: " + ET.tostring(tspan, encoding="unicode"))
fill = get_fill(text)
try:
document.ch[y][x] = ch
if fill is not None:
document.fg[y][x] = fill
except IndexError:
print("Warning: text element is out of bounds: " + ET.tostring(text, encoding="unicode"))
continue
return document
@staticmethod
def decode_based_on_file_extension(content: bytes, file_path: str, default_bg: str = "#ffffff", default_fg: str = "#000000") -> 'AnsiArtDocument':
"""Creates a document from the given bytes, detecting the file format.
@ -1184,7 +1299,9 @@ class AnsiArtDocument:
return AnsiArtDocument.from_ansi(content.decode('utf-8'), default_bg, default_fg)
elif format_id == "PLAINTEXT":
return AnsiArtDocument.from_plain(content.decode('utf-8'), default_bg, default_fg)
elif format_id in Image.SAVE or format_id in ["HTML", "SVG", "RICH_CONSOLE_MARKUP"]:
elif format_id == "SVG":
return AnsiArtDocument.from_svg(content.decode('utf-8'), default_bg, default_fg)
elif format_id in Image.SAVE or format_id in ["HTML", "RICH_CONSOLE_MARKUP"]:
# This is a write-only format.
raise FormatReadNotSupported(localized_message=_("Cannot read files saved as %1 format.", format_id))
else: