Compare commits

...

16 Commits

Author SHA1 Message Date
Jany Belluz 996150820b
Merge pull request #142 from d125q/master
Make build scripts compatible with Python 3
2022-03-16 08:28:54 +01:00
Jany Belluz 265d15ecae
Merge pull request #150 from brian6932/fixed-broken-link
Fixed broken install link in readme
2022-03-16 08:27:53 +01:00
Brian 3fefa18b29
Fixed broken install link in readme 2022-03-15 19:29:35 -04:00
Jany Belluz 02917e46a1
Merge pull request #141 from VelichkoAlexander/fix/readme-brew-install-command
README.md - Update brew install command
2021-03-28 15:54:51 +01:00
Dario Gjorgjevski 6f32679e61
Make build scripts compatible with Python 3 2021-02-07 19:20:40 +01:00
Alexander Velichko 070726cfce fix brew install command 2020-12-27 11:26:39 +02:00
Jany Belluz a58e874526 Format README 2020-07-04 13:23:46 +01:00
Jany Belluz 5f00c7b95a
Merge pull request #135 from JakeElder/master
Add build Dockerfile
2020-07-04 11:45:05 +01:00
Jany Belluz d1c591af5a
Update README.md 2020-07-04 11:43:46 +01:00
Jany Belluz 514454e9e7
Merge pull request #134 from claymager/fira-logic
Port Fira Code's ligature logic
2020-07-04 11:42:02 +01:00
Jake Elder 290926b2da Add build Dockerfile 2020-07-02 13:23:21 +07:00
John Mager 5c088fcf68 Skip generating ignores for certain ligatures.
- Logic ripped from FiraCode, not currently applicable to Fantasque
 Sans

 - Some potential ligatures ( <*>, <$> ), can be arbitrarily extended
 while still making sense: ( <<<$>>>, etc). Standard 'ignore' rules will
 prevent this from displaying, so we can skip those for certain
 ligatures.
2020-06-21 15:00:57 -04:00
John Mager 7506577e37 Protects some sequences for being ligaturized 2020-06-21 15:00:57 -04:00
John Mager 2508b90ce9 Fixes bug in potentially cyclic ligatures
* fixes issue #133
2020-06-21 15:00:57 -04:00
John Mager bb0e4dc3af Allows hardcoding sequences to ignore ligatures
- Certain operator combinations - especially those involving equals
 signs - look wrong when some of them form ligatures.

 - All current rules from FiraCode were ported over, but many are
 currently irrelevant for this font. Those are commented out of the
 file.

 - Fixes issue #118
2020-06-21 15:00:57 -04:00
John Mager 824cd6a9f8 Added support for longer ligatures
- Breaking out the ligature substitution rules like this also allows for
 easy logical extensions, to handle edge cases as FiraCode does.
2020-06-21 15:00:45 -04:00
4 changed files with 224 additions and 33 deletions

21
Dockerfile 100644
View File

@ -0,0 +1,21 @@
FROM ubuntu:18.04
RUN apt-get update && \
apt-get install software-properties-common -y && \
add-apt-repository ppa:fontforge/fontforge -y && \
apt-get update && \
apt-get install -y --no-install-recommends \
fontforge \
woff-tools \
woff2 \
ttfautohint \
make \
zip
WORKDIR /fantasque
VOLUME /fantasque/Release
COPY . /fantasque
CMD ["make"]

View File

