├── .gitignore
├── BUILDING.md
├── LICENSE
├── Makefile
├── README.md
├── images
├── brutalist.jpg
└── brutalist_vs_dejavu.gif
├── scripts
├── colorize.pl
├── generate.pe
├── generate.py
├── italicize.pe
├── langcover.pl
├── lgc.pe
├── merge.pl
├── mescover.pl
├── narrow.pe
├── narrowmerge.pe
├── narrowmerge.sh
├── problems.pl
├── samples.sh
├── sfdnormalize.pl
├── stats.sh
├── status.pl
├── strip_glyphs.pe
├── ttpostproc.pl
├── unhinted.pl
├── unicover.pl
└── wikidownloadtemplate.sh
└── src
├── BrutalistMono-Bold.sfd
├── BrutalistMono-BoldOblique.sfd
├── BrutalistMono-Oblique.sfd
└── BrutalistMono.sfd
/.gitignore:
--------------------------------------------------------------------------------
1 | build/
2 | dist/
3 | tmp/
4 | /src/*.sfd~
5 | *.DS_Store
--------------------------------------------------------------------------------
/BUILDING.md:
--------------------------------------------------------------------------------
1 | Building
2 | --------
3 |
4 | ### Prerequisites
5 |
6 | To build these fonts, you will need:
7 |
8 | * [FontForge][1], installable on Debian through the `fontforge` package:
9 |
10 | ~~~shell
11 | sudo apt-get install software-properties-common
12 | sudo add-apt-repository ppa:fontforge/fontforge
13 | sudo apt-get update
14 | sudo apt-get install fontforge
15 | ~~~
16 |
17 | macOS users can install using [Homebrew][2]:
18 |
19 | ~~~shell
20 | brew install fontforge
21 | ~~~
22 |
23 | Fedora users should run the following command as root:
24 |
25 | ~~~shell
26 | yum install fontforge
27 | ~~~
28 |
29 | See FontForge's [installation docs][3] for more info.
30 |
31 |
32 | * [Perl][4], with the [`Font::TTF`][5] and [`IO::String`][6] modules
33 | installed:
34 |
35 | ~~~shell
36 | cpan Font::TTF IO::String
37 | ~~~
38 |
39 | Debian users can install this with the `libfont-ttf-perl` package.
40 |
41 | macOS users may need to install as root if they encounter
42 | permission problems.
43 |
44 |
45 | * GNU-compatible [Make][7], installable through Debian package `make`.
46 |
47 | macOS users who have installed XCode's CLI utils should already
48 | have `make` available.
49 |
50 |
51 | ### Generating TrueType files
52 |
53 | To generate all fonts:
54 |
55 | ~~~console
56 | $ make
57 | ~~~
58 |
59 | [1]: https://fontforge.github.io/en-US/
60 | [2]: https://brew.sh/
61 | [3]: http://designwithfontforge.com/en-US/Installing_Fontforge.html
62 | [4]: https://www.perl.org/
63 | [5]: https://metacpan.org/release/Font-TTF/
64 | [6]: https://metacpan.org/pod/IO::String
65 | [7]: http://www.gnu.org/software/make/manual/make.html
66 | [8]: https://wiki.freedesktop.org/www/Software/fontconfig/
67 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Fonts are (c) Bitstream (see below). Brutalist changes are in public domain.
2 | Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below)
3 |
4 |
5 | Bitstream Vera Fonts Copyright
6 | ------------------------------
7 |
8 | Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is
9 | a trademark of Bitstream, Inc.
10 |
11 | Permission is hereby granted, free of charge, to any person obtaining a copy
12 | of the fonts accompanying this license ("Fonts") and associated
13 | documentation files (the "Font Software"), to reproduce and distribute the
14 | Font Software, including without limitation the rights to use, copy, merge,
15 | publish, distribute, and/or sell copies of the Font Software, and to permit
16 | persons to whom the Font Software is furnished to do so, subject to the
17 | following conditions:
18 |
19 | The above copyright and trademark notices and this permission notice shall
20 | be included in all copies of one or more of the Font Software typefaces.
21 |
22 | The Font Software may be modified, altered, or added to, and in particular
23 | the designs of glyphs or characters in the Fonts may be modified and
24 | additional glyphs or characters may be added to the Fonts, only if the fonts
25 | are renamed to names not containing either the words "Bitstream" or the word
26 | "Vera".
27 |
28 | This License becomes null and void to the extent applicable to Fonts or Font
29 | Software that has been modified and is distributed under the "Bitstream
30 | Vera" names.
31 |
32 | The Font Software may be sold as part of a larger software package but no
33 | copy of one or more of the Font Software typefaces may be sold by itself.
34 |
35 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
36 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY,
37 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT,
38 | TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME
39 | FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING
40 | ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
41 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
42 | THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE
43 | FONT SOFTWARE.
44 |
45 | Except as contained in this notice, the names of Gnome, the Gnome
46 | Foundation, and Bitstream Inc., shall not be used in advertising or
47 | otherwise to promote the sale, use or other dealings in this Font Software
48 | without prior written authorization from the Gnome Foundation or Bitstream
49 | Inc., respectively. For further information, contact: fonts at gnome dot
50 | org.
51 |
52 | Arev Fonts Copyright
53 | ------------------------------
54 |
55 | Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved.
56 |
57 | Permission is hereby granted, free of charge, to any person obtaining
58 | a copy of the fonts accompanying this license ("Fonts") and
59 | associated documentation files (the "Font Software"), to reproduce
60 | and distribute the modifications to the Bitstream Vera Font Software,
61 | including without limitation the rights to use, copy, merge, publish,
62 | distribute, and/or sell copies of the Font Software, and to permit
63 | persons to whom the Font Software is furnished to do so, subject to
64 | the following conditions:
65 |
66 | The above copyright and trademark notices and this permission notice
67 | shall be included in all copies of one or more of the Font Software
68 | typefaces.
69 |
70 | The Font Software may be modified, altered, or added to, and in
71 | particular the designs of glyphs or characters in the Fonts may be
72 | modified and additional glyphs or characters may be added to the
73 | Fonts, only if the fonts are renamed to names not containing either
74 | the words "Tavmjong Bah" or the word "Arev".
75 |
76 | This License becomes null and void to the extent applicable to Fonts
77 | or Font Software that has been modified and is distributed under the
78 | "Tavmjong Bah Arev" names.
79 |
80 | The Font Software may be sold as part of a larger software package but
81 | no copy of one or more of the Font Software typefaces may be sold by
82 | itself.
83 |
84 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
85 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
86 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
87 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL
88 | TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
89 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
90 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
91 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
92 | OTHER DEALINGS IN THE FONT SOFTWARE.
93 |
94 | Except as contained in this notice, the name of Tavmjong Bah shall not
95 | be used in advertising or otherwise to promote the sale, use or other
96 | dealings in this Font Software without prior written authorization
97 | from Tavmjong Bah. For further information, contact: tavmjong @ free
98 | . fr.
99 |
100 | TeX Gyre DJV Math
101 | -----------------
102 | Fonts are (c) Bitstream (see below). Brutalist changes are in public domain.
103 |
104 | Math extensions done by B. Jackowski, P. Strzelczyk and P. Pianowski
105 | (on behalf of TeX users groups) are in public domain.
106 |
107 | Letters imported from Euler Fraktur from AMSfonts are (c) American
108 | Mathematical Society (see below).
109 | Bitstream Vera Fonts Copyright
110 | Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera
111 | is a trademark of Bitstream, Inc.
112 |
113 | Permission is hereby granted, free of charge, to any person obtaining a copy
114 | of the fonts accompanying this license (“Fonts”) and associated
115 | documentation
116 | files (the “Font Software”), to reproduce and distribute the Font Software,
117 | including without limitation the rights to use, copy, merge, publish,
118 | distribute,
119 | and/or sell copies of the Font Software, and to permit persons to whom
120 | the Font Software is furnished to do so, subject to the following
121 | conditions:
122 |
123 | The above copyright and trademark notices and this permission notice
124 | shall be
125 | included in all copies of one or more of the Font Software typefaces.
126 |
127 | The Font Software may be modified, altered, or added to, and in particular
128 | the designs of glyphs or characters in the Fonts may be modified and
129 | additional
130 | glyphs or characters may be added to the Fonts, only if the fonts are
131 | renamed
132 | to names not containing either the words “Bitstream” or the word “Vera”.
133 |
134 | This License becomes null and void to the extent applicable to Fonts or
135 | Font Software
136 | that has been modified and is distributed under the “Bitstream Vera”
137 | names.
138 |
139 | The Font Software may be sold as part of a larger software package but
140 | no copy
141 | of one or more of the Font Software typefaces may be sold by itself.
142 |
143 | THE FONT SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS
144 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY,
145 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT,
146 | TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME
147 | FOUNDATION
148 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL,
149 | SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN
150 | ACTION
151 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR
152 | INABILITY TO USE
153 | THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
154 | Except as contained in this notice, the names of GNOME, the GNOME
155 | Foundation,
156 | and Bitstream Inc., shall not be used in advertising or otherwise to promote
157 | the sale, use or other dealings in this Font Software without prior written
158 | authorization from the GNOME Foundation or Bitstream Inc., respectively.
159 | For further information, contact: fonts at gnome dot org.
160 |
161 | AMSFonts (v. 2.2) copyright
162 |
163 | The PostScript Type 1 implementation of the AMSFonts produced by and
164 | previously distributed by Blue Sky Research and Y&Y, Inc. are now freely
165 | available for general use. This has been accomplished through the
166 | cooperation
167 | of a consortium of scientific publishers with Blue Sky Research and Y&Y.
168 | Members of this consortium include:
169 |
170 | Elsevier Science IBM Corporation Society for Industrial and Applied
171 | Mathematics (SIAM) Springer-Verlag American Mathematical Society (AMS)
172 |
173 | In order to assure the authenticity of these fonts, copyright will be
174 | held by
175 | the American Mathematical Society. This is not meant to restrict in any way
176 | the legitimate use of the fonts, such as (but not limited to) electronic
177 | distribution of documents containing these fonts, inclusion of these fonts
178 | into other public domain or commercial font collections or computer
179 | applications, use of the outline data to create derivative fonts and/or
180 | faces, etc. However, the AMS does require that the AMS copyright notice be
181 | removed from any derivative versions of the fonts which have been altered in
182 | any way. In addition, to ensure the fidelity of TeX documents using Computer
183 | Modern fonts, Professor Donald Knuth, creator of the Computer Modern faces,
184 | has requested that any alterations which yield different font metrics be
185 | given a different name.
186 |
187 | $Id$
188 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: all check munge full sans lgc ttf full-ttf sans-ttf lgc-ttf status dist src-dist full-dist sans-dist lgc-dist norm check-harder pre-patch clean
2 |
3 | # Release version
4 | VERSION = 1.0
5 | # Snapshot version
6 | SNAPSHOT =
7 | # Initial source directory, assumed read-only
8 | SRCDIR = src
9 | # Directory where temporary files live
10 | TMPDIR = tmp
11 | # Directory where final files are created
12 | BUILDDIR = build
13 | # Directory where final archives are created
14 | DISTDIR = dist
15 |
16 | # Release layout
17 | FONTCONFDIR = fontconfig
18 | DOCDIR = .
19 | SCRIPTSDIR = scripts
20 | TTFDIR = ttf
21 | RESOURCEDIR = resources
22 |
23 | ifeq "$(SNAPSHOT)" ""
24 | ARCHIVEVER = $(VERSION)
25 | else
26 | ARCHIVEVER = $(VERSION)-$(SNAPSHOT)
27 | endif
28 |
29 | SRCARCHIVE = brutalist-fonts-$(ARCHIVEVER)
30 | FULLARCHIVE = brutalist-fonts-ttf-$(ARCHIVEVER)
31 | SANSARCHIVE = brutalist-ttf-$(ARCHIVEVER)
32 | LGCARCHIVE = brutalist-lgc-fonts-ttf-$(ARCHIVEVER)
33 |
34 | ARCHIVEEXT = .zip .tar.bz2
35 | SUMEXT = .zip.md5 .tar.bz2.md5 .tar.bz2.sha512
36 |
37 | OLDSTATUS = $(DOCDIR)/status.txt
38 | BLOCKS = $(RESOURCEDIR)/Blocks.txt
39 | UNICODEDATA = $(RESOURCEDIR)/UnicodeData.txt
40 | FC-LANG = $(RESOURCEDIR)/fc-lang
41 |
42 | GENERATE = $(SCRIPTSDIR)/generate.pe
43 | TTPOSTPROC = $(SCRIPTSDIR)/ttpostproc.pl
44 | LGC = $(SCRIPTSDIR)/lgc.pe
45 | UNICOVER = $(SCRIPTSDIR)/unicover.pl
46 | LANGCOVER = $(SCRIPTSDIR)/langcover.pl
47 | STATUS = $(SCRIPTSDIR)/status.pl
48 | PROBLEMS = $(SCRIPTSDIR)/problems.pl
49 | NORMALIZE = $(SCRIPTSDIR)/sfdnormalize.pl
50 | NARROW = $(SCRIPTSDIR)/narrow.pe
51 |
52 | SRC := $(wildcard $(SRCDIR)/*.sfd)
53 | SFDFILES := $(patsubst $(SRCDIR)/%, %, $(SRC))
54 | FULLSFD := $(patsubst $(SRCDIR)/%.sfd, $(TMPDIR)/%.sfd, $(SRC))
55 | NORMSFD := $(patsubst %, %.norm, $(FULLSFD))
56 | MATSHSFD := $(wildcard $(SRCDIR)/*Math*.sfd)
57 | LGCSRC := $(filter-out $(MATSHSFD),$(SRC))
58 | LGCSFD := $(patsubst $(SRCDIR)/Brutalist%.sfd, $(TMPDIR)/BrutalistLGC%.sfd, $(LGCSRC))
59 | FULLTTF := $(patsubst $(TMPDIR)/%.sfd, $(BUILDDIR)/%.ttf, $(FULLSFD))
60 | LGCTTF := $(patsubst $(TMPDIR)/%.sfd, $(BUILDDIR)/%.ttf, $(LGCSFD))
61 |
62 | FONTCONF := $(wildcard $(FONTCONFDIR)/*.conf)
63 | FONTCONFLGC := $(wildcard $(FONTCONFDIR)/*lgc*.conf)
64 | FONTCONFFULL := $(filter-out $(FONTCONFLGC), $(FONTCONF))
65 |
66 | STATICDOC := $(addprefix $(DOCDIR)/, AUTHORS LICENSE NEWS README.md)
67 | STATICSRCDOC := $(addprefix $(DOCDIR)/, BUILDING.md)
68 | GENDOCFULL = unicover.txt langcover.txt status.txt
69 | GENDOCSANS = unicover-sans.txt langcover-sans.txt
70 | GENDOCLGC = unicover-lgc.txt langcover-lgc.txt
71 |
72 | all : full sans lgc
73 |
74 | $(TMPDIR)/%.sfd: $(SRCDIR)/%.sfd
75 | @echo "[1] $< => $@"
76 | install -d $(dir $@)
77 | sed "s@\(Version:\? \)\(0\.[0-9]\+\.[0-9]\+\|[1-9][0-9]*\.[0-9]\+\)@\1$(VERSION)@" $< > $@
78 | touch -r $< $@
79 |
80 | $(TMPDIR)/BrutalistLGCMathTeXGyre.sfd: $(TMPDIR)/BrutalistMathTeXGyre.sfd
81 | @echo "[2] skipping $<"
82 |
83 | $(TMPDIR)/BrutalistLGC%.sfd: $(TMPDIR)/Brutalist%.sfd
84 | @echo "[2] $< => $@"
85 | sed -e 's,FontName: Brutalist,FontName: BrutalistLGC,'\
86 | -e 's,FullName: Brutalist,FullName: Brutalist LGC,'\
87 | -e 's,FamilyName: Brutalist,FamilyName: Brutalist LGC,'\
88 | -e 's,"Brutalist \(\(Sans\|Serif\)*\( Condensed\| Mono\)*\( Bold\)*\( Oblique\|Italic\)*\)","Brutalist LGC \1",g' < $< > $@
89 | @echo "Stripping unwanted glyphs from $@"
90 | $(LGC) $@
91 | touch -r $< $@
92 |
93 | $(BUILDDIR)/BrutalistLGCMathTeXGyre.ttf: $(TMPDIR)/BrutalistLGCMathTeXGyre.sfd
94 | @echo "[3] skipping $<"
95 |
96 | $(BUILDDIR)/%.ttf: $(TMPDIR)/%.sfd
97 | @echo "[3] $< => $@"
98 | install -d $(dir $@)
99 | $(GENERATE) $<
100 | mv $<.ttf $@
101 | $(TTPOSTPROC) $@
102 | $(RM) $@~
103 | touch -r $< $@
104 |
105 | $(BUILDDIR)/status.txt: $(FULLSFD)
106 | @echo "[4] => $@"
107 | install -d $(dir $@)
108 | $(STATUS) $(VERSION) $(OLDSTATUS) $(FULLSFD) > $@
109 |
110 | $(BUILDDIR)/unicover.txt: $(patsubst %, $(TMPDIR)/%.sfd, Brutalist BrutalistSerif BrutalistMono)
111 | @echo "[5] => $@"
112 | install -d $(dir $@)
113 | $(UNICOVER) $(UNICODEDATA) $(BLOCKS) \
114 | $(TMPDIR)/Brutalist.sfd "Sans" \
115 | $(TMPDIR)/BrutalistSerif.sfd "Serif" \
116 | $(TMPDIR)/BrutalistMono.sfd "Sans Mono" > $@
117 |
118 | $(BUILDDIR)/unicover-sans.txt: $(TMPDIR)/Brutalist.sfd
119 | @echo "[5] => $@"
120 | install -d $(dir $@)
121 | $(UNICOVER) $(UNICODEDATA) $(BLOCKS) \
122 | $(TMPDIR)/Brutalist.sfd "Sans" > $@
123 |
124 | $(BUILDDIR)/unicover-lgc.txt: $(patsubst %, $(TMPDIR)/%.sfd, BrutalistLGCSans BrutalistLGCSerif BrutalistLGCSansMono)
125 | @echo "[5] => $@"
126 | install -d $(dir $@)
127 | $(UNICOVER) $(UNICODEDATA) $(BLOCKS) \
128 | $(TMPDIR)/BrutalistLGCSans.sfd "Sans" \
129 | $(TMPDIR)/BrutalistLGCSerif.sfd "Serif" \
130 | $(TMPDIR)/BrutalistLGCSansMono.sfd "Sans Mono" > $@
131 |
132 | $(BUILDDIR)/langcover.txt: $(patsubst %, $(TMPDIR)/%.sfd, Brutalist BrutalistSerif BrutalistMono)
133 | @echo "[6] => $@"
134 | install -d $(dir $@)
135 | ifeq "$(FC-LANG)" ""
136 | touch $@
137 | else
138 | $(LANGCOVER) $(FC-LANG) \
139 | $(TMPDIR)/Brutalist.sfd "Sans" \
140 | $(TMPDIR)/BrutalistSerif.sfd "Serif" \
141 | $(TMPDIR)/BrutalistMono.sfd "Sans Mono" > $@
142 | endif
143 |
144 | $(BUILDDIR)/langcover-sans.txt: $(TMPDIR)/Brutalist.sfd
145 | @echo "[6] => $@"
146 | install -d $(dir $@)
147 | ifeq "$(FC-LANG)" ""
148 | touch $@
149 | else
150 | $(LANGCOVER) $(FC-LANG) \
151 | $(TMPDIR)/Brutalist.sfd "Sans" > $@
152 | endif
153 |
154 | $(BUILDDIR)/langcover-lgc.txt: $(patsubst %, $(TMPDIR)/%.sfd, BrutalistLGCSans BrutalistLGCSerif BrutalistLGCSansMono)
155 | @echo "[6] => $@"
156 | install -d $(dir $@)
157 | ifeq "$(FC-LANG)" ""
158 | touch $@
159 | else
160 | $(LANGCOVER) $(FC-LANG) \
161 | $(TMPDIR)/BrutalistLGCSans.sfd "Sans" \
162 | $(TMPDIR)/BrutalistLGCSerif.sfd "Serif" \
163 | $(TMPDIR)/BrutalistLGCSansMono.sfd "Sans Mono" > $@
164 | endif
165 |
166 | $(BUILDDIR)/Makefile: Makefile
167 | @echo "[7] => $@"
168 | install -d $(dir $@)
169 | sed -e "s+^VERSION\([[:space:]]*\)=\(.*\)+VERSION = $(VERSION)+g"\
170 | -e "s+^SNAPSHOT\([[:space:]]*\)=\(.*\)+SNAPSHOT = $(SNAPSHOT)+g" < $< > $@
171 | touch -r $< $@
172 |
173 | $(TMPDIR)/$(SRCARCHIVE): $(addprefix $(BUILDDIR)/, $(GENDOCFULL) Makefile) $(FULLSFD)
174 | @echo "[8] => $@"
175 | install -d -m 0755 $@/$(SCRIPTSDIR)
176 | install -d -m 0755 $@/$(SRCDIR)
177 | install -d -m 0755 $@/$(FONTCONFDIR)
178 | install -d -m 0755 $@/$(DOCDIR)
179 | install -p -m 0644 $(BUILDDIR)/Makefile $@
180 | install -p -m 0755 $(GENERATE) $(TTPOSTPROC) $(LGC) $(NORMALIZE) \
181 | $(UNICOVER) $(LANGCOVER) $(STATUS) $(PROBLEMS) \
182 | $@/$(SCRIPTSDIR)
183 | install -p -m 0644 $(FULLSFD) $@/$(SRCDIR)
184 | install -p -m 0644 $(FONTCONF) $@/$(FONTCONFDIR)
185 | install -p -m 0644 $(addprefix $(BUILDDIR)/, $(GENDOCFULL)) \
186 | $(STATICDOC) $(STATICSRCDOC) $@/$(DOCDIR)
187 |
188 | $(TMPDIR)/$(FULLARCHIVE): full
189 | @echo "[8] => $@"
190 | install -d -m 0755 $@/$(TTFDIR)
191 | install -d -m 0755 $@/$(FONTCONFDIR)
192 | install -d -m 0755 $@/$(DOCDIR)
193 | install -p -m 0644 $(FULLTTF) $@/$(TTFDIR)
194 | install -p -m 0644 $(FONTCONFFULL) $@/$(FONTCONFDIR)
195 | install -p -m 0644 $(addprefix $(BUILDDIR)/, $(GENDOCFULL)) \
196 | $(STATICDOC) $@/$(DOCDIR)
197 |
198 | $(TMPDIR)/$(SANSARCHIVE): sans
199 | @echo "[8] => $@"
200 | install -d -m 0755 $@/$(TTFDIR)
201 | install -d -m 0755 $@/$(DOCDIR)
202 | install -p -m 0644 $(BUILDDIR)/Brutalist.ttf $@/$(TTFDIR)
203 | install -p -m 0644 $(addprefix $(BUILDDIR)/, $(GENDOCSANS)) \
204 | $(STATICDOC) $@/$(DOCDIR)
205 |
206 | $(TMPDIR)/$(LGCARCHIVE): lgc
207 | @echo "[8] => $@"
208 | install -d -m 0755 $@/$(TTFDIR)
209 | install -d -m 0755 $@/$(FONTCONFDIR)
210 | install -d -m 0755 $@/$(DOCDIR)
211 | install -p -m 0644 $(LGCTTF) $@/$(TTFDIR)
212 | install -p -m 0644 $(FONTCONFLGC) $@/$(FONTCONFDIR)
213 | install -p -m 0644 $(addprefix $(BUILDDIR)/, $(GENDOCLGC)) \
214 | $(STATICDOC) $@/$(DOCDIR)
215 |
216 | $(DISTDIR)/%.zip: $(TMPDIR)/%
217 | @echo "[9] => $@"
218 | install -d $(dir $@)
219 | (cd $(TMPDIR); zip -rv $(abspath $@) $(notdir $<))
220 |
221 | $(DISTDIR)/%.tar.bz2: $(TMPDIR)/%
222 | @echo "[9] => $@"
223 | install -d $(dir $@)
224 | (cd $(TMPDIR); tar cjvf $(abspath $@) $(notdir $<))
225 |
226 | %.md5: %
227 | @echo "[10] => $@"
228 | (cd $(dir $<); md5sum -b $(notdir $<) > $(abspath $@))
229 |
230 | %.sha512: %
231 | @echo "[10] => $@"
232 | (cd $(dir $<); sha512sum -b $(notdir $<) > $(abspath $@))
233 |
234 | %.sfd.norm: %.sfd
235 | @echo "[11] $< => $@"
236 | $(NORMALIZE) $<
237 | touch -r $< $@
238 |
239 | check : $(NORMSFD)
240 | for sfd in $^ ; do \
241 | echo "[12] Checking $$sfd" ;\
242 | $(PROBLEMS) $$sfd ;\
243 | done
244 |
245 | munge: $(NORMSFD)
246 | for sfd in $(SFDFILES) ; do \
247 | echo "[13] $(TMPDIR)/$$sfd.norm => $(SRCDIR)/$$sfd" ;\
248 | cp $(TMPDIR)/$$sfd.norm $(SRCDIR)/$$sfd ;\
249 | done
250 |
251 | full : $(FULLTTF) $(addprefix $(BUILDDIR)/, $(GENDOCFULL))
252 |
253 | sans : $(addprefix $(BUILDDIR)/, Brutalist.ttf $(GENDOCSANS))
254 |
255 | lgc : $(LGCTTF) $(addprefix $(BUILDDIR)/, $(GENDOCLGC))
256 |
257 | ttf : full-ttf sans-ttf lgc-ttf
258 |
259 | full-ttf : $(FULLTTF)
260 |
261 | sans-ttf: $(BUILDDIR)/Brutalist.ttf
262 |
263 | lgc-ttf : $(LGCTTF)
264 |
265 | status : $(addprefix $(BUILDDIR)/, $(GENDOCFULL))
266 |
267 | dist : src-dist full-dist sans-dist lgc-dist
268 |
269 | src-dist : $(addprefix $(DISTDIR)/$(SRCARCHIVE), $(ARCHIVEEXT) $(SUMEXT))
270 |
271 | full-dist : $(addprefix $(DISTDIR)/$(FULLARCHIVE), $(ARCHIVEEXT) $(SUMEXT))
272 |
273 | sans-dist : $(addprefix $(DISTDIR)/$(SANSARCHIVE), $(ARCHIVEEXT) $(SUMEXT))
274 |
275 | lgc-dist : $(addprefix $(DISTDIR)/$(LGCARCHIVE), $(ARCHIVEEXT) $(SUMEXT))
276 |
277 | norm : $(NORMSFD)
278 |
279 | check-harder : clean check
280 |
281 | pre-patch : munge clean
282 |
283 | clean :
284 | $(RM) -r $(TMPDIR) $(BUILDDIR) $(DISTDIR)
285 |
286 | condensed: $(NORMSFD)
287 | $(NARROW) 90 $(TMPDIR)/Brutalist.sfd.norm
288 | $(NARROW) 90 $(TMPDIR)/Brutalist-Bold.sfd.norm
289 | $(NARROW) 90 $(TMPDIR)/Brutalist-Oblique.sfd.norm
290 | $(NARROW) 90 $(TMPDIR)/Brutalist-BoldOblique.sfd.norm
291 | $(NARROW) 90 $(TMPDIR)/BrutalistSerif.sfd.norm
292 | $(NARROW) 90 $(TMPDIR)/BrutalistSerif-Bold.sfd.norm
293 | $(NARROW) 90 $(TMPDIR)/BrutalistSerif-Italic.sfd.norm
294 | $(NARROW) 90 $(TMPDIR)/BrutalistSerif-BoldItalic.sfd.norm
295 | $(NORMALIZE) $(TMPDIR)/Brutalist.sfd.norm.narrow
296 | $(NORMALIZE) $(TMPDIR)/Brutalist-Bold.sfd.norm.narrow
297 | $(NORMALIZE) $(TMPDIR)/Brutalist-Oblique.sfd.norm.narrow
298 | $(NORMALIZE) $(TMPDIR)/Brutalist-BoldOblique.sfd.norm.narrow
299 | $(NORMALIZE) $(TMPDIR)/BrutalistSerif.sfd.norm.narrow
300 | $(NORMALIZE) $(TMPDIR)/BrutalistSerif-Bold.sfd.norm.narrow
301 | $(NORMALIZE) $(TMPDIR)/BrutalistSerif-Italic.sfd.norm.narrow
302 | $(NORMALIZE) $(TMPDIR)/BrutalistSerif-BoldItalic.sfd.norm.narrow
303 | cp $(TMPDIR)/Brutalist.sfd.norm.narrow.norm $(TMPDIR)/BrutalistCondensed.sfd.norm
304 | cp $(TMPDIR)/Brutalist-Bold.sfd.norm.narrow.norm $(TMPDIR)/BrutalistCondensed-Bold.sfd.norm
305 | cp $(TMPDIR)/Brutalist-Oblique.sfd.norm.narrow.norm $(TMPDIR)/BrutalistCondensed-Oblique.sfd.norm
306 | cp $(TMPDIR)/Brutalist-BoldOblique.sfd.norm.narrow.norm $(TMPDIR)/BrutalistCondensed-BoldOblique.sfd.norm
307 | cp $(TMPDIR)/BrutalistSerif.sfd.norm.narrow.norm $(TMPDIR)/BrutalistSerifCondensed.sfd.norm
308 | cp $(TMPDIR)/BrutalistSerif-Bold.sfd.norm.narrow.norm $(TMPDIR)/BrutalistSerifCondensed-Bold.sfd.norm
309 | cp $(TMPDIR)/BrutalistSerif-Italic.sfd.norm.narrow.norm $(TMPDIR)/BrutalistSerifCondensed-Italic.sfd.norm
310 | cp $(TMPDIR)/BrutalistSerif-BoldItalic.sfd.norm.narrow.norm $(TMPDIR)/BrutalistSerifCondensed-BoldItalic.sfd.norm
311 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Brutalist Mono
2 |
3 | Brutalist Mono is a very simple modification on top of [DejaVu Sans Mono](https://github.com/dejavu-fonts/dejavu-fonts) (yes, _another one_), making it more suitable for coding. The changes are very nitpicky and you can probably just move along.
4 |
5 | 
6 |
7 | Comparison to DejaVu Sans Mono:
8 |
9 | 
10 |
11 | ## Changes from DejaVu Sans Mono
12 |
13 | v1.0:
14 |
15 | * `r` – offset to the left
16 | * `_` – increase height, reduce width
17 | * `-` (minus) – make wider
18 | * `0` – slashed instead of dotted
19 |
20 | v1.1:
21 |
22 | * `l` – increase upper arm and recenter
23 | * `i` and `j` – increase dot sizes, making them square
24 |
25 | v1.2:
26 |
27 | * `l` – drop foot below the baseline
28 | * `C` – flatter terminals
29 | * `G` – flatter top terminal
30 | * `J` – flatter bottom terminal
31 | * `S` – flatter terminals
32 | * `a` – flatter top terminal
33 | * `c` – flatter terminals
34 | * `e` – flatter bottom terminal
35 | * `g` – flatter bottom terminal
36 | * `r` – flatter top terminal
37 | * `s` – flatter terminals
38 | * `2` – flatter top terminal
39 | * `3` – flatter terminals
40 | * `5` – flatter bottom terminal and mid stroke
41 | * `6` – flatter top terminal
42 | * `9` – flatter bottom terminal
43 | * `$` – flatter terminals
44 |
45 | v2.0:
46 |
47 | * `C` – slightly rounder shape
48 | * `G` – more even upper terminal
49 | * `S` – more even terminals
50 | * `c` – more even terminals
51 | * `l` – more pedantic bottom
52 | * `r` – wider right terminal
53 | * `s` – more even terminals
54 | * `9` – more even lower terminal
55 | * `g` – more even lower terminal
56 | * `J` – clean up lower terminal
57 | * `~` – blatantly stolen from Hack
58 | * `t` – make bottom curved the same as `l`
59 | * `j` – make bottom curved the same as `l`
60 | * `[` – make wider, match `lparen`'s width
61 | * `]` – make wider, match `rparen`'s width
62 | * `5` – more even bottom terminal, add back the "spike" in the middle
63 | * `y` – curved bottom terminal
64 | * `$` – more even terminals
65 |
66 | v2.1:
67 |
68 | * `,`, `.`, `;`, `:`, `!`, `?` – use rounded dots and commas (adapted from Hack)
69 | * `*` – use Menlo variant
70 | * `l` – clean up bottom curve a little bit
71 |
72 | v2.1.1
73 |
74 | * fix: font not showing up as monospace ([#2](https://github.com/BRUTALISM/Brutalist/pull/2))
75 |
76 | ## Motivation
77 |
78 | Another DejaVu/Vera Mono clone? Yes. But this one is opinionated:
79 |
80 | 1. I don't care about "readability on small font sizes". Make your font larger and/or get a decent monitor. It's the 21st century.
81 | 2. I don't care about font hinting. Modern monitors are high-DPI.
82 | * Note: The native renderer on Windows seems to require manual hinting instructions to be added even when rendering at high DPI screens. I will not be adding this. Therefore, this font looks terrible on Windows, unless you're using an Electron app (VS Code, Atom, etc). They're using a custom renderer and render correctly.
83 | 3. I don't care about `O0`, `lI1|`, or any of that crap. It's fine. See point #1.
84 |
85 | If any of this bothers you, try [Hack](https://github.com/source-foundry/Hack). It's awesome. I made [my mod](https://github.com/BRUTALISM/Hack) available as well.
86 |
87 | ### Why not Hack?
88 |
89 | Hack is great, but has way too many unnecessary modifications on top of the baseline DejaVu Sans Mono. To name a few:
90 |
91 | * the parentheses are unnecessarily spread out in earlier versions, and too rounded in newer ones
92 | * the `1` has an awkward downward facing arm
93 | * contributing is complicated if you're only using plain old FontForge (I don't want to shell out EUR 250 for Glyphs.app)
94 | * [alt-hack](https://github.com/source-foundry/alt-hack) is great but I ended up just using it to revert most mods back to the original DejaVu style, so I figured why bother?
95 |
96 | ### Why not Menlo?
97 |
98 | * the uppercase `N` is hideously wide (once you see it, you can not unsee it)
99 | * punctuation is unnecessarily exaggerated
100 | * there are [many weird tweaks](http://leancrew.com/all-this/2009/10/the-compleat-menlovera-sans-comparison/) done to it (relative to its parent Bitstream Vera Sans Mono) to make it render better on ancient Mac OS versions with low-DPI monitors, which disqualifies it immediately (see point #1 in "Motivation" above)
101 |
102 | ### Why not DejaVu Sans Mono?
103 |
104 | This typeface is almost perfect for programming†, except:
105 |
106 | * the underscore is ridiculously thin, making it visually odd when reading `THINGS_WITH_MANY_UNDERSCORES` and esoteric C/C++ identifiers with `__multiple__underscores__`
107 | * the lowercase `r` is offset a bit to the right (Hack has got this right – once you see it, you can not unsee it)
108 | * the `-` glyph is ridiculously narrow
109 | * other nitpicky stuff you really don't care about but I do
110 |
111 | † – [Source Code Pro](https://github.com/adobe-fonts/source-code-pro) is still the king, and I don't dare [come at the king](https://www.youtube.com/watch?v=py1WDlaIr9A).
112 |
113 | ## Building
114 |
115 | Short version:
116 |
117 | * Edit the .sdf files with FontForge (if you want to mod)
118 | * `make`
119 | * observe the `build` folder
120 |
121 | This will probably fail, so: [long version](BUILDING.md).
122 |
123 | ## Contributing
124 |
125 | If you really convince me.
126 |
127 | ## License
128 |
129 | Same as DejaVu fonts, see [LICENSE](LICENSE).
--------------------------------------------------------------------------------
/images/brutalist.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BRUTALISM/Brutalist/780c9de0895b516ff604e546804793a835e8ab0c/images/brutalist.jpg
--------------------------------------------------------------------------------
/images/brutalist_vs_dejavu.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BRUTALISM/Brutalist/780c9de0895b516ff604e546804793a835e8ab0c/images/brutalist_vs_dejavu.gif
--------------------------------------------------------------------------------
/scripts/colorize.pl:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env perl
2 | use warnings;
3 |
4 | # $Id$
5 |
6 | # glyph comment colorizing
7 | # (c)2004 Stepan Roh (PUBLIC DOMAIN)
8 | # usage: ./colorize.pl color sfd_files+ < glyph_codes_file
9 | # will create files with suffix .color
10 | # color is either none, yellow, green or hexadecimal 24bit value (0xff0000 is red)
11 | # glyph_codes_file should contain codes of glyphs to be colored delimited by whitespace and/or commas
12 | # - glyph codes are U+nnnn (nnnn is hexadecimal)
13 | # - syntax for code ranges: U+nnnn-U+nnnn (no whitespace inside)
14 | # file colorize.pe is created during the execution (it is safe to remove it after)
15 |
16 | # change to the fontforge binary location if it is not in the system PATH
17 | $fontforge = "fontforge";
18 |
19 | sub decode_color($) {
20 | my ($color) = @_;
21 | if ($color eq 'yellow' || $color eq 'wip') {
22 | return "0xffff00";
23 | } elsif ($color eq 'green' || $color eq 'req') {
24 | return "0x00ff00";
25 | } elsif ($color eq 'none') {
26 | return -2;
27 | }
28 | return $color;
29 | }
30 |
31 | sub decode_glyph_code($) {
32 | my ($code) = @_;
33 | $code =~ s/^[Uu]\+//;
34 | return $code;
35 | }
36 |
37 | sub parse_glyph_codes_from_stdin() {
38 | my @ret = ();
39 | my @input = split (/(?:\s|,)+/, join ('', ));
40 | foreach $token (@input) {
41 | if ($token =~ /^(.*)-(.*)$/) {
42 | push (@ret, decode_glyph_code ($1), decode_glyph_code ($2));
43 | } else {
44 | push (@ret, decode_glyph_code ($token));
45 | push (@ret, decode_glyph_code ($token));
46 | }
47 | }
48 | return @ret;
49 | }
50 |
51 | if (@ARGV < 2) {
52 | print STDERR "usage: $0 color sfd_files+ < glyph_codes_file\n";
53 | exit (1);
54 | }
55 |
56 | @glyph_codes = parse_glyph_codes_from_stdin();
57 | $color = decode_color(shift @ARGV);
58 |
59 | sub min($$) {
60 | my ($a, $b) = @_;
61 | return ($a < $b) ? $a : $b;
62 | }
63 |
64 | open (PE, '>colorize.pe') || die "Unable to open colorize.pe : $!\n";
65 | print PE 'i = 1
66 | while ( i < $argc )
67 | Open($argv[i], 1)
68 | SelectNone()
69 | ';
70 | for (my $i = 0; $i < @glyph_codes; $i += 16) {
71 | print PE ' SelectMore(', join (',', map { '0u'.$_ } @glyph_codes[$i..min($i+15, @glyph_codes-1)]), ")\n";
72 | }
73 | print PE ' SetCharColor('.$color.')
74 | Save($argv[i] + ".color")
75 | i++
76 | endloop
77 | ';
78 | close (PE) || die "Unable to close colorize.pe : $!\n";
79 |
80 | system ($fontforge, '-nosplash', '-script', 'colorize.pe', @ARGV);
81 |
82 | 1;
83 |
--------------------------------------------------------------------------------
/scripts/generate.pe:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env fontforge
2 | # $Id$
3 |
4 | # script file for FontForge for TTF generation
5 | # usage:
6 | # mkdir generated
7 | # chmod +x generate.pe
8 | # ./generate.pe *.sfd
9 |
10 | # font generation flags:
11 | # 8 => do not include TT instructions (for experimental typefaces)
12 | # 0x20 => generate a 'PfEd' table and store glyph comments
13 | # 0x40 => generate a 'PfEd' table and store glyph colors
14 | # 0x800 => generate old fashioned kern tables.
15 | # - this one is important because it generates correct kerning tables
16 | def_gen_flags = 0x20 + 0x40 + 0x800
17 | exp_gen_flags = def_gen_flags + 8
18 |
19 | if ($version < "20080330")
20 | Error("Your version of FontForge is too old - 20080330 or newer is required");
21 | endif
22 | # FoundryName is not used in TTF generation
23 | SetPref("FoundryName", "Brutalist")
24 | # first 4 characters of TTFFoundry are used for achVendId
25 | SetPref("TTFFoundry", "Brutalist")
26 | i = 1
27 | while ( i < $argc )
28 | Open($argv[i], 1)
29 | gen_flags = def_gen_flags
30 | # Serif Italic and Serif Bold Italic are experimental
31 | if ((Strcasestr ($fontname, "Serif") > -1) && (Strcasestr ($fontname, "Italic") > -1))
32 | gen_flags = exp_gen_flags
33 | endif
34 | if (Strcasestr ($fontname, "Condensed") > -1)
35 | gen_flags = exp_gen_flags
36 | endif
37 | if (Strcasestr ($fontname, "ExtraLight") > -1)
38 | gen_flags = exp_gen_flags
39 | endif
40 | Generate( $curfont + ".ttf", "", gen_flags)
41 | Close()
42 | i++
43 | endloop
44 |
--------------------------------------------------------------------------------
/scripts/generate.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env fontforge
2 | # $Id: generate.py 1902 2007-06-21 23:44:12Z apanov $
3 |
4 | # script file for FontForge for TTF generation
5 | # usage:
6 | # mkdir generated
7 | # chmod +x generate.pe
8 | # ./generate.pe *.sfd
9 | import fontforge, sys;
10 | required_version = "20080330"
11 |
12 | # font generation flags:
13 | # omit-instructions => do not include TT instructions (for experimental typefaces)
14 | # opentype => include OpenType tables
15 | # glyph-comments => generate a 'PfEd' table and store glyph comments
16 | # glyph-colors => generate a 'PfEd' table and store glyph colors
17 | # old-kern => generate old fashioned kern tables.
18 | # - this one is important because it generates correct kerning tables for legacy
19 | # applications
20 | def_gen_flags = ("opentype", "glyph-comments", "glyph-colors", "old-kern")
21 | exp_gen_flags = def_gen_flags + ("omit-instructions",)
22 |
23 | if fontforge.version() < required_version:
24 | print ("Your version of FontForge is too old - %s or newer is required" % (required_version));
25 | # FoundryName is not used in TTF generation
26 | fontforge.setPrefs("FoundryName", "Brutalist");
27 | # first 4 characters of TTFFoundry are used for achVendId
28 | fontforge.setPrefs("TTFFoundry", "Brutalist")
29 | i = 1
30 | while i < len(sys.argv):
31 | font=fontforge.open(sys.argv[i]);
32 | gen_flags = def_gen_flags
33 |
34 | # Serif Italic and Serif Bold Italic are experimental
35 | if font.fontname.rfind("Serif") > -1 and font.fontname.rfind("Italic") > -1:
36 | gen_flags = exp_gen_flags;
37 | if font.fontname.rfind("Condensed") > -1:
38 | gen_flags = exp_gen_flags;
39 | if font.fontname.rfind("ExtraLight") > -1:
40 | gen_flags = exp_gen_flags;
41 |
42 | fontname = "generated/" + font.fontname + ".ttf";
43 | font.generate(fontname,"",gen_flags);
44 | font.close();
45 | i += 1;
46 |
--------------------------------------------------------------------------------
/scripts/italicize.pe:
--------------------------------------------------------------------------------
1 | # $Id$
2 |
3 | # script file for FontForge for font italicization
4 | # usage: fontforge -script italicize.pe angle *.sfd
5 | # created files have suffix .oblique
6 |
7 | if ($argc < 2)
8 | Error("usage: angle sfds...")
9 | endif
10 | angle = Strtol($argv[1])
11 | i = 2
12 | while (i < $argc)
13 | Open($argv[i], 1)
14 | SelectAll()
15 | Skew(angle)
16 | SetItalicAngle(-angle)
17 | # Panose changes are valid only for Brutalist fonts (I guess)
18 | SetPanose(5, 3)
19 | SetPanose(7, 11)
20 | new_fontname = $fontname
21 | j = Strstr(new_fontname, "-Roman")
22 | if (j > -1)
23 | new_fontname = Strsub(new_fontname, 0, j)
24 | endif
25 | if (Strstr(new_fontname, "-") > -1)
26 | new_fontname = new_fontname + "Oblique"
27 | else
28 | new_fontname = new_fontname + "-Oblique"
29 | endif
30 | new_fullname = $fullname + " Oblique"
31 | SetFontNames(new_fontname, "", new_fullname)
32 | SetTTFName(1033, 3, new_fullname)
33 | Save($argv[i] + ".oblique")
34 | i++
35 | endloop
36 |
--------------------------------------------------------------------------------
/scripts/langcover.pl:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env perl
2 | use warnings;
3 |
4 | # $Id$
5 |
6 | # language coverage analyzator
7 | # (c)2006 Stepan Roh (PUBLIC DOMAIN)
8 | # usage: ./langcover.pl fc-lang_dir sfd_file1 label1 (sfd_file2 label2...)
9 | # files from http://webcvs.freedesktop.org/fontconfig/fontconfig/fc-lang/ should be downloaded to fc-lang directory
10 |
11 | use FileHandle;
12 |
13 | sub parse_fc_lang_dir($);
14 | sub parse_orth_file($;$);
15 | sub parse_sfd_file($);
16 | sub inc_coverage($$);
17 | sub print_coverage();
18 |
19 | # map (language code => ( 'name' => name, 'chars' => list of glyphs, 'coverage' => ( sfd_file => coverage ) )
20 | %langs = ();
21 |
22 | sub parse_fc_lang_dir($) {
23 | my ($fc_lang_dir) = @_;
24 |
25 | opendir(DIR, $fc_lang_dir) || die "Unable to open $fc_lang_dir : $!\n";
26 | my @orth_files = map { "$fc_lang_dir/$_" } grep { /\.orth$/ } readdir(DIR);
27 | closedir(DIR);
28 |
29 | foreach $orth_file (@orth_files) {
30 | parse_orth_file($orth_file);
31 | }
32 | }
33 |
34 | # missing or UTF-8 lang names
35 | %lang_names = (
36 | 'gn' => 'Guarani',
37 | 'ja' => 'Japanese',
38 | 'nb' => 'Norwegian Bokmal',
39 | 'no' => 'Norwegian (Bokmal)',
40 | 'se' => 'North Sami',
41 | 'sma' => 'South Sami',
42 | 'smj' => 'Lule Sami',
43 | 'smn' => 'Inari Sami',
44 | 'sms' => 'Skolt Sami',
45 | 'vo' => 'Volapuk',
46 | 'zh-tw' => 'Chinese (traditional)',
47 | );
48 |
49 | sub parse_orth_file($;$) {
50 | my ($orth_file, $lang) = @_;
51 |
52 | if (!defined $lang) {
53 | ($lang) = ($orth_file =~ m,/([^/]*)\.[^./]*$,);
54 | $lang =~ tr/_/-/;
55 | }
56 | # XXX some names in orth files have different language codes
57 | my $orth_lang = $lang;
58 | $orth_lang = 'kw' if ($orth_lang eq 'ay');
59 | $orth_lang = 'kw' if ($orth_lang eq 'fj');
60 | $orth_lang = 'eth' if ($orth_lang eq 'gez');
61 | $orth_lang = 'hi' if ($orth_lang eq 'pa');
62 | $orth_lang = 'cu' if ($orth_lang eq 'sco');
63 | $orth_lang = 'af' if ($orth_lang eq 'sm');
64 | $orth_lang = 'smj' if ($orth_lang eq 'sms');
65 | $orth_lang = 'ge' if ($orth_lang eq 'te');
66 | if (exists($lang_names{$lang})) {
67 | $langs{$lang}{'name'} = $lang_names{$lang};
68 | }
69 | my $f = new FileHandle($orth_file) || die "Unable to open $orth_file : $!\n";
70 | while (<$f>) {
71 | if (!exists($langs{$lang}{'name'})) {
72 | if (/^#\s*(.*?)\s*\($lang\)/i) {
73 | $langs{$lang}{'name'} = $1;
74 | next;
75 | }
76 | if (/^#\s*(.*?)\s*\($orth_lang\)/i) {
77 | $langs{$lang}{'name'} = $1;
78 | next;
79 | }
80 | }
81 | next if (/^\s*(#|$)/);
82 | if (/^\s*include\s+(\S+)/) {
83 | my $include = $1;
84 | my $include_file;
85 | ($include_file = $orth_file) =~ s,/[^/]+$,/$include,;
86 | if ($include_file eq $orth_file) {
87 | print "$orth_file creates an include loop";
88 | }
89 | else {
90 | parse_orth_file($include_file, $lang);
91 | }
92 | next;
93 | }
94 | my ($start) = ($_ =~ /^\s*(\S+)/);
95 | if ($lang eq 'ibo') {
96 | # XXX ibo.orth 1ee1 -> 1ee4 (https://bugs.freedesktop.org/show_bug.cgi?id=6237)
97 | $start = '1ee4' if ($start eq '1ee1');
98 | }
99 | my $end = $start;
100 | if ($start =~ /-/) {
101 | ($start, $end) = split(/-/, $start);
102 | }
103 | # XXX ab.orth 0re1 -> 04e1 (https://bugs.freedesktop.org/show_bug.cgi?id=6238)
104 | $end = '04e1' if ($end eq '0re1');
105 | $start = hex ($start);
106 | $end = hex ($end);
107 | for (my $dec_enc = $start; $dec_enc <= $end; $dec_enc++) {
108 | $langs{$lang}{'chars'}{$dec_enc} = 1;
109 | }
110 | }
111 | $f->close();
112 | }
113 |
114 | sub inc_coverage($$) {
115 | my ($sfd_file, $dec_enc) = @_;
116 |
117 | foreach $lang (keys %langs) {
118 | if (exists $langs{$lang}{'chars'}{$dec_enc}) {
119 | $langs{$lang}{'coverage'}{$sfd_file}++;
120 | }
121 | }
122 | }
123 |
124 | sub parse_sfd_file($) {
125 | my ($sfd_file) = @_;
126 |
127 | open (F, $sfd_file) || die "Unable to open $sfd_file : $!\n";
128 | my $curchar = '';
129 | my $curenc = '';
130 | my $empty = 0;
131 | while () {
132 | if (/^StartChar:\s*(\S+)\s*$/) {
133 | $curchar = $1;
134 | $curenc = '';
135 | $empty = 0;
136 | } elsif (/^Colour:/) {
137 | # XXX this is quick'n'dirty hack to detect non-empty glyphs
138 | $empty = 1;
139 | } elsif (/^Encoding:\s*\d+\s*(\d+)\s*\d+\s*$/) {
140 | $curenc = $1;
141 | } elsif ($curenc && !$empty && /^EndChar\s*/) {
142 | inc_coverage ($sfd_file, $curenc);
143 | }
144 | }
145 | close (F);
146 | }
147 |
148 | # TODO: formats would be better
149 | sub print_coverage() {
150 | print < 0) {
170 | printf " %3d%%", $percent;
171 | } else {
172 | print " ";
173 | }
174 | printf " %-13s", "($coverage/$length)";
175 | }
176 | print "\n";
177 | }
178 | }
179 |
180 | if (@ARGV < 3) {
181 | print STDERR "usage: fc-lang_dir sfd_file1 label1 (sfd_file2 label2...)\n";
182 | exit 1;
183 | }
184 |
185 | $fc_lang_dir = shift @ARGV;
186 | @sfd_files = ();
187 | %sfd_files = ();
188 | while (@ARGV) {
189 | $sfd_file = shift @ARGV;
190 | $label = shift @ARGV;
191 | push (@sfd_files, $sfd_file);
192 | $sfd_files{$sfd_file} = $label;
193 | }
194 |
195 | parse_fc_lang_dir($fc_lang_dir);
196 | foreach $sfd_file (@sfd_files) {
197 | parse_sfd_file($sfd_file);
198 | }
199 | print_coverage();
200 |
201 | 1;
202 |
--------------------------------------------------------------------------------
/scripts/lgc.pe:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env fontforge
2 |
3 | # script to strip non-LGC glyphs out of the fonts
4 |
5 | i = 1
6 | while ( i < $argc )
7 | Open($argv[i], 1)
8 |
9 | Select(0u0530, 0u1cff) # Armenian through Vedic Extensions
10 | SelectMore(0u2c00, 0u2c5f) # Glagolitic
11 | SelectMore(0u2d00, 0u2ddf) # Georgian Supplement through Ethiopic Extended
12 | SelectMore(0u2e80, 0ua63f) # CJK Radicals through Vai
13 | SelectMore(0ua6a0, 0ua6ff) # Banum
14 | SelectMore(0ua800, 0udfff) # Syloti Nagri through Hangul
15 | SelectMore(0uf900, 0ufaff) # CJK Compatibility Ideographs
16 | SelectMore(0ufb07, 0ufe1f) # Alphabetical Presentation Forms (partial) through Vertical Forms
17 | SelectMore(0ufe30, 0uffef) # CJK Compatibility Forms through Halfwidth and Fullwidth Forms
18 | SelectMore(0u10000, 0u1d37f) # Linear B Syllabary through Counting Rod Numerals
19 | SelectMore(0u1ee00, 0u1f02f) # Arabic Mathematical Alphabetic Symbols through Mahjong Tiles
20 | SelectMore(0u1f200, 0u1f2ff) # Enclosed Ideographic Supplement
21 | SelectMore(0u20000, 0ueffff) # CJK Unified Ideographs Extention B and everything thereafter
22 | Clear()
23 |
24 | Save($argv[i])
25 | i++
26 | endloop
27 |
--------------------------------------------------------------------------------
/scripts/merge.pl:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env perl
2 | use warnings;
3 |
4 | # $Id$
5 |
6 | # SFD files merger
7 | # (c)2005 Stepan Roh (PUBLIC DOMAIN)
8 | # usage: ./merge.pl sfd_from sfd_to sfd_out
9 | # will merge sfd_to with sfd_from into the sfd_out (if there are some merges)
10 | # will print merged glyph codes on stdout
11 |
12 | # SFD = ( 'header' => @header_lines, 'footer' => @footer_lines, 'glyphs' => ( glyphenc => @glyph_lines ) )
13 |
14 | sub load_sfd ($) {
15 | my ($sfdfile) = @_;
16 |
17 | my %sfd = ();
18 | open (SFD, $sfdfile) || die "Unable to open $sfdfile : $!\n";
19 | # -1 = header, 0 = glyphs, 1 = footer
20 | my $section = -1;
21 | my $enc = 0;
22 | my @cur = ();
23 | while () {
24 | if (/^StartChar:/) {
25 | $section = 0;
26 | } elsif (/^Encoding:\s*(\d+)/) {
27 | $enc = $1;
28 | }
29 | if ($section == -1) {
30 | push (@{$sfd{'header'}}, $_);
31 | } elsif ($section == 1) {
32 | push (@{$sfd{'footer'}}, $_);
33 | } else {
34 | push (@cur, $_);
35 | }
36 | if (/^EndChar\s*$/) {
37 | $section = 1;
38 | @{$sfd{'glyphs'}{$enc}} = @cur;
39 | @cur = ();
40 | }
41 | }
42 | close (SFD);
43 |
44 | return %sfd;
45 | }
46 |
47 | sub save_sfd ($\%) {
48 | my ($sfdfile, $sfd_ref) = @_;
49 |
50 | open (SFD, '>'.$sfdfile) || die "Unable to open $sfdfile : $!\n";
51 | print SFD @{$$sfd_ref{'header'}};
52 | foreach $enc (sort { $a <=> $b } keys %{$$sfd_ref{'glyphs'}}) {
53 | print SFD @{$$sfd_ref{'glyphs'}{$enc}};
54 | }
55 | print SFD @{$$sfd_ref{'footer'}};
56 | close (SFD);
57 | }
58 |
59 | sub is_dummy_glyph (\@) {
60 | my ($glyph_ref) = @_;
61 |
62 | foreach $l (@$glyph_ref) {
63 | if ($l =~ /^Colour:/) {
64 | # XXX this is quick'n'dirty hack to detect dummy glyphs
65 | return 1;
66 | }
67 | }
68 |
69 | return 0;
70 | }
71 |
72 | sub merge_glyphs (\%\%) {
73 | my ($sfd_from, $sfd_to) = @_;
74 |
75 | my $merged = 0;
76 | foreach $enc (sort { $a <=> $b } keys %{$$sfd_from{'glyphs'}}) {
77 | if (!exists ($$sfd_to{'glyphs'}{$enc}) ||
78 | (!is_dummy_glyph(@{$$sfd_from{'glyphs'}{$enc}}) && is_dummy_glyph(@{$$sfd_to{'glyphs'}{$enc}}))) {
79 | $$sfd_to{'glyphs'}{$enc} = $$sfd_from{'glyphs'}{$enc};
80 | print $enc, "\n";
81 | $merged++;
82 | }
83 | }
84 |
85 | return $merged;
86 | }
87 |
88 | if (@ARGV < 3) {
89 | print STDERR "usage: $0 sfd_from sfd_to sfd_out\n";
90 | exit (1);
91 | }
92 |
93 | ($sfdfile_from, $sfdfile_to, $sfdfile_out) = @ARGV;
94 |
95 | %sfd_from = load_sfd ($sfdfile_from);
96 | %sfd_to = load_sfd ($sfdfile_to);
97 | if (merge_glyphs (%sfd_from, %sfd_to) > -1) {
98 | save_sfd ($sfdfile_out, %sfd_to);
99 | }
100 |
101 | 1;
102 |
--------------------------------------------------------------------------------
/scripts/mescover.pl:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env perl
2 | use warnings;
3 |
4 | # $Id$
5 |
6 | # MES coverage analyzer
7 | # (c)2005 Stepan Roh (PUBLIC DOMAIN)
8 | # usage: ./mescover.pl mes_spec_file sfd_files+
9 |
10 | sub parse_mes_spec_file($);
11 | sub init_mes_glyphs();
12 | sub print_mes_glyphs();
13 | sub parse_sfd_file($);
14 |
15 | # map (MES glyph dec => 1)
16 | %mes_glyphs = ();
17 | $mes_collection = 'UNKNOWN';
18 |
19 | sub parse_mes_spec_file($) {
20 | my ($mes_file) = @_;
21 |
22 | open (F, $mes_file) || die "Unable to open $mes_file : $!\n";
23 | my $plane = '00';
24 | while () {
25 | if (/^Collection Name:\s+(.*?)\s*$/) {
26 | $mes_collection = $1;
27 | } elsif (/^Plane\s+(\d+)\s*$/) {
28 | $plane = $1;
29 | } elsif (/^([A-F0-9]+)\s+(.*?)\s*$/) {
30 | my $row = $1;
31 | my @cells = split(/\s+/, $2);
32 | foreach $cell (@cells) {
33 | my @range = split(/-/, $cell);
34 | if (@range == 1) {
35 | my $hexenc = $plane.$row.$range[0];
36 | my $decenc = hex($hexenc);
37 | $mes_glyphs{$decenc} = 1;
38 | } else {
39 | my $hexenc_start = $plane.$row.$range[0];
40 | my $decenc_start = hex($hexenc_start);
41 | my $hexenc_end = $plane.$row.$range[1];
42 | my $decenc_end = hex($hexenc_end);
43 | for (my $decenc = $decenc_start; $decenc <= $decenc_end; $decenc++) {
44 | $mes_glyphs{$decenc} = 1;
45 | }
46 | }
47 | }
48 | }
49 | }
50 | close (F);
51 | }
52 |
53 | sub init_mes_glyphs() {
54 | foreach $decenc (keys %mes_glyphs) {
55 | $mes_glyphs{$decenc} = 1;
56 | }
57 | }
58 |
59 | sub print_mes_glyphs() {
60 | my $cnt = 0;
61 | my $missed = 0;
62 | my $lastenc = -100;
63 | my $in_range = 0;
64 | foreach $decenc (sort keys %mes_glyphs) {
65 | if ($mes_glyphs{$decenc} != 0) {
66 | if ($decenc == $lastenc + 1) {
67 | $lastenc = $decenc;
68 | $in_range = 1;
69 | } else {
70 | if ($in_range) {
71 | printf("-U+%04x", $lastenc);
72 | }
73 | printf(" U+%04x", $decenc);
74 | $in_range = 0;
75 | }
76 | $lastenc = $decenc;
77 | $missed++;
78 | }
79 | $cnt++;
80 | }
81 | if ($in_range) {
82 | printf("-U+%04x", $lastenc);
83 | }
84 | print " [$missed/$cnt]";
85 | }
86 |
87 | sub parse_sfd_file($) {
88 | my ($sfd_file) = @_;
89 |
90 | open (F, $sfd_file) || die "Unable to open $sfd_file : $!\n";
91 | my $curchar = '';
92 | my $curenc = '';
93 | my $empty = 0;
94 | while () {
95 | if (/^StartChar:\s*(\S+)\s*$/) {
96 | $curchar = $1;
97 | $curenc = '';
98 | $empty = 0;
99 | } elsif (/^Colour:/) {
100 | # XXX this is quick'n'dirty hack to detect non-empty glyphs
101 | $empty = 1;
102 | } elsif (/^Encoding:\s*\d+\s*(\d+)\s*\d+\s*$/) {
103 | $curenc = $1;
104 | } elsif ($curenc && !$empty && /^EndChar\s*/) {
105 | if (defined $mes_glyphs{$curenc}) {
106 | $mes_glyphs{$curenc} = 0;
107 | }
108 | }
109 | }
110 | close (F);
111 | }
112 |
113 | if (@ARGV < 2) {
114 | print STDERR "usage: mes_spec_file sfd_files+\n";
115 | exit 1;
116 | }
117 |
118 | $mes_spec_file = shift @ARGV;
119 | parse_mes_spec_file($mes_spec_file);
120 | print "Missing glyphs from collection $mes_collection\n\n";
121 | while (@ARGV) {
122 | $sfd_file = shift @ARGV;
123 | print $sfd_file, ':';
124 | init_mes_glyphs();
125 | parse_sfd_file($sfd_file);
126 | print_mes_glyphs();
127 | print "\n";
128 | }
129 |
130 | 1;
131 |
--------------------------------------------------------------------------------
/scripts/narrow.pe:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env fontforge
2 | # $Id$
3 |
4 | # script file for FontForge for font narrowing
5 | # usage: fontforge -script narrow.pe scale *.sfd
6 | # created files have suffix .narrow
7 |
8 | # Brutalist Condensed have scale 90
9 |
10 | if ($version < "20050209")
11 | Error("Your version of FontForge transforms glyphs w/o content");
12 | endif
13 | if ($argc < 2)
14 | Error("usage: scale sfds...")
15 | endif
16 | scale = Strtol($argv[1])
17 | i = 2
18 | while (i < $argc)
19 | Open($argv[i], 1)
20 | SelectAll()
21 | Scale(scale, 100, 0, 0)
22 | # Panose changes are valid only for Brutalist fonts (I guess)
23 | SetPanose(3, 6)
24 | new_fontname = $fontname
25 | old_familyname = $familyname
26 | j = Strstr(new_fontname, "-Roman")
27 | if (j > -1)
28 | new_fontname = Strsub(new_fontname, 0, j)
29 | endif
30 | j = Strstr(new_fontname, "-")
31 | if (j > -1)
32 | new_fontname = Strsub(new_fontname, 0, j) + "Condensed" + Strsub(new_fontname, j)
33 | else
34 | new_fontname = new_fontname + "Condensed"
35 | endif
36 | new_fullname = $fullname
37 | newprefsubfamily = "Condensed"
38 | new_subfamily = $weight
39 | j = Strstr(new_fullname, "Bold")
40 | if (j == -1)
41 | j = Strstr(new_fullname, "Oblique")
42 | endif
43 | if (j == -1)
44 | j = Strstr(new_fullname, "Italic")
45 | endif
46 | if (j > -1)
47 | new_subfamily = Strsub(new_fullname, j)
48 | new_fullname = Strsub(new_fullname, 0, j) + "Condensed " + new_subfamily
49 | newprefsubfamily = newprefsubfamily + " " + new_subfamily
50 | else
51 | new_fullname = new_fullname + " Condensed"
52 | endif
53 | new_familyname = $familyname + " Condensed"
54 | SetFontNames(new_fontname, new_familyname, new_fullname)
55 | SetOS2Value("Width",4)
56 | SetTTFName(1033, 2, new_subfamily) # ttf_subfamily
57 | SetTTFName(1033, 3, new_fullname) # ttf_uniqueid
58 | SetTTFName(1033, 16, old_familyname) # ttf_preffamilyname
59 | SetTTFName(1033, 17, newprefsubfamily) # ttf_prefmodifiers
60 | Save($argv[i] + ".narrow")
61 | i++
62 | endloop
63 |
--------------------------------------------------------------------------------
/scripts/narrowmerge.pe:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env fontforge
2 | # $Id$
3 |
4 | # script file for FontForge for font narrowing after merge
5 | # usage: fontforge -script narrowmerge.pe scale sfd glyph_codes*
6 | # created file has suffix .narrow
7 |
8 | # Brutalist Condensed have scale 90
9 |
10 | if ($version < "20050209")
11 | Error("Your version of FontForge transforms glyphs w/o content");
12 | endif
13 | if ($argc < 3)
14 | Error("usage: scale sfd glyph_codes...")
15 | endif
16 | scale = Strtol($argv[1])
17 | file = $argv[2]
18 | if ($argc < 4)
19 | Print("Nothing to do for ", file)
20 | Quit(0)
21 | endif
22 | Open(file, 1)
23 | i = 3
24 | while (i < $argc)
25 | Select(Strtol($argv[i]))
26 | ref_count = GlyphInfo("RefCount")
27 | if (ref_count == 0)
28 | Scale(scale, 100, 0, 0)
29 | else
30 | Print(GlyphInfo("Name"), " contains references: not scaled")
31 | endif
32 | i++
33 | endloop
34 | Save(file + ".narrow")
35 |
--------------------------------------------------------------------------------
/scripts/narrowmerge.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # $Id$
4 |
5 | # This script will merge new glyphs into the condensed faces.
6 |
7 | # It scans for Condensed faces in the current directory and creates
8 | # new files called BrutalistCondensed*.sfd.merged
9 |
10 | # The script requires that all fonts are normalized with
11 | # sfdnormalize.pl. The newly created .merged file is automatically
12 | # normalized
13 |
14 |
15 | for tofile in *Condensed*.sfd; do
16 | fromfile=`echo $tofile | sed 's,Condensed,,'`
17 |
18 | echo "Merging: $fromfile"
19 |
20 | # making new narrow font
21 | ./narrow.pe 90 $fromfile
22 |
23 | # normalizing the new narrow font
24 | ./sfdnormalize.pl $fromfile.narrow
25 |
26 | # merging the new normalized narrow font into the existing Condensed font
27 | mergelist=`./merge.pl $fromfile.narrow.norm $tofile $tofile.merged`
28 |
29 | # normalizing the new merged font
30 | ./sfdnormalize.pl $tofile.merged
31 |
32 | # removing files that aren't needed anymore
33 | if [ -e $fromfile.narrow ]; then
34 | rm $fromfile.narrow
35 | fi
36 | if [ -e $fromfile.narrow.norm ]; then
37 | rm $fromfile.narrow.norm
38 | fi
39 | if [ -e $tofile.merged ]; then
40 | rm $tofile.merged
41 | fi
42 |
43 | # renaming normalized file
44 | if [ -e $tofile.merged.norm ]; then
45 | mv $tofile.merged.norm $tofile.merged
46 | fi
47 |
48 | done
49 |
--------------------------------------------------------------------------------
/scripts/problems.pl:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env perl
2 | use warnings;
3 |
4 | # $Id$
5 |
6 | # possible problems finder
7 | # (c)2004,2005 Stepan Roh (PUBLIC DOMAIN)
8 | # usage: ./problems.pl [-l <0|1|2|3>] sfd_files+
9 |
10 | # detected problems (higher levels contain lower levels):
11 | # level 0:
12 | # monospaced font (with Mono in name) without indication in Panose (and vice-versa)
13 | # glyphs in monospaced face with different width
14 | # not normalized file (looks for WinInfo, DisplaySize, HStem, VStem, Ref, KernsSLIF, different position than encoding,
15 | # unordered glyphs or H or M flag)
16 | # glyphs without width or with negative width
17 | # duplicate glyphs
18 | # combining marks with non-zero width in non-monospaced fonts
19 | # missing point numbers in splines
20 | # level 1 (default):
21 | # colorized glyphs with content
22 | # level 2:
23 | # different set of mapped content glyphs (first SFD file specified on command line is taken as an etalon)
24 | # level 3:
25 | # ligature referencing colorized or missing glyphs
26 | # ligature in colorized glyph (due to bug in FF <20050502 it causes problems on Mac OS X)
27 | # ligature in empty glyph
28 |
29 | sub process_sfd_file($$);
30 |
31 | # glyph name => ( 'dec_enc' => dec_enc, 'hex_enc' => hex_enc )
32 | %glyphs = ();
33 | $glyphs_loaded = 0;
34 | %problems_counter = ();
35 |
36 | sub process_sfd_file($$) {
37 | local ($sfd_file, $max_level) = @_;
38 |
39 | sub problem($@) {
40 | my ($level, $problem, @args) = @_;
41 |
42 | if ($level <= $max_level) {
43 | print $sfd_file, ': [', $level, '] ', $problem, ': ', @args, "\n";
44 | $problems_counter{'['.$level.'] '.$problem}++;
45 | }
46 | }
47 |
48 | sub is_combining($) {
49 | my ($dec_enc) = @_;
50 |
51 | return (($dec_enc >= 0x0300) && ($dec_enc <= 0x036F))
52 | || (($dec_enc >= 0x1DC0) && ($dec_enc <= 0x1DFF))
53 | || (($dec_enc >= 0x20D0) && ($dec_enc <= 0x20FF))
54 | || (($dec_enc >= 0xFE20) && ($dec_enc <= 0xFE2F));
55 | }
56 |
57 | my $curchar = '';
58 | my $hex_enc = '';
59 | my $dec_enc = 0;
60 | my $colorized;
61 | my $flags;
62 | my ($fontname, $panose, $is_mono_name, $is_mono_panose) = ('', '', 0, 0);
63 | my $is_mono = 0;
64 | my $font_width = -1;
65 | my $curwidth = 0;
66 | my $has_ligature = 0;
67 | my $is_empty = 1;
68 | my %content_glyphs = ();
69 | my @ligature_refs = ();
70 | my %all_glyphs = ();
71 | my $prev_enc = -1;
72 | my $in_spline_set = 0;
73 | my $has_splines;
74 | my $has_refs;
75 | my %all_names = ();
76 | open (SFD, $sfd_file) || die "Unable to open $sfd_file : $!\n";
77 | while () {
78 | if (/^StartChar:\s*(\S+)\s*$/) {
79 | $curchar = $1;
80 | if (exists($all_names{$curchar})) {
81 | problem (0, 'duplicate glyph name', $1);
82 | }
83 | $all_names{$curchar} = 1;
84 | $hex_enc = '';
85 | $dec_enc = 0;
86 | $curwidth = -1;
87 | undef $colorized;
88 | undef $flags;
89 | $has_ligature = 0;
90 | @ligature_refs = ();
91 | $is_empty = 1;
92 | $in_spline_set = 0;
93 | $has_splines = 0;
94 | $has_refs = 0;
95 | } elsif (/^Colour:\s*(\S+)\s*/) {
96 | $colorized = $1;
97 | } elsif (/^Flags:\s*(\S+)\s*/) {
98 | $flags = $1;
99 | if ($flags =~ /([MH])/) {
100 | problem (0, 'not normalized: '.$1.' flag', $curchar, ' ', $dec_enc, ($hex_enc ? ' U+'.$hex_enc : ''), ': flags=', $flags);
101 | }
102 | } elsif (/^Encoding:\s*(\d+)\s*((?:-|\d)+)\s*(\d+)\s*$/) {
103 | $dec_enc = $1;
104 | if ($2 > -1) {
105 | $hex_enc = sprintf ('%04x', $2);
106 | }
107 | if ($dec_enc != $3) {
108 | problem (0, 'not normalized: glyph position differs from encoding', $curchar, ' ', $dec_enc, ($hex_enc ? ' U+'.$hex_enc : ''), ': position=', $3);
109 | }
110 | if ($dec_enc <= $prev_enc) {
111 | problem (0, 'not normalized: unordered glyphs', $curchar, ' ', $dec_enc, ($hex_enc ? ' U+'.$hex_enc : ''), ': previous=', $prev_enc);
112 | }
113 | $prev_enc = $dec_enc;
114 | } elsif (/^Width:\s*(\S+)\s*/) {
115 | $curwidth = $1;
116 | } elsif (/^Ligature:\s*\S*\s*\S*\s*\S*\s*(.*?)\s*$/) {
117 | @ligature_refs = split(/\s+/, $1);
118 | $has_ligature = 1;
119 | } elsif (/^SplineSet\s*$/) {
120 | $is_empty = 0;
121 | $in_spline_set = 1;
122 | problem (0, 'mixed content', $curchar, ' ', $dec_enc, ($hex_enc ? ' U+'.$hex_enc : '')) if ($has_refs);
123 | $has_splines = 1;
124 | } elsif (/^EndSplineSet\s*$/) {
125 | $in_spline_set = 0;
126 | } elsif ($in_spline_set && !/.+,.+,.+$/) {
127 | problem (0, 'point numbers missing', $curchar, ' ', $dec_enc, ($hex_enc ? ' U+'.$hex_enc : ''));
128 | } elsif (/^Ref:/) {
129 | $is_empty = 0;
130 | problem (0, 'not normalized: old-style "Ref"', $curchar, ' ', $dec_enc, ($hex_enc ? ' U+'.$hex_enc : ''));
131 | } elsif (/^Refer:\s*\d+\s*\d+\s*\S\s*(-?\d+(?:\.\d*)?(?:e-?\d+)?)\s*(-?\d+(?:\.\d*)?(?:e-?\d+)?)\s*(-?\d+(?:\.\d*)?(?:e-?\d+)?)\s*(-?\d+(?:\.\d*)?(?:e-?\d+)?)\s*-?\d+(?:\.\d*)?(?:e-?\d+)?\s*-?\d+(?:\.\d*)?(?:e-?\d+)?\s*(\d+)/) {
132 | $is_empty = 0;
133 | if (($5 & 0x2) != 0x2) {
134 | problem (0, 'reference is not rounded to grid', $curchar, ' ', $dec_enc, ($hex_enc ? ' U+'.$hex_enc : ''), ' flags=', $5);
135 | }
136 | if (!(($1 == 1) && ($2 == 0) && ($3 == 0) && ($4 == 1))) {
137 | problem (0, 'transformed reference', $curchar, ' ', $dec_enc, ($hex_enc ? ' U+'.$hex_enc : ''));
138 | }
139 | problem (0, 'mixed content', $curchar, ' ', $dec_enc, ($hex_enc ? ' U+'.$hex_enc : '')) if ($has_splines);
140 | $has_refs = 1;
141 | } elsif (/^DisplaySize:/) {
142 | problem (0, 'not normalized: DisplaySize');
143 | } elsif (/^WinInfo:/) {
144 | problem (0, 'not normalized: WinInfo');
145 | } elsif (/^HStem:/) {
146 | problem (0, 'not normalized: HStem');
147 | } elsif (/^VStem:/) {
148 | problem (0, 'not normalized: VStem');
149 | } elsif (/^KernsSLIF:/) {
150 | problem (0, 'not normalized: KernsSLIF');
151 | } elsif (/^EndChar\s*$/) {
152 | if (!defined $colorized && !$is_empty) {
153 | $content_glyphs{$curchar}{'dec_enc'} = $dec_enc;
154 | $content_glyphs{$curchar}{'hex_enc'} = $hex_enc;
155 | @{$content_glyphs{$curchar}{'ligature'}} = @ligature_refs;
156 | # only mapped glyphs
157 | if ($hex_enc) {
158 | if ($glyphs_loaded) {
159 | if (!exists $glyphs{$curchar}) {
160 | problem (2, 'etalon-free glyph', $curchar, ' ', $dec_enc, ($hex_enc ? ' U+'.$hex_enc : ''));
161 | }
162 | } else {
163 | $glyphs{$curchar}{'dec_enc'} = $dec_enc;
164 | $glyphs{$curchar}{'hex_enc'} = $hex_enc;
165 | }
166 | }
167 | }
168 | if (defined $colorized && !$is_empty) {
169 | problem (1, 'colorized content', $curchar, ' ', $dec_enc, ($hex_enc ? ' U+'.$hex_enc : '') , ': color=', $colorized);
170 | }
171 | if (defined $colorized && defined $flags && ($flags =~ /W/)) {
172 | problem (1, 'colorized content', $curchar, ' ', $dec_enc, ($hex_enc ? ' U+'.$hex_enc : '') , ': color=', $colorized, ', flags=', $flags);
173 | }
174 | if (!$is_mono && defined $colorized && ($curwidth != 2048) && ($curwidth != 0)) {
175 | problem (1, 'colorized content', $curchar, ' ', $dec_enc, ($hex_enc ? ' U+'.$hex_enc : '') , ': color=', $colorized, ', width=', $curwidth);
176 | }
177 | if ($curwidth == -1) {
178 | problem (0, 'glyph w/o width', $curchar, ' ', $dec_enc, ($hex_enc ? ' U+'.$hex_enc : ''));
179 | } elsif ($curwidth < 0) {
180 | problem (0, 'negative width', $curchar, ' ', $dec_enc, ($hex_enc ? ' U+'.$hex_enc : ''), ': width=', $curwidth);
181 | } elsif ($is_mono && defined $flags && ($flags =~ /W/)) {
182 | if ($font_width == -1) {
183 | $font_width = $curwidth;
184 | } elsif ($curwidth != $font_width) {
185 | problem (0, 'incorrect width', $curchar, ' ', $dec_enc, ($hex_enc ? ' U+'.$hex_enc : ''), ': font width=', $font_width, ', glyph width=', $curwidth);
186 | }
187 | } elsif (!$is_mono && is_combining($dec_enc) && ($curwidth != 0) && !$is_empty) {
188 | problem (0, 'combining mark with non-zero width', $curchar, ' ', $dec_enc, ($hex_enc ? ' U+'.$hex_enc : ''), ': width=', $curwidth);
189 | }
190 | if (defined $colorized && $has_ligature) {
191 | problem (3, 'colorized ligature', $curchar, ' ', $dec_enc, ($hex_enc ? ' U+'.$hex_enc : ''), ': color=', $colorized);
192 | }
193 | if ($is_empty && $has_ligature) {
194 | problem (3, 'empty ligature', $curchar, ' ', $dec_enc, ($hex_enc ? ' U+'.$hex_enc : ''));
195 | }
196 | if (exists $all_glyphs{$dec_enc}) {
197 | problem (0, 'duplicate', $curchar, ' ', $dec_enc, ($hex_enc ? ' U+'.$hex_enc : ''));
198 | }
199 | $all_glyphs{$dec_enc} = 1;
200 | } elsif (/^FontName:\s*(.*?)\s*$/) {
201 | $fontname = $1;
202 | $is_mono_name = ($fontname =~ /mono/i);
203 | $is_mono = 1 if ($is_mono_name);
204 | } elsif (/^Panose:\s*(.*?)\s*$/) {
205 | $panose = $1;
206 | $is_mono_panose = ((split(/\s+/, $panose))[3] == 9);
207 | $is_mono = 1 if ($is_mono_panose);
208 | }
209 | }
210 | close (SFD);
211 | if ($is_mono_name != $is_mono_panose) {
212 | problem (0, 'mixed monospace', 'font name=', $fontname, ', panose=', $panose);
213 | }
214 | foreach $glyph (sort { $content_glyphs{$a}{'dec_enc'} <=> $content_glyphs{$b}{'dec_enc'} } keys %content_glyphs) {
215 | my $dec_enc = $content_glyphs{$glyph}{'dec_enc'};
216 | my $hex_enc = $content_glyphs{$glyph}{'hex_enc'};
217 | foreach $liga (@{$content_glyphs{$glyph}{'ligature'}}) {
218 | if (!exists ($content_glyphs{$liga})) {
219 | problem (3, 'ligature references colorized or missing glyph', $glyph, ' ', $dec_enc, ($hex_enc ? ' U+'.$hex_enc : ''), ': ligature ref=', $liga);
220 | }
221 | }
222 | }
223 | if ($glyphs_loaded) {
224 | foreach $glyph (sort { $glyphs{$a}{'dec_enc'} <=> $glyphs{$b}{'dec_enc'} } keys %glyphs) {
225 | my $dec_enc = $glyphs{$glyph}{'dec_enc'};
226 | my $hex_enc = $glyphs{$glyph}{'hex_enc'};
227 | if (!exists $content_glyphs{$glyph}) {
228 | problem (2, 'missing glyph', $glyph, ' ', $dec_enc, ($hex_enc ? ' U+'.$hex_enc : ''));
229 | }
230 | }
231 | }
232 | $glyphs_loaded = 1;
233 | }
234 |
235 | if (!@ARGV) {
236 | print STDERR "usage: [-l <0|1|2|3>] sfd_files+\n";
237 | exit 1;
238 | }
239 |
240 | $max_level = 1;
241 | if ($ARGV[0] eq '-l') {
242 | shift @ARGV;
243 | $max_level = shift @ARGV;
244 | }
245 | @sfd_files = @ARGV;
246 |
247 | foreach $sfd_file (@sfd_files) {
248 | process_sfd_file ($sfd_file, $max_level);
249 | }
250 |
251 | foreach $problem (sort keys %problems_counter) {
252 | print $problems_counter{$problem}, ' problems of type "', $problem, '"', "\n";
253 | }
254 |
255 | 1;
256 |
--------------------------------------------------------------------------------
/scripts/samples.sh:
--------------------------------------------------------------------------------
1 | #! /bin/sh
2 |
3 | # Author Євгеній Мещеряков
4 | # This file is in public domain
5 |
6 | set -e
7 |
8 | if [ $# != 2 ]
9 | then
10 | echo "Usage: $0 "
11 | exit 1
12 | fi
13 |
14 | new_dir="$1"
15 | old_dir="$2"
16 | tmp_dir=`mktemp -dt gensamples.XXXXXXXXXX`
17 |
18 | for file in $new_dir/*.ttf
19 | do
20 | base_name=`basename $file .ttf`
21 | fntsample -f $file -o $tmp_dir/$base_name.pdf -d $old_dir/$base_name.ttf -l > $tmp_dir/$base_name.txt
22 | pdfoutline $tmp_dir/$base_name.pdf $tmp_dir/$base_name.txt $base_name.pdf
23 | done
24 |
25 | rm -rf $tmp_dir
26 |
--------------------------------------------------------------------------------
/scripts/sfdnormalize.pl:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env perl
2 | use warnings;
3 |
4 | # $Id$
5 |
6 | # SFD normalizer (discards GUI information from SFD files)
7 | # (c)2004,2005 Stepan Roh (PUBLIC DOMAIN)
8 | # usage: ./sfdnormalize.pl sfd_file(s)
9 | # will create files with suffix .norm
10 |
11 | # changes done:
12 | # WinInfo - discarded
13 | # DisplaySize
14 | # - discarded
15 | # Flags - discarded O (open), H (changed since last hinting - useless for TTF), M (manual hinting - useless for TTF)
16 | # Refer - changed S (selected) to N (not selected)
17 | # Fore, Back, SplineSet, Grid
18 | # - all points have 4 masked out from flags (selected)
19 | # and 256 (do not interpolate)
20 | # HStem, VStem
21 | # - discarded (those are Type1 stems = PS hints)
22 | # recalculate number of characters and positional encoding
23 | # NameList
24 | # - discarded (temporary)
25 | # ModificationTime - discarded
26 | # CreationTime - discarded
27 | # VWidth - discarded (for now)
28 | # TeXData - discarded
29 | # TeX - discarded
30 | # Validated - discarded
31 | # UndoRedoHistory - discarded
32 | # changes making it incompatible with FF older than (approx.) 20050728:
33 | # Ref - renamed to Refer
34 | # KernsSLIF
35 | # - renamed to KernsSLIFO
36 |
37 | # !!! Always review changes done by this utility !!!
38 |
39 | sub process_sfd_file($);
40 |
41 | sub process_sfd_file($) {
42 | my ($sfd_file) = @_;
43 |
44 | my $out = $sfd_file . '.norm';
45 |
46 | open (SFD, $sfd_file) || die "Unable to open $sfd_file : $!\n";
47 | open (OUT, '>'.$out) || die "Unable to open $out : $!\n";
48 |
49 | my $curchar = '';
50 | my %glyphs = ();
51 | my $in_spline_set = 0;
52 | my $max_dec_enc = 0;
53 | my %pos_glyphs_map = ();
54 | my $in_undo_redo = 0;
55 |
56 | while () {
57 | next if (/^(WinInfo|DisplaySize|HStem|VStem|ModificationTime|CreationTime|VWidth|TeX|TeXData|Validated|AltUni2):/);
58 | next if (/^$/);
59 | s,^(NameList:).*$,$1 AGL without afii,;
60 | s,^Ref:,Refer:,;
61 | s,^KernsSLIF:,KernsSLIFO:,;
62 | s,^(Flags:.*?)O(.*)$,$1$2,;
63 | s,^(Flags:.*?)H(.*)$,$1$2,;
64 | s,^(Flags:.*?)M(.*)$,$1$2,;
65 | # remove empty Flags line
66 | next if (/^Flags:\s*$/);
67 | s,^(Refer:.*?)S(.*)$,$1N$2,;
68 | if (/^(Fore|Back|SplineSet|Grid)\s*$/) {
69 | $in_spline_set = 1;
70 | } elsif (/^EndSplineSet\s*$/) {
71 | $in_spline_set = 0;
72 | } elsif ($in_spline_set) {
73 | s/(\s+)(\S+?)(,\S+\s*)$/$1.($2 & ~4 & ~256).$3/e;
74 | s/([\s^])-0(\s)/$1."0".$2/eg
75 | }
76 | #remove UndoRedoHistory block
77 | if (/^UndoRedoHistory/) {
78 | $in_undo_redo = 1; next
79 | } elsif (/^EndUndoRedoHistory/) {
80 | $in_undo_redo = 0; next
81 | }
82 | next if ($in_undo_redo);
83 | if (/^BeginChars:/) {
84 | $in_chars = 1;
85 | } elsif (/^EndChars\s*$/) {
86 | $in_chars = 0;
87 | # adding of 1 to max_dec_enc is strange, but works
88 | # second parameter is set to 0 to avoid merging conflicts (fontforge ignores anyway)
89 | print OUT "BeginChars: ", $max_dec_enc + 1, " 0\n";
90 | foreach $glyph (sort { $glyphs{$a}{'dec_enc'} <=> $glyphs{$b}{'dec_enc'} } keys %glyphs) {
91 | print OUT "StartChar: ", $glyphs{$glyph}{'name'}, "\n";
92 | my $dec_enc = $glyphs{$glyph}{'dec_enc'};
93 | my $mapped_enc = $glyphs{$glyph}{'mapped_enc'};
94 | print OUT "Encoding: ", $dec_enc, " ", $mapped_enc, " ", $dec_enc, "\n";
95 | # recalculate references and kerning pairs
96 | foreach $l (@{$glyphs{$glyph}{'lines'}}) {
97 | $l =~ s/^(Refer:\s*)(\S+)/$1.(exists $pos_glyphs_map{$2} ? $pos_glyphs_map{$2} : (warn "Glyph $glyph ($dec_enc) has reference to unknown glyph position $2\n", $2))/e;
98 | if ($l =~ /^KernsSLIFO:\s*(.*)$/) {
99 | my @nums = split(/\s+/, $1);
100 | if ((scalar (@nums) % 4) != 0) {
101 | warn "Kerning definition in glyph $curchar ($dec_enc) is malformed and won't be remapped\n";
102 | } else {
103 | for (my $i = 0; $i < scalar (@nums); $i += 4) {
104 | my $pos = $nums[$i];
105 | if (exists $pos_glyphs_map{$pos}) {
106 | $nums[$i] = $pos_glyphs_map{$pos};
107 | } else {
108 | warn "Glyph $glyph ($dec_enc) has kerning reference to unknown glyph position $pos\n";
109 | }
110 | }
111 | }
112 | $l = "KernsSLIFO: " . join(' ', @nums) . "\n";
113 | }
114 | print OUT $l;
115 | }
116 | print OUT "EndChar\n";
117 | $pos++;
118 | }
119 | print OUT "EndChars\n";
120 | } elsif (/^StartChar:\s*(\S+)\s*$/) {
121 | my $name = $1;
122 | $curchar = $name;
123 | while (exists $glyphs{$curchar}) {
124 | $curchar .= '#';
125 | }
126 | $glyphs{$curchar}{'name'} = $name;
127 | } elsif (/^Encoding:\s*(\d+)\s*((?:-|\d)+)\s*(\d+)\s*$/) {
128 | $dec_enc = $1;
129 | $max_dec_enc = $dec_enc if ($dec_enc > $max_dec_enc);
130 | $mapped_enc = $2;
131 | $pos = $3;
132 | $glyphs{$curchar}{'dec_enc'} = $dec_enc;
133 | $glyphs{$curchar}{'mapped_enc'} = $mapped_enc;
134 | $glyphs{$curchar}{'pos'} = $pos;
135 | if (exists $pos_glyphs_map{$pos}) {
136 | warn "Glyph $curchar ($dec_enc) has duplicate glyph position $pos - won't be remapped - possible output corruption!\n";
137 | } else {
138 | $pos_glyphs_map{$pos} = $dec_enc;
139 | }
140 | } elsif (/^EndChar\s*$/) {
141 | $curchar = '';
142 | } else {
143 | if (!$in_chars) {
144 | print OUT;
145 | } elsif ($curchar eq '') {
146 | warn "Malformed input file $sfd_file?";
147 | } else {
148 | push (@{$glyphs{$curchar}{'lines'}}, $_);
149 | }
150 | }
151 | }
152 |
153 | close (SFD);
154 | close (OUT);
155 | }
156 |
157 | if (!@ARGV) {
158 | print STDERR "usage: sfd_files+\n";
159 | exit 1;
160 | }
161 |
162 | @sfd_files = @ARGV;
163 |
164 | foreach $sfd_file (@sfd_files) {
165 | process_sfd_file ($sfd_file);
166 | }
167 |
168 | 1;
169 |
--------------------------------------------------------------------------------
/scripts/stats.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # $Id$
4 |
5 | # Brutalist fonts statistics generator
6 | # (c)2005 Stepan Roh (PUBLIC DOMAIN)
7 | # usage: ./stats.sh
8 |
9 | # Motto: "To proof that each task can be coded in very complicated way."
10 |
11 | echo "Version New glyphs*)"
12 | echo "-------- ----------"
13 | versions=`grep 'U+' status.txt \
14 | | tr -s ' ' \
15 | | cut -d' ' -f 3 \
16 | | sed 's,original,0.0,' \
17 | | sed 's,^\(.\)\.\(.\)$,\1.0\2,' \
18 | | sort \
19 | | uniq \
20 | | sed 's,^\(.\).0\(.\)$,\1.\2,' \
21 | | sed 's,0\.0,original,'`
22 | for ver in $versions; do
23 | sver=`echo $ver | sed 's,\.,\\\\.,'`
24 | count=`grep "^U+.*$sver\( \|$\)" status.txt | wc -l`
25 | printf '%-8s %10i\n' $ver $count
26 | done
27 | echo
28 | echo "*) some glyphs may be counted multiple times if they were added to different faces in different versions"
29 |
--------------------------------------------------------------------------------
/scripts/status.pl:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env perl
2 | use warnings;
3 |
4 | # $Id$
5 |
6 | # status.txt file generator
7 | # (c)2004 Stepan Roh (PUBLIC DOMAIN)
8 | # usage: ./status.pl version_tag status_file sfd_files+
9 | # will print new status file on standard output
10 |
11 | sub parse_status_file(\%$);
12 | sub parse_sfd_file(\%$$);
13 | sub print_status_file(\%);
14 | sub parse_versions($);
15 | sub format_versions(\%);
16 | sub normalize_version($);
17 | sub denormalize_version($);
18 | sub detect_full_typefaces_support(@);
19 |
20 | # internal parsed format:
21 | # _ => ( header lines* )
22 | # hexadecimal character encoding => ( 'name' => name, 'versions' => ( version => ( typefaces* ) ) )
23 |
24 | $debug = 0;
25 |
26 | $name_width = 20;
27 |
28 | if ($debug) {
29 | use Data::Dumper;
30 |
31 | $Data::Dumper::Indent = 1;
32 | $Data::Dumper::Sortkeys = 1;
33 | $Data::Dumper::Purity = 1;
34 | }
35 |
36 | %parsed_typefaces = ();
37 |
38 | sub normalize_version($) {
39 | my ($version) = @_;
40 |
41 | if ($version eq 'original') {
42 | $version = '0.0';
43 | }
44 | return join ('_', map { sprintf ("%02s", $_) } split (/\./, $version));
45 | }
46 |
47 | sub denormalize_version($) {
48 | my ($version) = @_;
49 |
50 | $version = join ('.', map { $_ + 0 } split (/_/, $version));
51 | if ($version eq '0.0') {
52 | $version = 'original';
53 | }
54 | return $version;
55 | }
56 |
57 | sub parse_versions($) {
58 | my ($versions) = @_;
59 |
60 | my %ret = ();
61 | while ($versions =~ s,^\s*(\S+)\s*(?:\((.*?)\)|),,) {
62 | my ($version, $typefaces) = ($1, $2);
63 | $version = normalize_version($version);
64 | if ($typefaces) {
65 | my @typefaces = split (/\s*,\s*/, $typefaces);
66 | $ret{$version} = \@typefaces;
67 | } else {
68 | $ret{$version} = [];
69 | }
70 | }
71 | return %ret;
72 | }
73 |
74 | sub parse_status_file(\%$) {
75 | my ($parsed_ref, $status_file) = @_;
76 |
77 | open (STATUS, $status_file) || die "Unable to open $status_file : $!\n";
78 | while () {
79 | if (/^U+/) {
80 | my ($hex_enc, $name, $versions) = ($_ =~ /^U\+(\S+)\s+(\S+)\s+(.*?)\s*$/);
81 | my %versions = parse_versions ($versions);
82 | $$parsed_ref{$hex_enc}{'name'} = $name;
83 | $$parsed_ref{$hex_enc}{'versions'} = \%versions;
84 | } else {
85 | push (@{$$parsed_ref{'_'}}, $_);
86 | }
87 | }
88 | close (STATUS);
89 | }
90 |
91 | sub parse_sfd_file(\%$$) {
92 | my ($parsed_ref, $version_tag, $sfd_file) = @_;
93 |
94 | open (SFD, $sfd_file) || die "Unable to open $sfd_file : $!\n";
95 | my $typeface = '';
96 | my $curchar = '';
97 | my $hex_enc = '';
98 | my $empty = 0;
99 | $version_tag = normalize_version($version_tag);
100 | while () {
101 | if (/^FullName:\s+\S+\s+(.*?)\s*$/) {
102 | # Brutalist is not included in typeface
103 | $typeface = $1;
104 | $parsed_typefaces{$typeface} = 1;
105 | } elsif (/^StartChar:\s*(\S+)\s*$/) {
106 | $curchar = $1;
107 | $hex_enc = '';
108 | $empty = 0;
109 | } elsif (/^Colour:/) {
110 | # XXX this is quick'n'dirty hack to detect non-empty glyphs
111 | $empty = 1;
112 | } elsif (/^Encoding:\s*\d+\s*(\d+)\s*\d+\s*$/) {
113 | $hex_enc = sprintf ('%04x', $1);
114 | } elsif ($hex_enc && !$empty && /^EndChar\s*$/) {
115 | $$parsed_ref{$hex_enc}{'name'} = $curchar;
116 | push (@{$$parsed_ref{$hex_enc}{'versions'}{$version_tag}}, $typeface);
117 | }
118 | }
119 | close (SFD);
120 | }
121 |
122 | sub detect_full_typefaces_support(@) {
123 | my %typefaces = ();
124 | foreach $typeface (@_) {
125 | $typefaces{$typeface} = 1;
126 | }
127 | foreach $typeface (keys %parsed_typefaces) {
128 | return 0 if (!exists $typefaces{$typeface});
129 | }
130 | return 1;
131 | }
132 |
133 | sub format_versions(\%) {
134 | my ($versions_ref) = @_;
135 |
136 | my @ret = ();
137 | my %done_typefaces = ();
138 | foreach $version (sort keys %{$versions_ref}) {
139 | my ($str) = denormalize_version ($version);
140 | my $do_last = 1;
141 | if (@{$$versions_ref{$version}}) {
142 | my @print_typefaces = ();
143 | foreach $typeface (@{$$versions_ref{$version}}) {
144 | if (!exists ($done_typefaces{$typeface})) {
145 | $done_typefaces{$typeface} = 1;
146 | push (@print_typefaces, $typeface);
147 | }
148 | }
149 | next if (!@print_typefaces);
150 | if (!detect_full_typefaces_support(@print_typefaces)) {
151 | $str .= ' (' . join (', ', sort @print_typefaces) . ')';
152 | $do_last = 0;
153 | }
154 | }
155 | push (@ret, $str);
156 | last if ($do_last);
157 | }
158 | return join (' ', @ret);
159 | }
160 |
161 | sub print_status_file(\%) {
162 | my ($parsed_ref) = @_;
163 |
164 | print @{$$parsed_ref{'_'}};
165 | delete $$parsed_ref{'_'};
166 | foreach $hex_enc (sort {hex($a) <=> hex($b)} keys %{$parsed_ref}) {
167 | my ($versions) = format_versions (%{$$parsed_ref{$hex_enc}{'versions'}});
168 | printf ('U+%s %-'.$name_width.'s %s'."\n", $hex_enc, $$parsed_ref{$hex_enc}{'name'}, $versions);
169 | }
170 | }
171 |
172 | if (@ARGV < 3) {
173 | print STDERR "usage: version_tag status_file sfd_files+\n";
174 | exit 1;
175 | }
176 |
177 | $version_tag = shift @ARGV;
178 | $status_file = shift @ARGV;
179 | @sfd_files = @ARGV;
180 |
181 | %parsed = ();
182 |
183 | parse_status_file (%parsed, $status_file);
184 | foreach $sfd_file (@sfd_files) {
185 | parse_sfd_file (%parsed, $version_tag, $sfd_file);
186 | }
187 |
188 | if ($debug) {
189 | print STDERR Data::Dumper->Dump([\%parsed], ['*parsed']);
190 | print STDERR "Parsed typefaces: ", join (', ', @parsed_typefaces), "\n";
191 | }
192 |
193 | print_status_file (%parsed);
194 |
195 | 1;
196 |
--------------------------------------------------------------------------------
/scripts/strip_glyphs.pe:
--------------------------------------------------------------------------------
1 | #! /usr/bin/fontforge -script
2 | #
3 | # $Id$
4 | #
5 | # "glyphs_to_remove" argument can be a single code point
6 | # or a range of codepoints separated by ":"
7 | #
8 | # code points can be specified both as integers and as unicode
9 | # you can also mix the two formats (i.e "48:u54")
10 | #
11 | # example:
12 | #
13 | # "strip_glyphs in.sfd out.sfd 12 u20 100:150 u200:u230 u300:65000"
14 |
15 | if ($argc < 4)
16 | Print( "Usage: strip_glyphs in out glyphs_to_remove..." )
17 | Quit()
18 | endif
19 |
20 |
21 | Open($1); shift
22 | Reencode("unicode")
23 | out = $1; shift
24 |
25 | #
26 | # Loop through the arguments and select the glyphs
27 | # which need to be cleared
28 | #
29 | while ($argc > 1)
30 | len = Strlen($1)
31 | colon_idx = Strstr($1, ":")
32 |
33 | #
34 | # argument is a single glyph
35 | #
36 | if (colon_idx == -1)
37 | if(Strstr($1, "u") == -1)
38 | SelectMore(Strtol($1)) ## Integer
39 | else
40 | SelectMore($1) ## Unicode code point (i.e "u0027")
41 | endif
42 |
43 | #
44 | # argument is a range low:high
45 | #
46 | else
47 | low = Strsub($1, 0, colon_idx)
48 | if(Strstr($1, "u") == -1)
49 | low = Strtol(low) ## Integer
50 | endif
51 |
52 | high = Strsub($1, colon_idx+1, len)
53 | if(Strstr($1, "u") == -1)
54 | high = Strtol(high) ## Integer
55 | endif
56 |
57 | SelectMore(low, high);
58 | endif
59 |
60 | shift
61 | endloop
62 |
63 | Clear()
64 |
65 | Save(out)
66 | Quit()
67 |
--------------------------------------------------------------------------------
/scripts/ttpostproc.pl:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env perl
2 | use warnings;
3 | # $Id$
4 |
5 | # TTF postprocessing
6 | # usage:
7 | # chmod +x ttpostproc.pl
8 | # ./ttpostproc.pl generated/*.ttf
9 |
10 | use Font::TTF::Font;
11 |
12 | sub postproc($) {
13 | my ($file) = @_;
14 | my $backup = $file.'~';
15 |
16 | rename($file, $backup) || die "Unable to rename $f as $backup : $!\n";
17 |
18 | $font = Font::TTF::Font->open($backup) || die "Unable to open font $backup : $!\n";
19 |
20 | # head.flags:
21 | # 1 => baseline for font at y=0
22 | # 2 => left sidebearing point at x=0
23 | # 4 => instructions may depend on point size
24 | # 8 => force ppem to integer values for all internal scaler math
25 | # 0x10 => instructions may alter advance width
26 | $font->{'head'}->{'flags'} = 1 + 2 + 4 + 8 + 0x10;
27 |
28 | # gasp table
29 | # version = 0
30 | # numRanges = 2
31 | # range[0].rangeMaxPPEM = 8
32 | # range[0].rangeGaspBehavior = 2 (GASP_DOGRAY)
33 | # range[1].rangeMaxPPEM = 65535 (max.)
34 | # range[1].rangeGaspBehavior = 3 (GASP_DOGRAY|GASP_GRIDFIT)
35 | $gasp_data = pack('nnnnnn', 0, 2, 8, 2, 0xffff, 3);
36 | $font->{'gasp'} = Font::TTF::Table->new(( 'dat' => $gasp_data ));
37 |
38 | $font->out($file) || die "Unable to write to $file : $!\n";
39 | $font->release();
40 | }
41 |
42 | @files = @ARGV;
43 | foreach $f (@files) {
44 | postproc($f);
45 | }
46 |
47 | 1;
48 |
--------------------------------------------------------------------------------
/scripts/unhinted.pl:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env perl
2 | use warnings;
3 |
4 | # $Id$
5 |
6 | # output (TT-)unhinted glyphs
7 | # (c)2005 Stepan Roh (PUBLIC DOMAIN)
8 | # usage: ./unhinted.pl [-v] [-c] sfd_files+
9 |
10 | sub parse_sfd_file($$$);
11 |
12 | sub parse_sfd_file($$$) {
13 | my ($sfd_file, $verbose, $composites) = @_;
14 |
15 | open (SFD, $sfd_file) || die "Unable to open $sfd_file : $!\n";
16 | print $sfd_file, ': ';
17 | my $typeface = '';
18 | my $curchar = '';
19 | my $hex_enc = '';
20 | my $empty = 0;
21 | my $hinted = 0;
22 | my $total = 0;
23 | my $unhinted = 0;
24 | my $experimental = 0;
25 | my @unhinted = ();
26 | my $contours = 0;
27 | while () {
28 | if (/^FullName:\s+\S+\s+(.*?)\s*$/) {
29 | $typeface = $1;
30 | $experimental = ($typeface =~ /Condensed|(Serif.*Oblique)/);
31 | } elsif (/^StartChar:\s*(\S+)\s*$/) {
32 | $curchar = $1;
33 | $hex_enc = '';
34 | $empty = 0;
35 | $hinted = 0;
36 | $contours = 0;
37 | } elsif (/^TtfInstrs:/) {
38 | $hinted = 1;
39 | } elsif (/^Colour:/) {
40 | # XXX this is quick'n'dirty hack to detect non-empty glyphs
41 | $empty = 1;
42 | } elsif (/^Fore$/) {
43 | $contours = 1;
44 | } elsif (/^Encoding:\s*\d+\s*(\d+)\s*\d+\s*$/) {
45 | $hex_enc = sprintf ('%04X', $1);
46 | } elsif ($hex_enc && !$empty && /^EndChar\s*$/) {
47 | $total++;
48 | if (($composites || $contours) && !$hinted) {
49 | $unhinted++;
50 | push (@unhinted, $curchar . ' (U+' . $hex_enc . ')');
51 | }
52 | }
53 | }
54 | print "[experimental] " if ($experimental);
55 | printf "%.0d%% (%d/%d)", $unhinted / $total * 100, $unhinted, $total;
56 | print "\n";
57 | print ' ', join (', ', @unhinted), "\n" if ($verbose);
58 | close (SFD);
59 | }
60 |
61 | if (@ARGV < 1) {
62 | print STDERR "usage: [-v] [-c] sfd_files+\n";
63 | exit 1;
64 | }
65 |
66 | while ($ARGV[0] =~ /^-/) {
67 | if ($ARGV[0] eq '-v') {
68 | $verbose = 1;
69 | } elsif ($ARGV[0] eq '-c') {
70 | $composites = 1;
71 | } else {
72 | last;
73 | }
74 | shift @ARGV;
75 | }
76 | @sfd_files = @ARGV;
77 |
78 | foreach $sfd_file (@sfd_files) {
79 | parse_sfd_file ($sfd_file, $verbose, $composites);
80 | }
81 |
82 | 1;
83 |
--------------------------------------------------------------------------------
/scripts/unicover.pl:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env perl
2 | use warnings;
3 |
4 | # $Id$
5 |
6 | # unicode coverage analyzator
7 | # (c)2004,2005 Stepan Roh (PUBLIC DOMAIN)
8 | # usage: ./unicover.pl unicode_data_file blocks_file sfd_file1 label1 (sfd_file2 label2...)
9 | # unicode data file can be downloaded from http://www.unicode.org/Public/UNIDATA/UnicodeData.txt
10 | # blocks file can be downloaded from http://www.unicode.org/Public/UNIDATA/Blocks.txt
11 |
12 | sub parse_blocks_file($);
13 | sub parse_unicode_data_file($);
14 | sub parse_sfd_file($);
15 | sub inc_coverage($$);
16 | sub print_coverage();
17 | sub disable_char($);
18 |
19 | $debug = 0;
20 |
21 | if ($debug) {
22 | use Data::Dumper;
23 |
24 | $Data::Dumper::Indent = 1;
25 | $Data::Dumper::Sortkeys = 1;
26 | $Data::Dumper::Purity = 1;
27 | }
28 |
29 | # map (start dec => ( 'name' => block name, 'end' => end dec, 'coverage' => ( sfd_file => coverage ), 'disabled_count' => number of disabled glyphs )
30 | %blocks = ();
31 | %chars = ();
32 |
33 | sub parse_blocks_file($) {
34 | my ($blocks_file) = @_;
35 |
36 | open (F, $blocks_file) || die "Unable to open $blocks_file : $!\n";
37 | while () {
38 | next if (/^\s*(#|$)/);
39 | my ($start, $end, $name) = ($_ =~ /^(.*?)\.\.(.*?);\s*(.*?)\s*$/);
40 | $start = hex ($start);
41 | $end = hex ($end);
42 | $blocks{$start}{'name'} = $name;
43 | $blocks{$start}{'end'} = $end;
44 | }
45 | close (F);
46 | }
47 |
48 | sub disable_char($) {
49 | my ($dec_enc) = @_;
50 |
51 | foreach $block_start (keys %blocks) {
52 | my ($block_end) = $blocks{$block_start}{'end'};
53 | if (($dec_enc >= $block_start) && ($dec_enc <= $block_end)) {
54 | $blocks{$block_start}{'disabled_count'}++;
55 | last;
56 | }
57 | }
58 | }
59 |
60 | sub disable_char_range($$) {
61 | my ($range_start, $range_end) = @_;
62 |
63 | my $cur_enc = $range_start;
64 | while ($cur_enc <= $range_end) {
65 | my $cur_block_start = -1;
66 | foreach $block_start (keys %blocks) {
67 | my ($block_end) = $blocks{$block_start}{'end'};
68 | if (($cur_enc >= $block_start) && ($cur_enc <= $block_end)) {
69 | $cur_block_start = $block_start;
70 | last;
71 | }
72 | }
73 | return if ($cur_block_start == -1);
74 | while (($cur_enc <= $range_end) && ($cur_enc <= $blocks{$cur_block_start}{'end'})) {
75 | $blocks{$cur_block_start}{'disabled_count'}++;
76 | $cur_enc++;
77 | }
78 | }
79 | }
80 |
81 | sub parse_unicode_data_file($) {
82 | my ($ud_file) = @_;
83 |
84 | open (F, $ud_file) || die "Unable to open $ud_file : $!\n";
85 | my $prev_enc = -1;
86 | while () {
87 | next if (/^\s*(#|$)/);
88 | my ($enc, $name) = split (/;/);
89 | $enc = hex ($enc);
90 | if ($prev_enc + 1 < $enc) {
91 | disable_char_range ($prev_enc + 1, $enc - 1);
92 | }
93 | disable_char ($enc) if ($name =~ /^);
94 | $chars{$enc} = 1 if ($name !~ /^);
95 | $prev_enc = $enc;
96 | }
97 | # find last possible character
98 | $last_enc = $prev_enc;
99 | foreach $block_start (keys %blocks) {
100 | my ($block_end) = $blocks{$block_start}{'end'};
101 | $last_enc = $block_end if ($block_end > $last_enc);
102 | }
103 | if ($prev_enc + 1 <= $last_enc) {
104 | disable_char_range ($prev_enc + 1, $last_enc);
105 | }
106 | close (F);
107 | }
108 |
109 | sub inc_coverage($$) {
110 | my ($sfd_file, $dec_enc) = @_;
111 |
112 | foreach $block_start (keys %blocks) {
113 | my ($block_end) = $blocks{$block_start}{'end'};
114 | if (($dec_enc >= $block_start) && ($dec_enc <= $block_end)) {
115 | if (exists $chars{$dec_enc}) {
116 | $blocks{$block_start}{'coverage'}{$sfd_file}++;
117 | }
118 | last;
119 | }
120 | }
121 | }
122 |
123 | sub parse_sfd_file($) {
124 | my ($sfd_file) = @_;
125 |
126 | open (F, $sfd_file) || die "Unable to open $sfd_file : $!\n";
127 | my $curchar = '';
128 | my $curenc = '';
129 | my $empty = 0;
130 | while () {
131 | if (/^StartChar:\s*(\S+)\s*$/) {
132 | $curchar = $1;
133 | $curenc = '';
134 | $empty = 0;
135 | } elsif (/^Colour:/) {
136 | # XXX this is quick'n'dirty hack to detect non-empty glyphs
137 | $empty = 1;
138 | } elsif (/^Encoding:\s*\d+\s*(\d+)\s*\d+\s*$/) {
139 | $curenc = $1;
140 | } elsif ($curenc && !$empty && /^EndChar\s*/) {
141 | inc_coverage ($sfd_file, $curenc);
142 | }
143 | }
144 | close (F);
145 | }
146 |
147 | # TODO: formats would be better
148 | sub print_coverage() {
149 | print < $b } keys %blocks) {
163 | my ($block_end) = $blocks{$block_start}{'end'};
164 | my ($name) = $blocks{$block_start}{'name'};
165 | my ($disabled) = $blocks{$block_start}{'disabled_count'};
166 | $disabled = 0 if (!defined $disabled);
167 | my ($length) = $block_end - $block_start + 1 - $disabled;
168 | printf "U+%04x %-40s", $block_start, $name;
169 | foreach $sfd_file (@sfd_files) {
170 | my ($coverage) = $blocks{$block_start}{'coverage'}{$sfd_file};
171 | $coverage = 0 if (!defined $coverage);
172 | my ($percent) = ($length != 0) ? ($coverage/$length * 100) : 0;
173 | if ($percent > 0) {
174 | printf " %3d%%", $percent;
175 | } else {
176 | print " ";
177 | }
178 | printf " %-13s", "($coverage/$length)";
179 | }
180 | print "\n";
181 | }
182 | }
183 |
184 | if (@ARGV < 3) {
185 | print STDERR "usage: unicode_data_file blocks_file sfd_file1 label1 (sfd_file2 label2...)\n";
186 | exit 1;
187 | }
188 |
189 | $unicode_data_file = shift @ARGV;
190 | $blocks_file = shift @ARGV;
191 | @sfd_files = ();
192 | %sfd_files = ();
193 | while (@ARGV) {
194 | $sfd_file = shift @ARGV;
195 | $label = shift @ARGV;
196 | push (@sfd_files, $sfd_file);
197 | $sfd_files{$sfd_file} = $label;
198 | }
199 |
200 | parse_blocks_file($blocks_file);
201 | parse_unicode_data_file($unicode_data_file);
202 | foreach $sfd_file (@sfd_files) {
203 | parse_sfd_file($sfd_file);
204 | }
205 | print_coverage();
206 |
207 | if ($debug) {
208 | print STDERR Data::Dumper->Dump([\%blocks], ['*blocks']);
209 | }
210 |
211 | 1;
212 |
--------------------------------------------------------------------------------
/scripts/wikidownloadtemplate.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # call this script from the main directory like scripts/wikidownloadtemplate.sh
4 |
5 | # use the output of this script to update http://brutalist.sourceforge.net/wiki/index.php?title=Download
6 |
7 |
8 | # assumes that version in Makefile is correct (which it should be or something was done wrong)
9 | VERSION=`grep "VERSION = " Makefile -m1 | cut -d' ' -f3`
10 |
11 | # directory where to look for the files
12 | DIRECTORY=`grep "DISTDIR = " Makefile -m1 | cut -d' ' -f3`
13 |
14 |
15 | function makeentry
16 | # usage: makeentry filename description
17 | {
18 | file=$DIRECTORY/$1
19 |
20 | if [ -f $file ]; then
21 |
22 | echo "{{SFFile|"
23 |
24 | echo " name= $1"
25 | echo "| version= ${VERSION}"
26 | echo "| size= `wc -c $file | cut -f1 -d' '`"
27 | echo "| desc= $2"
28 | echo "| md5= `md5sum $file | cut -f1 -d' '`"
29 | echo "| sha256= `sha256sum $file | cut -f1 -d' '`"
30 |
31 | echo "}}"
32 |
33 | else
34 |
35 | echo "Error: File '$file' does not exist"
36 |
37 | fi
38 | }
39 |
40 |
41 | makeentry "brutalist-fonts-ttf-${VERSION}.tar.bz2" "TrueType fonts packed as [[Wikipedia:tar.bz2|tar.bz2]] archive"
42 |
43 | makeentry "brutalist-fonts-ttf-${VERSION}.zip" "TrueType fonts packed as [[Wikipedia:ZIP (file format)|zip]] archive"
44 |
45 | makeentry "brutalist-fonts-${VERSION}.tar.bz2" "Fonts in source form (SFD) for [[FontForge]]"
46 |
47 | makeentry "brutalist-lgc-fonts-ttf-${VERSION}.tar.bz2" "Brutalist LGC (Latin, Greek, Cyrillic) TrueType fonts packed as [[Wikipedia:tar.bz2|tar.bz2]] archive"
48 |
49 | makeentry "brutalist-lgc-fonts-ttf-${VERSION}.zip" "Brutalist LGC (Latin, Greek, Cyrillic) TrueType fonts packed as [[Wikipedia:ZIP (file format)|zip]] archive"
50 |
51 | makeentry "brutalist-ttf-${VERSION}.zip" "This package only includes Brutalist.ttf in a [[Wikipedia:ZIP (file format)|zip]] archive"
52 |
53 |
--------------------------------------------------------------------------------