Skip to content

Commit f5b1079

Browse files
authored
Merge pull request #6 from goboscript/v2
feat: std/font
2 parents 6bb3dc5 + bdde510 commit f5b1079

File tree

5 files changed

+2421
-1
lines changed

5 files changed

+2421
-1
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@
55
Add the following to your `goboscript.toml` file:
66

77
```toml
8-
std = "2.0.0"
8+
std = "2.1.0"
99
```

font.gs

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# Usage:
2+
# copy font.txt to your project directory
3+
# include the font data in the font_data list
4+
# list font_data = file ```font.txt```;
5+
# generate your own font using font.py
6+
# in inkscape, use these options:
7+
# Input/Output > SVG output > Path data > Path string format = Absolute
8+
# Input/Output > SVG output > Path data > Force repeat commands = Checked
9+
10+
var font_offset = 0;
11+
var font_x = 0;
12+
var font_y = 0;
13+
var font_scale = 1;
14+
var font_x1 = 0;
15+
var font_x2 = 240;
16+
var font_char_spacing = 0;
17+
var font_line_spacing = 0;
18+
var font_linelen = 0;
19+
20+
%define FONT_NAME font_data[font_offset+1]
21+
%define FONT_CREATOR font_data[font_offset+2]
22+
%define FONT_RIGHTS font_data[font_offset+3]
23+
%define FONT_WIDTH font_data[font_offset+4]
24+
%define FONT_HEIGHT font_data[font_offset+5]
25+
26+
%define FONT_CALCULATE_WIDTH_FROM_LENGTH(LENGTH) \
27+
(LENGTH*(FONT_WIDTH+2+font_char_spacing)-(2+font_char_spacing))*font_scale
28+
%define FONT_CALCULATE_WIDTH(TEXT) FONT_CALCULATE_WIDTH_FROM_LENGTH(length($TEXT))
29+
30+
proc font_render_char char {
31+
switch_costume $char;
32+
local x = "";
33+
local y = "";
34+
local i = font_offset+font_data[5+font_offset+costume_number()];
35+
forever {
36+
if font_data[i] == "M" {
37+
pen_up;
38+
goto font_x+font_scale*font_data[i+1], font_y-font_scale*font_data[i+2];
39+
i += 3;
40+
if x == "" {
41+
x = x_position();
42+
y = y_position();
43+
}
44+
} elif font_data[i] == "L" {
45+
pen_down;
46+
goto font_x+font_scale*font_data[i+1], font_y-font_scale*font_data[i+2];
47+
i += 3;
48+
} elif font_data[i] == "H" {
49+
pen_down;
50+
set_x font_x+font_scale*font_data[i+1];
51+
i += 2;
52+
} elif font_data[i] == "V" {
53+
pen_down;
54+
set_y font_y-font_scale*font_data[i+1];
55+
i += 2;
56+
} elif font_data[i] == "Z" {
57+
pen_down;
58+
goto x, y;
59+
i += 1;
60+
} else {
61+
pen_up;
62+
stop_this_script;
63+
}
64+
}
65+
}
66+
67+
proc font_render_begin {
68+
font_x = font_x1;
69+
font_linelen = 0;
70+
}
71+
72+
proc font_render_text text {
73+
local i = 1;
74+
repeat length($text) {
75+
font_render_char $text[i];
76+
font_x += (FONT_WIDTH+2+font_char_spacing)*font_scale;
77+
i++;
78+
}
79+
}
80+
81+
proc font_render_text_softwrap text {
82+
local maxlen = (font_x2 - font_x1) // ((FONT_WIDTH+2+font_char_spacing)*font_scale);
83+
local i = 1;
84+
local font_linelen = 0;
85+
local word = "";
86+
until i > length($text) {
87+
until $text[i] == " " or i > length($text) {
88+
word &= $text[i];
89+
i++;
90+
}
91+
if font_linelen + length(word) > maxlen {
92+
font_y -= (FONT_HEIGHT+4+font_line_spacing)*font_scale;
93+
font_x = font_x1;
94+
font_linelen = 0;
95+
}
96+
local j = 1;
97+
repeat length(word) {
98+
font_render_char word[j];
99+
font_x += (FONT_WIDTH+2+font_char_spacing)*font_scale;
100+
font_linelen += 1;
101+
if font_x > font_x2 {
102+
font_y -= (FONT_HEIGHT+4+font_line_spacing)*font_scale;
103+
font_x = font_x1;
104+
font_linelen = 0;
105+
}
106+
j++;
107+
}
108+
word = "";
109+
until $text[i] != " " or i > length($text) {
110+
word &= $text[i];
111+
i++;
112+
}
113+
local j = 1;
114+
repeat length(word) {
115+
font_render_char word[j];
116+
font_x += (FONT_WIDTH+2+font_char_spacing)*font_scale;
117+
font_linelen += 1;
118+
if font_x > font_x2 {
119+
font_y -= (FONT_HEIGHT+4+font_line_spacing)*font_scale;
120+
font_x = font_x1;
121+
font_linelen = 0;
122+
}
123+
j++;
124+
}
125+
word = "";
126+
}
127+
}

