8

I would like to use OpenType Vollkorn with xetex but its "1" causes me problems. It has an alternate glyph (whose glyph name is "one.ss01"), which I can trigger via StylisticSet={1}. But this also changes other characters:

otfinfo -g Vollkorn-Regular.otf | grep "ss01"

J.ss01
Jcircumflex.ss01
Q.ss01
uni1E9E.ss01
a.ss01
aacute.ss01
abreve.ss01
acircumflex.ss01
adieresis.ss01
agrave.ss01
amacron.ss01
aogonek.ss01
aring.ss01
atilde.ss01
g.ss01
gbreve.ss01
gcircumflex.ss01
gcommaaccent.ss01
gdotaccent.ss01
one.ss01
three.ss01
zero.lf.ss01
three.lf.ss01
seven.lf.ss01
zero.tf.ss01
three.tf.ss01
seven.tf.ss01
slash.ss01

I don't want the alternate "a". I could use locally \XeTeXglyph as in this example:

\documentclass{article}

\usepackage{fontspec}

% http://vollkorn-typeface.com/

\setmainfont{Vollkorn}[%StylisticSet={1},
ItalicFont={* Italic}, BoldFont={* Bold}, BoldItalicFont={* Bold Italic}]

% \def\VollkornFamilyName{Vollkorn(0)}
% utiliser \XeTeXglyph 381 en testant \f@family ?
% non je ne vais pas rendre le 1 actif !
% et c'est \XeTeXglyph 309 en italic

\begin{document}

1234567890

\bfseries 1234567890

\itshape 1234567890

\mdseries 1234567890

\normalfont

\XeTeXglyph 381 234567890

\bfseries \XeTeXglyph 381 234567890

% need to change glyph number

\itshape \XeTeXglyph 309 234567890

\mdseries \XeTeXglyph 309 234567890

\end{document}

% Local Variables:
% TeX-engine: xetex
% End:

vollkorn digits

But I need to apply this on a document which is generated from other sources not manually typed and it seems somewhat dangerous to make the 1 active, with the idea that it would test for the font family and font shapes,etc... and insert the suitable glyph via \XeTeXglyph or just \string1.

as expected making the 1 active had many issues, among them that \XeTeXglyph can not be used in math mode, or in a \csname -- just not an option

Is there a solution for using one.ss01 glyph without activating the full StylisticSet ?

I checked the fontspec manual and discovered the notion of CharacterVariant but there is no such features in that OpenType font:

Fonts$ otfinfo -f Vollkorn-Regular.otf 
aalt    Access All Alternates
calt    Contextual Alternates
case    Case-Sensitive Forms
cpsp    Capital Spacing
dlig    Discretionary Ligatures
dnom    Denominators
frac    Fractions
kern    Kerning
liga    Standard Ligatures
lnum    Lining Figures
mark    Mark Positioning
numr    Numerators
onum    Oldstyle Figures
ordn    Ordinals
pnum    Proportional Figures
salt    Stylistic Alternates
ss01    Stylistic Set 1
sups    Superscript
tnum    Tabular Figures
  • 1
    Unless you use LuaLaTeX and add custom Opentype features using "FeatureFile", I don't think this is possible. – Will Robertson Dec 14 '15 at 11:49
  • @WillRobertson is there some free tool which I could use to modify myself the OpenType font in order to add a custom ss02 stylistic set which switches only the "one" glyph ? or add a "CharacterVariant" for the "one" glyph which would be "one.ss01" ? – user4686 Dec 14 '15 at 12:09
  • FontForge is an open source font editor. I don't know how easy/difficult will be to do this, but I guess it can do it. – TeXtnik Dec 14 '15 at 12:19
  • Imho it should be possible to map the glyph with a mapping file. Search for "teckit". – Ulrike Fischer Dec 14 '15 at 13:18
  • @WillRobertson I have added an answer for LuaLaTeX as you suggested. Hoping for some XeLaTeX way though. – user4686 Dec 14 '15 at 14:22
6

The following Python script for FontForge creates

#!/usr/bin/env python2
import fontforge
import os.path

font_files =  [
    'Vollkorn-Bold.otf',
    'Vollkorn-BoldItalic.otf',
    'Vollkorn-Italic.otf',
    'Vollkorn-Regular.otf',
    ]

def main():
    for font_file_name in font_files:        
        make_new_font(font_file_name)

def get_file_name_stem(font_file_name):
    name_without_directory = os.path.basename(font_file_name)
    name_stem = os.path.splitext(name_without_directory)[0]
    return name_stem