@ -48,7 +48,7 @@ Stylistic set(s)
### `ss01`: nondescript `k`
No ~~distractive~~ lovely loop.
[Get the pre-activated version here](https://github.com/belluzj/fantasque-sans/releases/download/1.8.0/FantasqueSansMono-NoLoopK.zip)
[Get the pre-activated version here](https://github.com/belluzj/fantasque-sans/releases/download/v1.8.0/FantasqueSansMono-NoLoopK.zip)
or see the [issue #67](https://github.com/belluzj/fantasque-sans/issues/67)
for techniques to activate the stylistic set.
@ -77,7 +77,7 @@ useful for users of accented capitals. For more info, see the [CHANGELOG](CHANGE
Automatic installation on macOS with [homebrew](https://brew.sh):
brew tap homebrew/cask-fonts #You only need to do this once for cask-fonts
brew cask install font-fantasque-sans-mono
brew install --cask font-fantasque-sans-mono
Instructions for other platforms might follow.
@ -105,6 +105,15 @@ the latest prebuilt release of these fonts.
`make install` will install the TTF fonts into your local `.fonts/` directory
and update the font cache. It comes in handy while modifying the font.
Alternatively, if you'd like to build Fantasque without installing required
dependencies, a Dockerfile is provided. Run the following command, and the
fonts will be built to the `./Variants` directory.
```sh
docker build -t fantasque .
docker run -v "$(pwd)/Variants:/fantasque/Variants" fantasque
```
[![](Specimen/Specimen.png)](Specimen/Specimen.pdf)
Webfonts

View File

@ -2,7 +2,10 @@
#
# Adapted from https://github.com/tonsky/FiraCode/blob/master/gen_calt.clj
from __future__ import unicode_literals
from textwrap import dedent
from collections import defaultdict
import tempfile
@ -32,7 +35,7 @@ def update_features(font):
# Add the dummy "LIG" glyph
lig = font.createChar(-1, 'LIG')
lig.width = font['space'].width
with tempfile.NamedTemporaryFile(suffix='.fea') as f:
with tempfile.NamedTemporaryFile(mode='w', suffix='.fea') as f:
f.write(fea_code)
f.seek(0)
font.mergeFeature(f.name)
@ -44,36 +47,192 @@ def rule(liga):
[LIG f i] LIG
[ f f i] LIG }
"""
if len(liga) == 2:
return dedent('''\
lookup {0}_{1} {{
ignore sub {0} {0}' {1};
ignore sub {0}' {1} {1};
sub {0}' {1} by LIG;
sub LIG {1}' by {0}_{1}.liga;
}} {0}_{1};
''').format(*liga)
elif len(liga) == 3:
return dedent('''\
lookup {0}_{1}_{2} {{
ignore sub {0} {0}' {1} {2};
ignore sub {0}' {1} {2} {2};
sub {0}' {1} {2} by LIG;
sub LIG {1}' {2} by LIG;
sub LIG LIG {2}' by {0}_{1}_{2}.liga;
}} {0}_{1}_{2};
''').format(*liga)
elif len(liga) == 4:
return dedent('''\
lookup {0}_{1}_{2}_{3} {{
ignore sub {0} {0}' {1} {2} {3};
ignore sub {0}' {1} {2} {3} {3};
sub {0}' {1} {2} {3} by LIG;
sub LIG {1}' {2} {3} by LIG;
sub LIG LIG {2}' {3} by LIG;
sub LIG LIG LIG {3}' by {0}_{1}_{2}_{3}.liga;
}} {0}_{1}_{2}_{3};
''').format(*liga)
rules = []
# standard ignores:
# ignore sub {0} {0}' {1};
# ignore sub {0}' {1} {1};
if tuple(liga) not in skip_ignores:
rules.extend(
[
ignore(prefix=liga[:1], head=liga[0], suffix=liga[1:]),
ignore(head=liga[0], suffix=(liga[1:] + [liga[-1]])),
]
)
# careful with repeats:
# #133 ->->->->, /**/**/**/, etc.
if len(liga) > 2 and liga[0] == liga[-1]:
rules.append(ignore([liga[-2]], liga[0], liga[1:]))
rules.append(ignore(head=liga[0], suffix=(liga[1:] + [liga[1]])))
# Don't cut into `prefix` to complete a ligature.
# i.e. regex `(?=`> is not `(?`=>.
rules.extend(
[
ignore(prefix[:-n], liga[0], liga[1:])
for prefix in ignore_prefixes
for n in range(1, len(liga))
if prefix[-n:] == liga[:n]
]
)
# hardcoded ignores, i.e. `<||>`
rules.extend(ignores[tuple(liga)])
name = "_".join(liga)
# substitution logic
# sub {0}' {1} by LIG;
# sub LIG {1}' by {0}_{1}.liga;
for i in range(len(liga)):
init = _join(["LIG" for lig in liga[:i]])
tail = _join(liga[i + 1 :])
replace = "LIG" if (i + 1 < len(liga)) else (name + ".liga")
rules.append("sub{0} {1}'{2} by {3};".format(init, liga[i], tail, replace))
# put it all together
lines = (
["lookup " + name + " {"] + [" " + r for r in rules] + ["}} {0};".format(name)]
)
return "\n".join(lines)
def _join(items):
return (" " + " ".join(items)) if items else ""
def ignore(prefix=None, head=None, suffix=None):
""" don't substitute `head` if it's surrounded by `prefix` and `suffix` """
assert head
pref = _join(prefix)
rest = _join(suffix)
return "ignore sub{0} {1}'{2};".format(pref, head, rest)
ignores = defaultdict(
list,
{
("slash", "asterisk"): [
"ignore sub slash' asterisk slash;",
"ignore sub asterisk slash' asterisk;",
],
("asterisk", "slash"): [
"ignore sub slash asterisk' slash;",
"ignore sub asterisk' slash asterisk;",
],
# ("asterisk", "asterisk"): [
# "ignore sub slash asterisk' asterisk;",
# "ignore sub asterisk' asterisk slash;",
# ],
# ("asterisk", "asterisk", "asterisk"): [
# "ignore sub slash asterisk' asterisk asterisk;",
# "ignore sub asterisk' asterisk asterisk slash;",
# ],
# <||>
("less", "bar", "bar"): ["ignore sub less' bar bar greater;"],
("bar", "bar", "greater"): ["ignore sub less bar' bar greater;"],
# # :>=
# ("colon", "greater"): ["ignore sub colon' greater equal;"],
# # {|}
# ("braceleft", "bar"): ["ignore sub braceleft' bar braceright;"],
# ("bar", "braceright"): ["ignore sub braceleft bar' braceright;"],
# # [|]
# ("bracketleft", "bar"): ["ignore sub bracketleft' bar bracketright;"],
# ("bar", "bracketright"): ["ignore sub bracketleft bar' bracketright;"],
# # <*>>> <+>>> <$>>>
# ("greater", "greater", "greater"): [
# "ignore sub [asterisk plus dollar] greater' greater greater;"
# ],
# # <<<*> <<<+> <<<$>
# ("less", "less", "less"): ["ignore sub less' less less [asterisk plus dollar];"],
# # =:=
# ("colon", "equal"): ["ignore sub equal colon' equal;"],
# =!=
("exclam", "equal"): ["ignore sub equal exclam' equal;"],
# =!==
("exclam", "equal", "equal"): ["ignore sub equal exclam' equal equal;"],
# =<= <=< <=> <=| <=: <=! <=/
("less", "equal"): [
"ignore sub equal less' equal;",
"ignore sub less' equal [less greater bar colon exclam slash];",
],
# >=<
# =>= >=> >=< >=| >=: >=! >=/
("greater", "equal"): [
"ignore sub equal greater' equal;",
"ignore sub greater' equal [less greater bar colon exclam slash];",
],
# <*>> <+>> <$>>
# >>->> >>=>>
("greater", "greater"): [
# "ignore sub [asterisk plus dollar] greater' greater;",
# "ignore sub [hyphen equal] greater' greater;",
# "ignore sub greater' greater [hyphen equal];",
],
# <<*> <<+> <<$>
# <<-<< <<=<<
("less", "less"): [
# "ignore sub less' less [asterisk plus dollar];",
# "ignore sub [hyphen equal] less' less;",
# "ignore sub less' less [hyphen equal];",
],
# ||-|| ||=||
("bar", "bar"): [
"ignore sub [hyphen equal] bar' bar;",
"ignore sub bar' bar [hyphen equal];",
],
# # <--> >--< |--|
# ("hyphen", "hyphen"): [
# "ignore sub [less greater bar] hyphen' hyphen;",
# "ignore sub hyphen' hyphen [less greater bar];",
# ],
# # <---> >---< |---|
# ("hyphen", "hyphen", "hyphen"):
# "ignore sub [less greater bar] hyphen' hyphen hyphen;",
# "ignore sub hyphen' hyphen hyphen [less greater bar];",
# ],
("equal", "equal"): [ # ==
# "ignore sub bracketleft equal' equal;", # [==
# "ignore sub equal' equal bracketright;",# ==]
"ignore sub equal [colon exclam] equal' equal;", # =:== =!==
# "ignore sub [less greater bar slash] equal' equal;", # <== >== |== /==
# "ignore sub equal' equal [less greater bar slash];", # ==< ==> ==| ==/
"ignore sub equal' equal [colon exclam] equal;", # ==:= ==!=
],
# [===[ ]===]
# [=== ===]
# <===> >===< |===| /===/ =:=== =!=== ===:= ===!=
("equal", "equal", "equal"): [
# "ignore sub bracketleft equal' equal equal;",
# "ignore sub equal' equal equal bracketright;",
"ignore sub equal [colon exclam] equal' equal equal;",
"ignore sub [less greater bar slash] equal' equal equal;",
# "ignore sub equal' equal equal [less greater bar slash];",
"ignore sub equal' equal equal [colon exclam] equal;",
],
# #118 https://
("slash", "slash"): ["ignore sub colon slash' slash;"],
},
)
ignore_prefixes = [
["parenleft", "question", "colon"],
# Regexp lookahead/lookbehind
["parenleft", "question", "equal"],
["parenleft", "question", "less", "equal"],
["parenleft", "question", "exclam"],
["parenleft", "question", "less", "exclam"],
# PHP <?=
["less", "question", "equal"],
]
# DO NOT generate ignores at all
skip_ignores = {
# # <<*>> <<+>> <<$>>
# ("less", "asterisk", "greater"),
# ("less", "plus", "greater"),
# ("less", "dollar", "greater"),
}
def indent(text, prefix):

View File

@ -3,6 +3,8 @@
# LICENSE: MIT
# vim: sts=4 sw=4 ts=4 et
from past.builtins import xrange
import fontforge
from itertools import compress
import os