font.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# pyright: reportAny=false
2+
# pyright: reportUnusedCallResult=false
3+
4+
import argparse
5+
import re
6+
import xml.etree.ElementTree
7+
from dataclasses import dataclass
8+
from pathlib import Path
9+
from typing import cast
10+
11+
ns = {
12+
"svg": "http://www.w3.org/2000/svg",
13+
"inkscape": "http://www.inkscape.org/namespaces/inkscape",
14+
"dc": "http://purl.org/dc/elements/1.1/",
15+
"cc": "http://creativecommons.org/ns#",
16+
"rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
17+
}
18+
19+
CHARSET = "".join(map(chr, range(ord(" "), ord("~") + 1)))
20+
21+
argparser = argparse.ArgumentParser()
22+
argparser.add_argument("font", type=Path, help="Font .svg file to convert")
23+
argparser.add_argument("output", type=Path, help="Output .txt file")
24+
25+
26+
@dataclass
27+
class Args:
28+
font: Path
29+
output: Path
30+
31+
32+
args = Args(**argparser.parse_args().__dict__)
33+
name = args.font.stem
34+
root = xml.etree.ElementTree.parse(args.font).getroot()
35+
width = int(cast(str, root.get("width")))
36+
height = int(cast(str, root.get("height")))
37+
creator = root.find(".//dc:creator/dc:Agent/dc:title", ns) or ""
38+
rights = root.find(".//dc:rights/dc:Agent/dc:title", ns) or ""
39+
chars: dict[str, list[str]] = {}
40+
41+
42+
def modulate(d: list[str]) -> list[str]:
43+
cmd: str = ""
44+
i = 0
45+
while i < len(d):
46+
if d[i].isalpha():
47+
cmd = d[i]
48+
i += 1
49+
if cmd.upper() in "ML":
50+
if cmd.isupper():
51+
d[i] = str(int(float(d[i])) % (width * 2))
52+
i += 2
53+
elif cmd.upper() == "H":
54+
if cmd.isupper():
55+
d[i] = str(int(float(d[i])) % (width * 2))
56+
i += 1
57+
elif cmd.upper() == "V":
58+
i += 1
59+
return d
60+
61+
62+
for path in root.findall(".//svg:path", ns):
63+
sd = cast(str, path.get("d"))
64+
label = cast(str, path.get("{%s}label" % ns["inkscape"]))
65+
if len(label) == 1:
66+
chars[label] = modulate(re.split(r"[,\s]+", sd))
67+
68+
with args.output.open("w") as f:
69+
70+
def print(*objs: object, sep: str = " ", end: str = "\n") -> None:
71+
f.write(sep.join(map(str, objs)) + end)
72+
73+
print(name)
74+
print(creator)
75+
print(rights)
76+
print(width)
77+
print(height)
78+
79+
i = 0
80+
for ch in CHARSET:
81+
d = chars.get(ch, [])
82+
d.append("#")
83+
print(6 + len(CHARSET) + i)
84+
i += len(d)
85+
for ch in CHARSET:
86+
d: list[str] = chars.get(ch, ["#"])
87+
for x in d:
88+
if str(x).islower() and x not in "Zz":
89+
x = "d" + x
90+
print(x)

0 commit comments

Comments
 (0)