forked from neodgm/neodgm
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmakesvg.exs
executable file
·123 lines (100 loc) · 3.34 KB
/
makesvg.exs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#!/usr/bin/elixir
defmodule SvgExporter do
@type glyph ::
{:unicode, integer, integer, [charlist]}
| {:unmapped, binary, integer, integer, [charlist]}
@spec svg_header(integer) :: binary
defp svg_header(width), do: """
<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg" width="#{width}" height="16">
<g style="fill:black;stroke:none">
"""
@spec svg_footer() :: binary
defp svg_footer, do: "</g></svg>"
@spec svg_rect(integer, integer) :: binary
defp svg_rect(x, y), do: ~s(<rect x="#{x}" y="#{y}" width="1" height="1" />)
@spec convert_dir(binary) :: :ok | {:error, File.posix}
def convert_dir(srcdir) do
srcdir = String.ends_with?(srcdir, "/") && srcdir || srcdir <> "/"
File.mkdir_p!("svg")
case File.ls(srcdir) do
{:ok, files} ->
files
|> Enum.filter(&String.ends_with?(&1, ".fnt"))
|> Enum.each(&convert_file(srcdir <> &1))
{:error, _} =
error -> error
end
end
@spec convert_file(binary) :: :ok
def convert_file(filename) do
IO.write("Converting #{filename}")
with {:ok, file} <- File.open(filename, [:read, :utf8]) do
file
|> load_glyphs()
|> Enum.each(&write_svg &1)
File.close(file)
IO.puts("done.")
else
{:error, reason} ->
IO.puts("\x1b[31mFailed: #{:file.format_error reason}\x1b[0m")
end
end
@spec load_glyphs(IO.device) :: Enumerable.t
defp load_glyphs(file) do
file
|> IO.read(:all)
|> String.split("\n")
|> Stream.chunk(17)
|> Stream.map(&create_glyph/1)
end
@spec create_glyph([binary]) :: glyph
defp create_glyph([header|data]) do
# Glyph Specification
# I. Normal Unicode Character:
# L1: <codepoint:int> <width:int>
# L2~L17: 1bpp bitmap data
# II. Unmapped Glyph (usually for ligatures)
# L1: <name:str> <data-width:int> <glyph-width:int> <xoffset:int>
# L2~L17: 1bpp bitmap data
i = & &1 |> Integer.parse |> elem(0)
lines = Enum.map(data, &String.to_charlist/1)
case String.split(header) do
[code, width] -> {:unicode, i.(code), i.(width), lines}
[name, width, xoff] -> {:unmapped, name, i.(width), i.(xoff), lines}
end
end
@spec write_svg(glyph) :: :ok
defp write_svg(glyph)
defp write_svg({:unicode, code, gwidth, lines}) do
hex = code |> Integer.to_string(16) |> String.pad_leading(4, "0")
{:ok, file} = File.open("svg/U#{hex}@#{gwidth}.svg", [:write, :utf8])
IO.write(file, svg_header(gwidth))
process_line(lines, 0, file, 0)
IO.write(file, svg_footer())
File.close(file)
IO.write(".")
end
defp write_svg({:unmapped, name, gwidth, xoff, lines}) do
{:ok, file} = File.open("svg/_#{name}@#{gwidth}.svg", [:write, :utf8])
IO.write(file, svg_header(gwidth))
process_line(lines, xoff, file, 0)
IO.write(file, svg_footer())
File.close(file)
IO.write(".")
end
@spec process_line([charlist], integer, IO.device, integer) :: :ok
defp process_line(line, xoffset, file, line_no)
defp process_line([], _, _, _), do: :ok
defp process_line([h|t], xoffset, file, line_no) do
h
|> Enum.with_index(xoffset)
|> Enum.each(fn
{?0, _} -> :nop
{?1, x} -> IO.write file, svg_rect(x, line_no)
end)
process_line t, xoffset, file, line_no + 1
end
end
[srcdir|_] = System.argv()
SvgExporter.convert_dir srcdir