> 
> So please, don't be shy, show me an example of this format :-)
> 
> SteveT
> 

It's really hard to find some examples!
Seems thy need a better marketing ;)

But there is a German blog with some code snips
http://www.luatex.de/

And find attached a luatex file from 
http://code.google.com/p/mingyue

Peter
% De http://luatex.bluwiki.com/go/Use_a_TrueType_font
% Environnement : max_print_line=2048

% Changes TODO: feature (and script, lang) indexes shouldn't depend on the font.
% Addition TODO: all the other lookups, and script-specific shaping ...

\catcode`\~=11

\def\lua{\directlua0}

\lua{
dofile('l-table.lua')
function message(...)
  n = select('#', ...)
  for i = 1, n do
    texio.write('term and log', tostring(select(i, ...)))
    if i == n
    then texio.write_nl('term and log', '')
    else texio.write('term and log', ' ') end
  end
  if n == 0 then texio.write('term and log', '\string\n') end
end

function message_ln(...)
  n = select('#', ...)
  for i = 1, n do
    texio.write('term and log', tostring(select(i, ...)))
    if i ~= n then texio.write('term and log', ' ') end
  end
end

verbose = 52134

function beverbose(n)
  return node.has_attribute(n, verbose)
end

function beverboser(n)
  return node.has_attribute(n, verbose) and node.has_attribute(n, verbose) >=2
end

function beverbosest(n)
  return node.has_attribute(n, verbose) and node.has_attribute(n, verbose) >=3
end

function table.nentries(t)
  local n = 0
  for _, _ in pairs(t) do n = n + 1 end
  return n
end
}

\attributedef\verbose=52134
\def\beverbose{\verbose=1\relax}% \relax needed because of the digit!
\def\beverboser{\verbose=2\relax}
\def\beverbosest{\verbose=3\relax}
\def\bequiet{\verbose=-1\relax}

\def\dumpnatfont{\lua{
  message(table.serialize(
    fontforge.to_table(fontforge.open(font.fonts[font.current()].filename))
  ))
}}
\def\dumptexfont{\lua{message(table.serialize(font.fonts[font.current()]))}}

\directlua0{
  function loadtruetype(name, size)
    fonttype = nil
    filename = kpse.find_file(name,"opentype fonts")
    if (filename)
    then fonttype = 'opentype'
    else filename = kpse.find_file(name, "truetype fonts")
    end
    if filename and not fonttype then fonttype = 'truetype' end
    if fonttype then
      if (size < 0) then size = (- 655.36) * size end

      ttffont = fontforge.to_table(fontforge.open(filename))
      if ttffont then
	% HACK HACK HACK
	local tweak_size 
        % if name == 'AdobeArabic-Regular' then tweak_size = true end
        if ttffont.units_per_em == 2048 and fonttype == 'opentype'
	then tweak_size = true end
        if tweak_size then size = size / 2.048 end

        f = { }
        f.name = ttffont.fontname
        f.fullname = ttffont.names[1].names.fullname
        f.parameters = { }
        f.designsize = size
        f.size = size
        direction = 0
        f.parameters.slant = 0
        f.parameters.space = size * 0.25
        f.parameters.space_stretch = 0.3 * size
        f.parameters.space_shrink = 0.1 * size
        f.parameters.x_height = 0.4 * size
        f.parameters.quad = 1.0 * size
        f.parameters.extra_space = 0
        f.characters = { }
        local mag = size / ttffont.units_per_em

        local scriptindex = { }
        local langindex = { }
        local featindex = { }

        f.map = ttffont.map
        f.features = { }
        for _, otable in ipairs({ "gsub", "gpos" }) do
          if ttffont[otable] then
            for _, feat in ipairs(ttffont[otable]) do
              if feat.features then
                for _, ft in ipairs(feat.features) do
                  if ft.tag then
                    local tag = ft.tag
                    if not featindex[tag]
                    then featindex[tag] = table.nentries(featindex) + 1 end
                    f.features[tag] = f.features[tag] or { }
                    local f_tag = f.features[tag]
                    f_tag.table = otable
                    local scripts = ft.scripts
                    for _, screcord in ipairs(ft.scripts) do
                      local script = screcord.script
                      if not scriptindex[script] then
                        scriptindex[script] = table.nentries(scriptindex) + 1
                      end
                      f_tag[script] = f_tag[script] or { }
                      for _, lang in ipairs(screcord.langs) do
                        if not langindex[lang] then
                          langindex[lang] = table.nentries(langindex) + 1
                        end
                        f_tag[script][lang] = f_tag[script][lang] or { }
                        f_tag[script][lang].subtables
                          = f_tag[script][lang].subtables or { }
                        local f_st = f_tag[script][lang].subtables
                        for _, st in ipairs(feat.subtables)
                        do table.insert(f_st, st.name) end
                        f_tag[script][lang].flags = feat.flags
                        f_tag[script][lang].name = feat.name
                        f_tag[script][lang].type = feat.type
                      end
                    end
                  end
                end
              end
            end
          end
        end

        local scriptlist = { }
        local langlist = { }
        local featlist = { }

        for tag, index in pairs(scriptindex)
        do scriptlist[index] = tag end

        for tag, index in pairs(langindex)
        do langlist[index] = tag end

        for tag, index in pairs(featindex)
        do featlist[index] = tag end

        f.scriptindex = scriptindex
        f.langindex = langindex
        f.featindex = featindex

        f.scriptlist = scriptlist
        f.langlist = langlist
        f.featlist = featlist

        f.lookups = ttffont.lookups

        names_of_char = { }
        for char, glyph in pairs(ttffont.map.map)
        do
          names_of_char[ttffont.glyphs[glyph].name]
            = ttffont.map.backmap[glyph]
        end

        names_of_glyph = { }
        for char, glyph in pairs(ttffont.map.map)
        do
          names_of_glyph[ttffont.glyphs[glyph].name]
            = glyph
        end

        f.map.names_of_char = names_of_char

        for char, glyph in pairs(ttffont.map.map) do
          glyph_table = ttffont.glyphs[glyph]

          local w = glyph_table.width * mag
          if tweak_size then w = w * 2.048 end
          f.characters[char] = {
            index = glyph,
            width = w,
            name = glyph_table.name,
            lookups = glyph_table.lookups
          }
          if glyph_table.boundingbox[4]
          then f.characters[char].height = glyph_table.boundingbox[4] * mag end
          if glyph_table.boundingbox[2]
          then f.characters[char].depth = -glyph_table.boundingbox[2] * mag end

          if glyph_table.kerns then
            local kerns = { }
            for _, kern in pairs(glyph_table.kerns)
            do kerns[names_of_char[kern.char]] = kern.off * mag end
            f.characters[char].kerns = kerns
          end
        end
        f.filename = filename
        f.type = 'real'
        f.format = fonttype
        f.embedding = "subset"
        f.cidinfo = {
          registry = "Adobe",
          ordering = "Identity",
          supplement = 0,
          version = 1
        }
      end
    else
      f = font.read_tfm(name, size)
    end
  return f
  end
}

\def\loadtruetype{\lua{callback.register('define_font', loadtruetype)}}
\loadtruetype

\lua{
  local cfont = fontforge.to_table(fontforge.open("CronosPro-Regular.otf"))

  function display_otable(ofont, tname)
    local otable = ofont[tname:lower()]
    message(tname, "features")
    for _, feat in ipairs(otable) do
      if not feat.features then feat.features = { } end
      for _, ft in ipairs(feat.features) do
        for _, sc in ipairs(ft.scripts) do
          message_ln(sc.script .. ', ')
          for _, lg in ipairs(sc.langs)
          do message_ln(lg, '; ') end
        end
        message_ln(ft.tag, ':')
      end

      for _, st in ipairs(feat.subtables)
      do message_ln('', st.name) end

      message(',', feat.type)
    end
    message()
  end
}

\lua{
function pad_right(str)
  local l = string.len(str)
  if l == 4 then return str
  else
    for n = l, 3
    do str = str .. " " end
    return str
  end
end

--[===[
Scripts and languages are attribute n. 512 and 513, respectively.
The attribute value is the index in scriptlist and langlist, and depends on the
current font.
--]===]
function setscriptindex(scripttag)
  local scripttag = pad_right(scripttag:lower())
  local currfont = font.fonts[font.current()]
  if currfont.scriptindex then
    if currfont.scriptindex[scripttag]
    then return currfont.scriptindex[scripttag]
    else return -1
    end
  end
end

function setlangindex(langtag)
  local langtag = pad_right(langtag:upper())
  local currfont = font.fonts[font.current()]
  if currfont.langindex then
    if currfont.langindex[langtag]
    then return currfont.langindex[langtag]
    else return -1
    end
  end
end

% function setfeatureindex(feattag)
function setfeatindex(feattag)
  local feattag = pad_right(feattag)
  local currfont = font.fonts[font.current()]
  if currfont.featindex then
    if currfont.featindex[feattag]
    then return currfont.featindex[feattag]
    else return -1
    end
  end
end
}

\attributedef\scripttag=512
\attributedef\langtag=513

\lua{
scripttag = 512 ; langtag = 513 ; feattagstart = 514

% TODO: merge the next three functions into a single one
% Even the four ...

function getscript(n)
  local si = node.has_attribute(n, scripttag)
  if si and si >=0
  then
    local currfont = font.fonts[font.current()]
    if currfont.scriptlist
    then return currfont.scriptlist[si]
    else return
    end
  else return
  end
end

function getlang(n)
  local li = node.has_attribute(n, langtag)
  if li and li >=0
  then
    local currfont = font.fonts[font.current()]
    if currfont.langlist
    then return currfont.langlist[li]
    else return
    end
  else return
  end
end

% Hmm ... added 2008-10-18 PM because # gave error messages
% (attempt to get length of a number value)
function tl(t)
  local n = 0
  for _, _ in ipairs(t) do n = n + 1 end
  return n
end

function getfeats(n)
  local currfont = font.fonts[font.current()]
  if currfont.featlist then
    local fl = { }
    % for fi = 1, tl(currfont.featlist) do
    for fi = 1, #currfont.featlist do
      local fv = node.has_attribute(n, feattagstart + fi)
      if fv and fv > 0
      then table.insert(fl, currfont.featlist[fi])
      end
    end
    return fl
  else return
  end
end

function setlookups(n)
  local currscript = getscript(n)
  local currlang = getlang(n)
  local currfeats = getfeats(n)
  local lookups = find_lookups(currscript, currlang, currfeats)
  return lookups
end

function find_lookups(sname, lname, ftable)
  local cfont = font.fonts[font.current()]
  local feats = cfont.features
  local script = sname or ""
  local lang = lname or "dflt"
  local ftable = ftable or { }
  local lookups = { }
  if feats then
    for _, fname in ipairs(ftable) do
      local feat = feats[fname]
      if feat then
        local fscript = feat[pad_right(script)]
        if fscript then
          local flang = fscript[pad_right(lang)] or fscript.dflt
          if flang then
            local sts = flang.subtables or { }
            for _, st in ipairs(sts)
            do table.insert(lookups, st)
            end
          end
        end
      end
    end
  end
  return lookups
end
}

\lua{
  --[===[
  We operate directly on the node list.
  Knows only about GSUB lookup type 1 for the moment!
  --]===]
  opentype = opentype or { }

  function opentype.mainengine(head)
    local currfont = font.fonts[head.font]
    local names_of_char = currfont.map.names_of_char

    if beverbose(head)
    then message_ln('', unicode.utf8.char(head.char), "font", currfont.filename)
    end
    if beverboser(head)
    then message('', 'subtype', head.subtype, 'xoffset', head.xoffset, 'left', head.left, 'right', head.right)
    elseif beverbose(head) then message()
    end
    % Clearly there must be a much more efficient way than to query the font
    % each time, but stick to that for the moment.
    lookups = setlookups(head)
    for _, ft in ipairs(lookups) do
      --[===[
      Glyph table does not need exist, we need to check for it explicitely.
      --]===]
      if currfont.characters[head.char] then
        local gl = currfont.characters[head.char].lookups
        if gl then
          if beverbose(head)
          then message(table.serialize(gl, "glyph lookups")) end
          local fa = gl[ft]
          if fa then
            for _, f in ipairs(fa) do
              if f.type == 'substitution' then
                head.char = names_of_char[f.specification.variant]
              end
            end
          end
        end
      end
    end
    return head
  end

  function dothingswithglyphs(head, groupcode)
    local glyph_id = node.id("glyph")
    if beverbose(head) then message() end

    for n in node.traverse(head) do
      if beverbose(n) then message_ln(node.type(n.id)) end
      if n.id == glyph_id then
        n = opentype.mainengine(n)
      elseif n.id == node.id('hlist') then
        local nl = n.list
        if nl then
          if nl.id == glyph_id then
            for nlc in node.traverse(nl) do
              if nlc.id == glyph_id then
                nlc = opentype.mainengine(nlc)
              end
            end
            n.list = opentype.mainengine(n.list)
          end
        end
        if beverbose(n) then message() end
      else if beverbose(n) then message() end
      end
    end
    return head
  end

  --[===[
  We can insert kerns readily in the node list, by simply creating a new node.
  --]===]
  function dothingswithglyphs2(head, groupcode)
    local glyph_id = node.id('glyph')
    for n in node.traverse(head) do
      if n.id == glyph_id and n.char == 0x54 then
        message("Found char 'T'")
        local kern = node.new('kern')
        kern.kern = -30000 % -29601.791999999998?
        for f, _ in pairs(pdf)
        do message(f) end
        message()
        head = node.insert_after(head, n, kern)
      end
    end
    return head
  end

  function list_nodes(head)
    local glyph_id = node.id('glyph')
    for n in node.traverse(head) do
      message_ln(node.type(n.id))
      if n.id == glyph_id then
        local currfont = font.fonts[n.font]
        message('', unicode.utf8.char(n.char), currfont.filename)
        message(table.serialize(currfont.characters[n.char], "glyph table"))
      else message() end
    end
    return head
  end
}

\def\listnodes{\lua{callback.register('pre_linebreak_filter', list_nodes)}}

\def\dothingswithglyphs{%
  \lua{callback.register("pre_linebreak_filter", dothingswithglyphs)}
}

\def\donothingwithglyphs{%
  \lua{callback.register("pre_linebreak_filter", nil)}
}

\def\setscript#1{\scripttag=\lua{tex.print(setscriptindex("#1"))}\relax}
\def\unsetscript{\scripttag=-1\relax}

\def\setlang#1{\langtag=\lua{tex.print(setlangindex("#1"))}\relax}
\def\unsetlang{\langtag=-1\relax}

% TODO: enable setting list (comma-separated, whatever).
% ... and unsetting for all the tags!
\def\setfeat#1{%
  \attribute\lua{
    local fi = setfeatindex("#1")
    if fi < 0 then fi = 0 end
    tex.print(feattagstart + fi)}=1\relax}

\def\unsetfeat#1{%
  \attribute\lua{
    local fi = setfeatindex("#1")
    if fi < 0 then fi = 0 end
    tex.print(feattagstart + fi)}=-1\relax}

\font\cronos=CronosPro-Regular\cronos

REUTENAUER

To

1729

And now, for something completely different:

% Setting script, language, and two OpenType features
\setscript{latn}
\setlang{CRT}
\setfeat{sups}
\setfeat{sinf}
% Starting the layout engine
\dothingswithglyphs

EN TOUTES CAPITALES {\setfeat{c2sc} ET EN PETITES CAPITALES :-)}

ET DE NOUVEAU EN (GRANDES) CAPITALES.

% Now, toggle c2sc for good.
\setfeat{c2sc}

To

REUTENAUER

Gábor BELLA

Ŗ Ř 1729

% Some more tests.
\lua{
  --[===[
  The table returned by FontForge, when reading an OpenType font,
  contains a 'kerns' entry which is the exact same as the one associated with
  the 'kern' feature in the 'gpos' entry.
  Here the relevant entries for CronosPro-Regular.
  --]===]
  local tfont = font.fonts[font.current()]
  local ofont = fontforge.to_table(fontforge.open(tfont.filename))
  local kerns_feat = ofont.gpos[2].subtables[2].kernclass
  local kerns_table = ofont.kerns
}

HELLO WORLD

RE-HELLO WORLD

\unsetfeat{c2sc}
HW

% Unset c2sc, which had just been unset.  Even without an explicit lang, it
% works, since Cronos has a default language.
\setfeat{c2sc}
\unsetlang
HW

% On the other hand, if no script is explicitely set, the layout engine cannot
% find any lookup.
\unsetscript
HW

% Thus, set the script back, and also add smcp in order to have all-small caps.
\setscript{LATN}
\unsetfeat{sups}
% Feature sinf makes punctuation rather horrible :-)
\setfeat{smcp}

\input knuth

% TODO: strange space at the end of the hbox (glyph 357223?)
T\kern-.1667em\lower.5ex\hbox{EEEEEE}\kern-.125emX

\unsetfeat{c2sc}
\unsetfeat{smcp}
\unsetfeat{sinf}
1{\setfeat{sups}st} experiments with {\setfeat{smcp}OpenType}!

% TODO: unset everything

M{\setfeat{sups}r} le Président, je vous fais une lettre, etc.

\font\lib=LinLibertine_Re-2.8.14.otf

{\lib
1{\setfeat{sups}str} experiments with {\setfeat{smcp}OpenType}!

% TODO: unset everything

% What happens below is very strange ...
% Language is correctly unset (no FRA language in the font),
% and things work better now that \set* and \unset* are followed by \relax,
% But the sups behaviour is just too strange ...

{\setlang{FRA}
M{\setfeat{sups}\relax r} le Président, je vous fais une lettre, etc.

1{\setfeat{sups}str} experiments with {\setfeat{smcp}OpenType}!
}
}

% \beverbose
3{\setfeat{zero}0}
\setfeat{zero}
30 aoeusneoatuaeo

% \listnodes

\font\doulos=DoulosSILR\doulos

Hello, world!

{\setfeat{smcp} Hello world}
% \dumpnatfont
\font\geeza={Geeza Pro}\geeza

ABCDE 覺Թى!

\font\thonburi=Thonburi\thonburi

℞

\font\fpl=FPLNeu-Italic\fpl

thdih

ABCDE 覺Թى!

% \beverboser

\font\trebuchet=trebuc\trebuchet

thdih

ABCDE 覺Թى!

\font\lm=lmroman12-regular\lm

Hello, Latin Modern! 42 {\setfeat{onum} 1729}

\setlang{arab} % ARGH! ARGH! ARGH!

% \donothingwithglyphs

\font\adobearabic=AdobeArabic-Regular\adobearabic

{\textdir TRT
  {\setfeat{init}ع}%
  {\setfeat{fina}ر}{\setfeat{init}ب}{\setfeat{medi}ي}%
  {\setfeat{fina}ة}%
}

ع

\font\myriad=MyriadPro-Regular\myriad

Myriad

\font\arabtype=arabtype\arabtype

{\textdir TRT
{\setfeat{init}ع}%
%\setfeat{medi}ربي%
{\setfeat{fina}ر}{\setfeat{init}ب}{\setfeat{medi}ي}%
{\setfeat{fina}ة}%
}

\font\minion=MinionPro-Capt\minion

\unsetfeat{sups}
Minion

\bye

Reply via email to