> Attached is an improved version.

As usual: right after sending an improved version I find a problem :-)

The old version only checked the first lookup of a 'mark' feature;
this is now fixed.


    Werner
% version 2025-Sep-16

\documentclass{article}

\usepackage{luacode}
\usepackage{fontspec}

\begin{luacode*}
  local report = luaotfload.log.report

  local function glyph_to_unicode(glyph, unicodes, fontname)
    local unicode = unicodes[glyph]
    if not unicode then
      report("both", 0, "add_to_mark",
             "error: there is no glyph '%s' in font '%s'",
             glyph, fontname)
    end
    return unicode
  end

  local function missing_base_glyph(base_glyph, fontname)
    report("both", 0, "add_to_mark",
           "error: base glyph '%s' not in 'mark' feature of font '%s'",
           base_glyph, fontname)
  end

  local function missing_mark_glyph(mark_glyph, fontname)
    report("both", 0, "add_to_mark",
           "error: mark glyph '%s' not in 'mark' feature of font '%s'",
           mark_glyph, fontname)
  end

  local patch_functions = {}

  -- Add glyph with name ACC as a diacritic to an existing 'mark'
  -- feature of font file FONT.  The arguments BASE and MARK specify
  -- names of glyphs that represent the desired anchor class that
  -- connects base and mark glyphs, respectively, to which ACC should
  -- be added.  The coordinates X and Y give the position of the
  -- anchor for ACC.
  --
  -- FONT should be the base name of an OpenType font, i.e., a file
  -- name without a path (example: `foo.otf`).
  --
  -- BASE and MARK must exist in FONT, and there must be an entry in
  -- the 'mark' feature that pairs them.  ACC must exist in FONT, too.
  --
  -- This function can be used repeatedly.  Note, however, that a mark
  -- glyph can only be part of a single anchor class.  As a
  -- consequence, a second call to this function with the same
  -- argument ACC (for a particular font) that results in a different
  -- anchor class overrides the result of the first call.
  function add_to_mark_feature(font, acc, base, mark, x, y)
    if not patch_functions[font] then
      patch_functions[font] = {}
    end

    local function patch_function(fontdata)
      local path = fontdata.specification.filename
      local filename = file.basename(path)

      local unicodes = fontdata.resources.unicodes
      if not unicodes then
        report("both", 0, "add_to_mark",
               "error: 'unicodes' subtable missing;"
               .. " cannot map glyph names to Unicode")
        return
      end

      local uni_acc = glyph_to_unicode(acc, unicodes, filename)
      local uni_base = glyph_to_unicode(base, unicodes, filename)
      local uni_mark = glyph_to_unicode(mark, unicodes, filename)
      if not (uni_acc and uni_base and uni_mark) then
        return
      end

      local have_mark_feature = false
      local base_glyph_in_coverage = false
      local mark_glyph_in_coverage = false

      for _, sequence in ipairs(fontdata.resources.sequences) do
        if (sequence.type == "gpos_mark2base"
            and sequence.features["mark"]) then
          have_mark_feature = true
          for _, step in ipairs(sequence.steps) do
            local coverage = step.coverage
            if coverage then
              local coverage_mark = coverage[uni_mark]
              if coverage_mark then
                mark_glyph_in_coverage = true
                local coverage_base = coverage_mark[1][uni_base]
                if coverage_base then
                  base_glyph_in_coverage = true
                  for i, baseclass in ipairs(step.baseclasses) do
                    if baseclass[uni_base] then
                      report("log", 0, "add_to_mark",
                             "found base-mark glyph combination '%s+%s'"
                             .. " in 'mark' feature of font '%s'",
                             base, mark, filename)
                      -- Report OpenType value for anchor class, not
                      -- the one used in luatex.
                      report("log", 0, "add_to_mark",
                             "adding glyph '%s' to anchor class %d",
                             acc, i - 1)
                      coverage[uni_acc] = {baseclass, {x, y}}
                      return
                    end
                  end
                end
              end
            end
          end
        end
      end

      if not have_mark_feature then
        report("log", 0, "add_to_mark",
               "no 'mark' feature in font '%s'",
               filename)
      else
        if not base_glyph_in_coverage then
          missing_base_glyph(base, filename)
        end
        if not mark_glyph_in_coverage then
          missing_mark_glyph(mark, filename)
        end
      end

      return
    end

    table.insert(patch_functions[font], patch_function)

    luatexbase.add_to_callback(
      "luaotfload.patch_font",
      function(fontdata)
        local path = fontdata.specification.filename
        local filename = file.basename(path)

        local patch_functions = patch_functions[filename]
        if not patch_functions then
          return
        end

        for _, patch_function in pairs(patch_functions) do
          patch_function(fontdata)
        end
      end,
      "patch-fonts"
    )
  end
\end{luacode*}


\begin{luacode*}
  add_to_mark_feature("EBGaramond-Regular.otf",
                      "uni0364", "e", "gravecomb", 115, 440)
  add_to_mark_feature("EBGaramond-Italic.otf",
                      "uni0364", "e", "gravecomb", 115, 440)
\end{luacode*}


\setmainfont{EB Garamond}


\begin{document}

schoͤn

\emph{schoͤn}

\end{document}

%%% Local Variables:
%%% mode: latex
%%% TeX-master: t
%%% End:

Reply via email to