\documentclass{article}
\usepackage{fontspec,filecontents}
\begin{filecontents*}{junicode.fea}
languagesystem DFLT dflt;
languagesystem latn dflt;
feature calt {
sub \beta by \uni03D0;
} calt;
\end{filecontents*}
\setmainfont[FeatureFile={junicode.fea}]{junicode}
\begin{document}
βιβλίον\par
\addfontfeature{Contextuals=Alternate}
βιβλίον
\end{document}
生产
是否可以改变替换,以便只在单词内部使用 slim beta,而将普通 beta 保留在单词开头?即,以便我们得到以下内容?
答案1
这可以通过上下文功能实现。该类@greekletter
定义所有希腊字母(示例仍需完成),然后希腊字母后的 beta 被其细长形式替换:
\documentclass{article}
\usepackage{fontspec,filecontents}
\begin{filecontents*}{junicode.fea}
languagesystem DFLT dflt;
languagesystem latn dflt;
# The list of Greek letters is now almost complete (some ligatures missing).
@greekletter = [
Alpha Alphatonos Beta Chi Delta Epsilon Epsilontonos Eta Etatonos
Gamma Iota Iotadieresis Iotatonos Kappa Lambda Mu Nu Omega
Omegatonos Omicron Omicrontonos Phi Pi Psi Rho Sigma Tau Theta
Upsilon Upsilondieresis Upsilontonos Xi Zeta alpha alphatonos beta
chi delta epsilon epsilontonos eta etatonos gamma iota iotadieresis
iotadieresistonos iotatonos kappa lambda mu nu omega omega1
omegatonos omicron omicrontonos phi phi1 pi psi rho sigma sigma1 tau
theta theta1 uni03D0 uni03D7-uni03D9 uni03DA-uni03DD uni03E0 uni03E1
uni03F0 uni03F1 uni1F00-uni1F09 uni1F0A-uni1F0F uni1F10-uni1F15
uni1F18 uni1F19 uni1F1A-uni1F1D uni1F20-uni1F29 uni1F2A-uni1F2F
uni1F30-uni1F39 uni1F3A-uni1F3F uni1F40-uni1F45 uni1F48 uni1F49
uni1F4A-uni1F4D uni1F50-uni1F57 uni1F59 uni1F5B uni1F5D uni1F5F
uni1F60-uni1F69 uni1F6A-uni1F6F uni1F70-uni1F79 uni1F7A-uni1F7D
uni1F80-uni1F89 uni1F8A-uni1F8F uni1F90-uni1F99 uni1F9A-uni1F9F
uni1FA0-uni1FA9 uni1FAA-uni1FAF uni1FB0-uni1FB4 uni1FB6-uni1FB9
uni1FBA-uni1FBC uni1FBE uni1FC2-uni1FC4 uni1FC6-uni1FC9
uni1FCA-uni1FCC uni1FD0-uni1FD3 uni1FD6-uni1FD9 uni1FDA uni1FDB
uni1FE0-uni1FE9 uni1FEA-uni1FEC uni1FF2-uni1FF4 uni1FF6-uni1FF9
uni1FFA-uni1FFC upsilon upsilondieresis upsilondieresistonos
upsilontonos xi zeta
];
feature calt {
sub @greekletter beta' by uni03D0;
} calt;
\end{filecontents*}
\setmainfont[FeatureFile={junicode.fea}]{junicode}
\begin{document}
βιβλίον\par
\addfontfeature{Contextuals=Alternate}
βιβλίον\par
βιβββλίον\par
Αβ
\end{document}
字体的希腊字母列表Junicode.ttf
可以通过编程生成:
Unicode 为希腊字母定义了两个块(
Blocks.txt
:0370..03FF; Greek and Coptic 1F00..1FFF; Greek Extended
此外,Unicode 还定义了代码点的类别,即大写字母和小写字母,例如:Ω 有
Lu
大写字母。我们只需要第一个字母即可将其分类为字母。FontForge 可以访问字形名称及其 Unicode 代码点,并且可以通过其 Python 绑定进行脚本编写。
以下 Python 脚本首先从 Unicode 标准中收集希腊字母。然后它调用 FontForge 并尝试找到这些字母的字形名称。
排序后的字形名称将被格式化并打印出来,以便包含在特征文件中。格式化是通过根据特征文件的规则将连续的字形名称折叠到范围来优化的(请参阅“2.gi 范围”一节)。OpenType 特征文件规范)。
#!/usr/bin/env python2
import fontforge
import re
import subprocess
import unicodedata
# Unicode ranges for Greek letters
greek_unicode_range = range(0x370, 0x3ff) + range(0x1f00, 0x1fff)
# Filter the unicode ranges for letters
greek_unicode_letters = []
for slot in greek_unicode_range:
category = unicodedata.category(unichr(slot))
if category[0] == 'L':
greek_unicode_letters.append(slot)
# Get font file
font_name = subprocess.check_output(['kpsewhich', 'Junicode.ttf']).rstrip()
# print('Font: ' + font_name)
# Load font in fontforge
font = fontforge.open(font_name)
# Get all glyphs from the font
glyphs = [glyph
for glyph in font.selection.byGlyphs.all()
if glyph.isWorthOutputting]
glyph_names = [glyph.glyphname
for glyph in glyphs
if glyph.unicode in greek_unicode_letters]
# Unformatted output:
# glyph_names.sort()
# print(' '.join(glyph_names))
# Formatted output with ranges
def is_next_in_line(a, b):
test_string = a + '-' + b
match = is_next_in_line.letter_pattern.match(test_string)
if match:
a_num = ord(match.group(2))
b_num = ord(match.group(4))
signature = match.group(1) + '-' + match.group(3)
if a_num + 1 == b_num:
return (True, signature)
match = is_next_in_line.number_pattern.match(test_string)
if match:
a_num = int(match.group(2))
b_num = int(match.group(4))
signature = match.group(1) + '-' + match.group(3)
if a_num + 1 == b_num:
return (True, signature)
return (False, None)
is_next_in_line.letter_pattern = re.compile(
r'(.*)([A-Za-z])(.*)-\1([A-Za-z])\3$')
is_next_in_line.number_pattern = re.compile(
r'(.*)([0-9]{1,3})(.*)-\1([0-9]{1,3})\3$')
def make_range(names):
if len(names) <= 2:
return names
return [names[0] + '-' + names[-1]]
def compact_names_with_same_length(names):
result = []
tmp = []
tmp_signature = None
for name in names:
if len(tmp) == 0:
tmp.append(name)
continue
(is_consecutive, signature) = is_next_in_line(tmp[-1], name)
if is_consecutive:
if tmp_signature is None:
tmp_signature = signature
tmp.append(name)
continue
if tmp_signature == signature:
tmp.append(name)
continue
result += make_range(tmp)
tmp = [name]
tmp_signature = None
result += make_range(tmp)
return result
def compact_names(names):
"""Replaces three and more consecutive elements by ranges."""
names.sort()
result = []
current_length = 0
tmp = []
for name in names:
if len(tmp) == 0:
tmp.append(name)
current_length = len(name)
continue
length = len(name)
if length == current_length:
tmp.append(name)
continue
result += compact_names_with_same_length(tmp)
tmp = [name]
current_length = length
result += compact_names_with_same_length(tmp)
return result
names = compact_names(glyph_names)
lines = []
line = ''
max_length = 68
for name in names:
if len(line) == 0:
line = name
continue
if len(line) + 1 + len(name) <= max_length:
line += ' ' + name
continue
lines.append(line)
line = name
lines.append(line)
print('@greekletter = [')
for line in lines:
print(' ' + line)
print('];')
结果:
@greekletter = [
Alpha Alphatonos Beta Chi Delta Epsilon Epsilontonos Eta Etatonos
Gamma Iota Iotadieresis Iotatonos Kappa Lambda Mu Nu Omega
Omegatonos Omicron Omicrontonos Phi Pi Psi Rho Sigma Tau Theta
Upsilon Upsilondieresis Upsilontonos Xi Zeta alpha alphatonos beta
chi delta epsilon epsilontonos eta etatonos gamma iota iotadieresis
iotadieresistonos iotatonos kappa lambda mu nu omega omega1
omegatonos omicron omicrontonos phi phi1 pi psi rho sigma sigma1 tau
theta theta1 uni03D0 uni03D7-uni03D9 uni03DA-uni03DD uni03E0 uni03E1
uni03F0 uni03F1 uni1F00-uni1F09 uni1F0A-uni1F0F uni1F10-uni1F15
uni1F18 uni1F19 uni1F1A-uni1F1D uni1F20-uni1F29 uni1F2A-uni1F2F
uni1F30-uni1F39 uni1F3A-uni1F3F uni1F40-uni1F45 uni1F48 uni1F49
uni1F4A-uni1F4D uni1F50-uni1F57 uni1F59 uni1F5B uni1F5D uni1F5F
uni1F60-uni1F69 uni1F6A-uni1F6F uni1F70-uni1F79 uni1F7A-uni1F7D
uni1F80-uni1F89 uni1F8A-uni1F8F uni1F90-uni1F99 uni1F9A-uni1F9F
uni1FA0-uni1FA9 uni1FAA-uni1FAF uni1FB0-uni1FB4 uni1FB6-uni1FB9
uni1FBA-uni1FBC uni1FBE uni1FC2-uni1FC4 uni1FC6-uni1FC9
uni1FCA-uni1FCC uni1FD0-uni1FD3 uni1FD6-uni1FD9 uni1FDA uni1FDB
uni1FE0-uni1FE9 uni1FEA-uni1FEC uni1FF2-uni1FF4 uni1FF6-uni1FF9
uni1FFA-uni1FFC upsilon upsilondieresis upsilondieresistonos
upsilontonos xi zeta
];