def make_new_font(font_file_name):
    name_stem = get_file_name_stem(font_file_name)
    feature_file_name = name_stem + '.fea'
    new_font_file_name = name_stem + '-ss02.otf'

    print('Font file: ' + font_file_name)
    font = fontforge.open(font_file_name)
    print('Font name: ' + font.fontname)
    print('=> ' + feature_file_name)
    font.generateFeatureFile(feature_file_name)

    print('Lookup names:')
    for lookup in font.gsub_lookups:
        print('  ' + lookup)

    lookup_ss01_name = "'ss01' Style Set 1 lookup 24"
    lookup_ss02_name = "'ss02' Style Set 2 lookup 25"
    subtable_ss02_name = "'ss02' Style Set 2 lookup 25 subtable"

    (type_name,
     flags,
     feature_script_lang_tuples) = font.getLookupInfo(lookup_ss01_name)

    print('Lookup(' + lookup_ss01_name + '): ')
    print('  Type: ' + type_name)
    print('  Flags: ' + ', '.join(flags))
    print('  Feature/Script/Languages: ')
    script_lang_ss01 = None
    for (feature, script_lang) in feature_script_lang_tuples:
        print('    ' + feature + ':')
        for (script, languages) in script_lang:
            language_list = ', '.join([l.rstrip() for l in languages])
            print('      ' + script + ': ' + language_list)
        if feature == 'ss01':
            script_lang_ss01 = script_lang
    if script_lang_ss01 is None:
        raise('Feature ss01 not found in Style Set 1')
    print('  Subtables:')
    for subtable in font.getLookupSubtables(lookup_ss01_name):
        print('    ' + subtable)

    font.addLookup(lookup_ss02_name,
                   type_name,
                   flags,
                   (('ss02', script_lang_ss01),),
                   lookup_ss01_name)
    font.addLookupSubtable(lookup_ss02_name, subtable_ss02_name)

    glyph_one = [glyph for glyph in font.selection.select('one').byGlyphs][0]
    glyph_one.addPosSub(subtable_ss02_name, 'one.ss01')

    print('=> ' + new_font_file_name)
    font.generate(new_font_file_name)

if __name__ == '__main__':
    main()

It generates files Vollkorn-Regular-ss02.otf, ... with an additional stylistic set 02 for the replacement of glyph one with one.ss01 and which can be activated:

\documentclass{article}

\usepackage{fontspec}

\setmainfont{Vollkorn-Regular-ss02.otf}[
  StylisticSet=2,
  ItalicFont=Vollkorn-Italic-ss02.otf,
  BoldFont=Vollkorn-Bold-ss02.otf,
  BoldItalicFont=Vollkorn-BoldItalic-ss02.otf,
]

\begin{document}

1234567890

\bfseries 1234567890

\itshape 1234567890

\mdseries 1234567890

\end{document}

Result with XeLaTeX:

Result

  • Good to have a solution also for XeLaTeX ! – user4686 Apr 3 '16 at 14:58
4

I am posting here the approach with lualatex which was alluded to by @WillRobertson in his comment

I created in my working repertory file testvollkornlua.fff with contents (I am not sure the first two lines are needed, and the stuff with Kerning was for testing purposes).

languagesystem DFLT dflt;
languagesystem latn dflt;

feature oneb {
  sub one by one.ss01;
} oneb;

# Kerning for testing only
feature kern {
pos \A \Y +5000;
pos \a \y +10000;
} kern;

Then with the following source :

\documentclass{article}

\usepackage{fontspec}

\setmainfont{Vollkorn}[%
    FeatureFile={\jobname.fff},
    RawFeature={+oneb},
    UprightFont={* -Regular},
    ItalicFont={* -Italic}, 
    BoldFont={* -Bold}, 
    BoldItalicFont={* -BoldItalic},
    Extension={.otf}]

\begin{document}

1234567890

\bfseries 1234567890

\itshape 1234567890

\mdseries 1234567890

AY

ay

\end{document}

% Local Variables:
% TeX-engine: luatex
% End:

I obtain the desired output (the last two lines were there to check initially if the feature file was found in case I had badly coded feature "oneb"; I indeed had a syntax error (missing "oneb;") and as a result nothing was functional, thus it did not help, I didn't know if the feature file had been found or not until I had fixed it.

enter image description here

Still hoping for a xelatex solution.

  • Putting font feature file in an absolute location does not seem to work: FeatureFile={/Users/XXXXXXX/Library/Fonts/vollkornlua.fff} fails. – user4686 Dec 14 '15 at 15:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy