2

You can perform a string substitution with Lua code using gsub (see this answer). With \AtBeginDocument you can limit the scope of the function to the document. But is it also possible to have the function applied only to the text and not to commands, so that the following example would work?

% !TEX TS-program = lualatex
\documentclass{article}
\newcommand*{\dark}[1]{\textbf{#1}}
\usepackage{luacode}
\begin{luacode}
function replace_dark_with_bright ( s )
  s = s:gsub ( "dark", "bright" )
  return s
end
\end{luacode}
\AtBeginDocument{%
  \directlua{luatexbase.add_to_callback (
    "process_input_buffer", replace_dark_with_bright, "replace_dark_with_bright" )}}
\begin{document}
Always look on the dark side of life.
To \dark{emphasize} a word.
\end{document}

Is it possible to replace entire strings with other strings using the font feature functionality, or does it always have to be a single glyph in at least one direction (as with ligature and multiple)? As a font feature substitution is only applied to the text (and not to commands), this would be a solution.

EDIT

To be more specific about what I am trying to do:

The idea is to use Lua string manipulations to implement automatic breaking of certain ligatures without affecting kerning or hyphenation points.

Such a type of solution, as exemplified in the following code, seems to work in principle:

% !TEX TS-program = lualatex
\documentclass{article}
\usepackage{fontspec}
\usepackage{luacode}

\begin{luacode}

fonts.handlers.otf.addfeature{ name = "ligkern", type = "kern", data = { ["f_f"] = { ["l"] = 50 }, ["f"] = { ["l"] = 50 }, }, }

function lig_parse ( s )

function no_fl_lig ( text )
    s = s:gsub ( text, text:gsub ( "fl", "fL" ) )
end

function keep_fl_lig ( text )
    s = s:gsub ( text:gsub ( "fl", "fL" ), text )
end

local c = [===[\char]===]
local ffl = c .. "64260"
local fl = c .. "64258"
local ff = c .. "64256"

no_fl_lig ( "flich" )
keep_fl_lig ( "flicht" )
no_fl_lig ( "öpflicht" )

no_fl_lig ( "griffl" )

s = s:gsub ( "ffl", ffl )
s = s:gsub ( "fl", fl )
s = s:gsub ( "fL", "fl" )
s = s:gsub ( "ff", ff )

return s

end

\end{luacode}

\setmainfont{Latin Modern Roman}[ Ligatures = {NoCommon, NoDiscretionary}, RawFeature =+ ligkern ]

\AtBeginDocument{% \directlua{luatexbase.add_to_callback ("process_input_buffer", lig_parse, "make and break ligatures")}% }

\begin{document}

\noindent Die vortrefflichen Pflichten\ des begrifflosen Knoepfflers\ sind unerschöpflichtief.

\end{document}

enter image description here

However, if there is (for example) an \sffamily in the document, this, of course, causes an issue.. Of course, one could add such commands to the substitutions. But I think it would be a much cleaner solution if the substitutions were only applied to the text in the first place.

keth-tex
  • 466
  • 2
  • 10
  • 5
    see the truly excellent chickenize package for code to change text. (don't be fooled by the silly name and silly examples, it's a good tutorial of lua callback use) – David Carlisle Mar 24 '22 at 21:12
  • 1
    @DavidCarlisle Thanks for the tip, but according to the manual, the chickenize package also applies the substitutions to the commands. I also read the tutorial, but I wouldn't know how to implement a solution based on the information given there. – keth-tex Mar 25 '22 at 06:37
  • 2
    No. Look at for example def\chickenize{ \directlua{luatexbase.add_to_callback("pre_linebreak_filter",chickenize,"chickenize") it is using pre_linebreak_filter so working at the level of typeset character nodes not process_input_buffer which is working on the source text lines. – David Carlisle Mar 25 '22 at 08:27
  • @DavidCarlisle Ah... of course! You are right. In principle, it should be possible that way. I will look into it. – keth-tex Mar 25 '22 at 17:05
  • @DavidCarlisle Well, I managed to get the substitutions to work. But now the problem is that the kerning has already been applied when using the pre_linebreak_filter as hook. There are the node library kerning and ligature functions (manual p. 154), but nobody knows how they are supposed to work. According to the comments in the source code, the author of the chickenize package didn't know how to apply them either. :-( – keth-tex Mar 27 '22 at 07:28
  • @DavidCarlisle Okay, I can quite easily remove the notes between the glyphs that are replaced by a ligature. Provided that the sidebearings and the kerning of the ligatures match the corresponding single characters (which is usually the case), it works that way. But it would still be nice to know how to apply the ligaturing and kerning functions of the node library. – keth-tex Mar 27 '22 at 08:20
  • Sorry can't look now but if you can make a small plain tex (not latex) example asking how to use some undocumented function and ask on the luatex list, Hans or another luatex insider is usually quite helpful:-) I think one simple way (if most of the document is in a standard font) would be at the start typeset your replacement word into a box then add a copy of that box content in each replacement call, so it has kerns etc already computed – David Carlisle Mar 27 '22 at 09:03

1 Answers1

2

I'm not sure if I understand what your main objective is. If it's to change "dark" to "bright" while leaving \dark alone, I suggest replacing the single search pattern dark with two patterns: "([^\\])dark" and ^dark, in two gsub statements.

enter image description here

% !TEX TS-program = lualatex
\documentclass{article}
\newcommand*{\dark}[1]{\textbf{#1}}
\usepackage{luacode}
\begin{luacode}
function replace_dark_with_bright ( s )
  s = s:gsub ( "([^\\])dark", "%1bright" ) 
  s = s:gsub ( "^dark" , "bright" )
  return s
end
\end{luacode}
\AtBeginDocument{\directlua{luatexbase.add_to_callback (
    "process_input_buffer", replace_dark_with_bright, "replace_dark_with_bright" )}}
\begin{document}
Always look on the dark side of life.
To \dark{emphasize} a word.

dark is the day \end{document}

Mico
  • 506,678