12

I have got the following problem: I am using Biblical Hebrew texts written in XeTeX but I would like to color consonants as well as vowel and accent signs differently. I have read Coloring combining characters without changing color of a base character, but the ways suggested to have it done seem too laborious.

So, is there a more straight forward way to assign - more or less permanently - each unicode sign (or maybe an entire consecutive unicode sign range) an inidvidual color? For instance, as in the linked example, I would like all consonants to appear in black, all vowels signs in red and all accent signs in blue.

3

2 Answers 2

7

You could do this in LuaLaTeX, which lets you (for the most part) simply copy and paste in Hebrew in your document body. You do need a language tag (such as \begin{otherlanguage}{hebrew}) when switching between left-to-right and right-to-left paragraphs, sections, and tables. Modulo a few bugs noted below, it mostly just works.

Latest Answer

This version has a few improvements over the others. It redefines most, but not all, of the glyph substitutions of the original font. If the rendering problems are not as important as making all vowels and only vowels the same color, you might prefer my first answer below.

\documentclass{article}
\tracinglostchars=3 % Panic if a font does not contain the character we need.
\usepackage[svgnames]{xcolor}
\usepackage{ luacode, luatexbase, luacolor }
\usepackage{inputnormalization}
% The input should be normalized to NFD form in order to correctly display
% ligatures.
\Uinputnormalization=2
\usepackage[bidi=basic, layout=sectioning.tabular, english]{babel}
\usepackage{fontspec}
\usepackage{microtype}
\usepackage{newunicodechar}

\babelprovide[import=he, onchar=fonts ids]{hebrew}

\begin{luacode*}
fonts.handlers.otf.addfeature {
  -- Currently unused.
  name = "xdsh", -- Disable unwanted shuruk and holam male ligatures. Happens too late to add color.
  type ="multiple",
  data = {
    [0xfb35] = { 0x05d5, 0x05bc },
    [0xfb4b] = { 0x05d5, 0x05b9 },
  },
}

fonts.handlers.otf.addfeature {
  name = "xalt",
  type = "chainsubstitution",
  lookups = {
    {
      type = "substitution", -- 1
      data = {
        ["sheva"] = "sheva_for_finalkaf",
        ["qamats"] = "qamats_for_finalkaf",
      },
    },
    {
      type = "substitution", -- 2
      data = {
        ["holamhaser"] = "holam",
      },
    },
    {
      type = "substitution", -- 3
      data = {
       [ "hiriq"] = "hiriqmem",
      },
    },
    {
      type = "substitution", -- 4
      data = {
        ["ayin"] = "ayin.alt",
      },
    },
  },
  data = {
    rules = {
      {
        before = { {"finalkaf"} }, -- This needs to work for final kaf with tafa, too.
        current = { {"sheva", "qamats"} },
        lookups = { 1 },
      },
      {
        current = { {"holamhaser"} },
        lookups = { 2 },
      },
      {
        before = { { "finalmem", "finalmem.wide" } }, -- Should allow intervening vowels and taamim.
        current = { {"hiriq"} },
        after = { { "patah", "qamats" } },
        lookups = { 3 },
      },
      {
        current = { {"ayin"} },
        -- Should support many other combinations, including accents between the ayin and an accent below.
        after = { { "atnah", "tipeha", "yetiv", "tevir", "atnahhafukh",
                    "munah", "mahapakh", "merkha", "merkhakefula", "darga",
                    "yerahbenyomo", "dehi", "sheva", "hatafsegol",
                    "hatafpatah", "hatafqamats", "hiriq", "tsere", "segol",
                    "patah", "qamats", "qubuts", "meteg", "qamatsqatan",
                    "hatafsegol_meteg", "hatafpatah_meteg",
                    "hatafqamats_meteg" } },
        lookups = { 4 },
      },
    },
  },
}

fonts.handlers.otf.addfeature {
  name = "xlig", -- User-defined replacement ligatures
  type = "ligature",
  data = {
    ["vavvav"] = { "vav", "vav" },
    ["vavyod"] = { "vav", "yod" },
    ["yodyod"] = { "yod", "yod" },
    -- Of the dagesh ligatures, only vav, zayin, yod and nun are defined in
    -- Taamey Ashkenaz of which vavdagesh, AKA shuruk, is intentionally
    -- omitted.
    ["zayindagesh"] = { "zayin", "dagesh" },
    ["yoddagesh"] = { "yod", "dagesh" },
    ["nundagesh"] = { "nun", "dagesh" },
    -- vavholam is intentionally omitted.
    -- None of the rafe ligatures are defined in Taamey Ashkenaz
    ["shinshindot.thin"] = { "shin.thin", "shindot" },
    ["shinsindot.thin"] = { "shin.thin", "sindot" },
    ["zayindagesh.thin"] = { "zayin.thin", "dagesh" },
    ["yoddagesh.thin"] = { "yod.thin", "dagesh" },
    ["nundagesh.thin"] = { "nun.thin", "dagesh" },
    ["hatafsegol_meteg"] = { "hatafsegol", "zerojoin", "meteg" },
    ["hatafpatah_meteg"] = { "hatafmatah", "zerojoin", "meteg" },
    ["hatafqamats_meteg"] = { "hatafqamats", "zerojoin", "meteg" },
    ["aleflamed"] = { "alef", "zerojoin", "lamed" },
  },
}

fonts.handlers.otf.addfeature {
  name = "xkrn", -- custom kern
  type = "pair",
  data = {
    ["vav"] = {
      ["holam"] = { -- AKA holam male
        false, { 300, 0, 0, 0 } -- Tuned for Taamey Ashkenaz
      },
    },
  },
}
\end{luacode*}

\defaultfontfeatures{ Scale=MatchUppercase }

% Taamey Ashkenaz and other Culmus fonts with Taamim are available, libre, at:
% https://culmus.sourceforge.io/taamim/index.html
% If you want Hebrew letters that slant from left to right rather than right
% to left, replace the ItalicFont= and BoldItalicFont= features with
% AutoFakeSlant = 0.15
\defaultfontfeatures[TaameyAshkenaz]{
  RawFeature = {-ccmp,+xlig,+xalt,+xkrn},
  UprightFont = *-Medium,
  BoldFont = *-Bold,
  ItalicFont = *-MediumOblique,
  BoldItalicFont = *-BoldOblique,
  Extension = .ttf }

\babelfont{rm}
          [Scale=1.0, Language=Default]{TaameyAshkenaz}
\babelfont[english]{rm}
          [Language=Default]{TeX Gyre Schola}

\newcommand\niqqudcolor{DarkOrange}
\newcommand\taameycolor{DarkCyan}

% In order to make shuruk behave differently from dagesh, we need some state:
\makeatletter
\newif\ifafter@vav@
\after@vav@false
\newcommand\DeclareHeBase[1]{\newunicodechar{#1}{\after@vav@false #1}}
\DeclareHeBase{^^^^05d0} % alef
\DeclareHeBase{^^^^05d1} % bet
\DeclareHeBase{^^^^05d2} % gimel
\DeclareHeBase{^^^^05d3} % dalet
\DeclareHeBase{^^^^05d4} % he
\newunicodechar{^^^^05d5}{\after@vav@true ^^^^05d5} % vav
\DeclareHeBase{^^^^05d6} % zayin
\DeclareHeBase{^^^^05d7} % het
\DeclareHeBase{^^^^05d8} % tet
\DeclareHeBase{^^^^05d9} % yod
\DeclareHeBase{^^^^05da} % final qof
\DeclareHeBase{^^^^05db} % qof
\DeclareHeBase{^^^^05dc} % lamed
\DeclareHeBase{^^^^05dd} % final mem
\DeclareHeBase{^^^^05de} % mem
\DeclareHeBase{^^^^05df} % final nun
\DeclareHeBase{^^^^05e0} % nun
\DeclareHeBase{^^^^05e1} % samekh
\DeclareHeBase{^^^^05e2} % ayin
\DeclareHeBase{^^^^05e3} % final pe
\DeclareHeBase{^^^^05e4} % pe
\DeclareHeBase{^^^^05e5} % final tsadi
\DeclareHeBase{^^^^05e6} % tsadi
\DeclareHeBase{^^^^05e7} % qof
\DeclareHeBase{^^^^05e8} % resh
\DeclareHeBase{^^^^05e9} % shin
\DeclareHeBase{^^^^05ea} % tav
\DeclareHeBase{^^^^00a0} % non-breaking space
\DeclareHeBase{^^^^25cc} % dotted circle

%Omitted: base letters that do not appear in NFD-normalized Biblical Hebrew.

% U+05BC needs to be check whether it is a dagesh, mapiq or shuruq:
\newunicodechar{^^^^05bc}{%
  \ifafter@vav@%
    {\textcolor{\niqqudcolor}{^^^^05bc}}%
    \else\textcolor{Red}{^^^^05bc}%
  \fi%
}

\newcommand\DeclareTaamey[1]{\newunicodechar{#1}{\textcolor{\taameycolor}{#1}}}
\DeclareTaamey{^^^^0591} % etnahta
\DeclareTaamey{^^^^0592} % segol
\DeclareTaamey{^^^^0593} % shalshelet
\DeclareTaamey{^^^^0594} % zakef qatan
\DeclareTaamey{^^^^0595} % zakef gadol
\DeclareTaamey{^^^^0596} % tipcha
\DeclareTaamey{^^^^0597} % revia
\DeclareTaamey{^^^^0598} % zarqa
\DeclareTaamey{^^^^0599} % pashta
\DeclareTaamey{^^^^059a} % yehiv
\DeclareTaamey{^^^^059b} % tevir
\DeclareTaamey{^^^^059c} % qeresh
\DeclareTaamey{^^^^059d} % weresh muqdam
\DeclareTaamey{^^^^059e} % gershayim
\DeclareTaamey{^^^^059f} % qarney para
\DeclareTaamey{^^^^05a0} % telishah gedola
\DeclareTaamey{^^^^05a1} % pazer
\DeclareTaamey{^^^^05a2} % atnah hafukh
\DeclareTaamey{^^^^05a3} % munah
\DeclareTaamey{^^^^05a4} % mahapakh
\DeclareTaamey{^^^^05a5} % merkha
\DeclareTaamey{^^^^05a6} % merkha kefula
\DeclareTaamey{^^^^05a7} % darga
\DeclareTaamey{^^^^05a8} % qadma
\DeclareTaamey{^^^^05a9} % telisha qetana
\DeclareTaamey{^^^^05aa} % yerah ben yomo
\DeclareTaamey{^^^^05ab} % ole
\DeclareTaamey{^^^^05ac} % iluy
\DeclareTaamey{^^^^05ad} % dehi
\DeclareTaamey{^^^^05ae} % zinor
\DeclareTaamey{^^^^05c0} % paseq
\DeclareTaamey{^^^^05c3} % sof pasuq
\DeclareTaamey{^^^^05c4} % upper dot
\DeclareTaamey{^^^^05c5} % lower dot
\DeclareTaamey{^^^^05f3} % geresh
\DeclareTaamey{^^^^05f4} % gershayim

\newcommand\DeclareNiqqud[1]{\newunicodechar{#1}{\textcolor{\niqqudcolor}{#1}}}
\DeclareNiqqud{^^^^05b0} % sheva
\DeclareNiqqud{^^^^05b1} % hataf segol
\DeclareNiqqud{^^^^05b2} % hataf paqah
\DeclareNiqqud{^^^^05b3} % hataf qamats
\DeclareNiqqud{^^^^05b4} % hiriq
\DeclareNiqqud{^^^^05b5} % tsere
\DeclareNiqqud{^^^^05b6} % segol
\DeclareNiqqud{^^^^05b7} % patah
\DeclareNiqqud{^^^^05b8} % qamatz
\DeclareNiqqud{^^^^05b9} % holam
\DeclareNiqqud{^^^^05ba} % holam haser
\DeclareNiqqud{^^^^05bb} % qubuts
\DeclareNiqqud{^^^^05bd} % meteg
\DeclareNiqqud{^^^^05c7} % qamats qatan

% Alphabetic presentation forms that need special handling
%\DeclareNiqqud{^^^^fb4b}
%\DeclareNiqqud{^^^^fb35}

% Intentionally not colored: shin dot, sin dot, rafe, maqaf, nun hafukha
\makeatother

% Improves layout of paragraphs with long, un-hyphenatable words:
\emergencystretch=3em

\begin{document}
\begin{otherlanguage}{hebrew}
\section*{קדושים}

\noindent
וּֽבְקֻצְרְכֶם֙ אֶת־קְצִ֣יר אַרְצְכֶ֔ם לֹ֧א תְכַלֶּ֛ה פְּאַ֥ת שָׂדְךָ֖ לִקְצֹ֑ר וְלֶ֥קֶט קְצִֽירְךָ֖ לֹ֥א תְלַקֵּֽט׃
וְכַרְמְךָ֙ לֹ֣א תְעוֹלֵ֔ל וּפֶ֥רֶט כַּרְמְךָ֖ לֹ֣א תְלַקֵּ֑ט לֶֽעָנִ֤י וְלַגֵּר֙ תַּעֲזֹ֣ב אֹתָ֔ם אֲנִ֖י יְהֹוָ֥ה אֱלֹהֵיכֶֽם׃
וְלֹֽא־תִשָּׁבְע֥וּ בִשְׁמִ֖י לַשָּׁ֑קֶר וְחִלַּלְתָּ֛ אֶת־שֵׁ֥ם אֱלֹהֶ֖יךָ אֲנִ֥י יְהֹוָֽה׃
לֹֽא־תַעֲשֹׁ֥ק אֶת־רֵֽעֲךָ֖ וְלֹ֣א תִגְזֹ֑ל לֹֽא־תָלִ֞ין פְּעֻלַּ֥ת שָׂכִ֛יר אִתְּךָ֖ עַד־בֹּֽקֶר׃
לֹא־תְקַלֵּ֣ל חֵרֵ֔שׁ וְלִפְנֵ֣י עִוֵּ֔ר לֹ֥א תִתֵּ֖ן מִכְשֹׁ֑ל וְיָרֵ֥אתָ מֵּאֱלֹהֶ֖יךָ אֲנִ֥י יְהֹוָֽה׃
לֹא־תַעֲשׂ֥וּ עָ֙וֶל֙ בַּמִּשְׁפָּ֔ט לֹא־תִשָּׂ֣א פְנֵי־דָ֔ל וְלֹ֥א תֶהְדַּ֖ר פְּנֵ֣י גָד֑וֹל בְּצֶ֖דֶק תִּשְׁפֹּ֥ט עֲמִיתֶֽךָ׃
לֹא־תֵלֵ֤ךְ רָכִיל֙ בְּעַמֶּ֔יךָ לֹ֥א תַעֲמֹ֖ד עַל־דַּ֣ם רֵעֶ֑ךָ אֲנִ֖י יְהֹוָֽה׃
לֹֽא־תִשְׂנָ֥א אֶת־אָחִ֖יךָ בִּלְבָבֶ֑ךָ הוֹכֵ֤חַ תּוֹכִ֙יחַ֙ אֶת־עֲמִיתֶ֔ךָ וְלֹא־תִשָּׂ֥א עָלָ֖יו חֵֽטְא׃
לֹֽא־תִקֹּ֤ם וְלֹֽא־תִטֹּר֙ אֶת־בְּנֵ֣י עַמֶּ֔ךָ וְאָֽהַבְתָּ֥ לְרֵעֲךָ֖ כָּמ֑וֹךָ אֲנִ֖י יְהֹוָֽה׃
\end{otherlanguage}


This is followed by, {הַגָּ֣ר אִתְּכֶ֗ם וְאָהַבְתָּ֥ לוֹ֙ כָּמ֔וֹךָ}.

\end{document}

Kedoshim in Taamey Ashkenaz

So, you will notice that ayin now usually displays with a shallow descender when it should, but not always. It won’t work if the ayin has a dagesh, holam or rafa. A final kaf will now display vowels in the correct position, unless it also has a rafe. And that the pashta between a dalet and a holam male displays too far to the right.

All of this is because I haven’t implemented any patterns that would require more than one-token lookahead or lookbehind.

First Answer

\documentclass{article}
\tracinglostchars=3 % Panic if a font does not contain the character we need.
\usepackage[svgnames]{xcolor}
\usepackage{luacolor}
\usepackage{inputnormalization}
% The code to set combining characters active assumes input in decomposed form.
% Setting input normalization should suffice for all real-world cases.  If,
% for some reason, you need to generate Hebrew text programmatically, please
% avoid generating any codepoints such as YOD WITH HIRIQ.  (Which is also
% not valid NFC since Unicode 3.0.)
\Uinputnormalization=2
\usepackage[bidi=basic, layout=sectioning.tabular, english]{babel}
\usepackage{fontspec}
\usepackage{microtype}
\usepackage{newunicodechar}

\babelprovide[import=he, onchar=fonts ids]{hebrew}
\defaultfontfeatures{ Ligatures=TeX, Scale=MatchUppercase }

% Taamey Ashkenaz and other Culmus fonts with Taamim are available, libre, at:
% https://culmus.sourceforge.io/taamim/index.html
% If you want Hebrew letters that slant from left to right rather than right
% to left, replace the ItalicFont= and BoldItalicFont= features with
% AutoFakeSlant = 0.15
\defaultfontfeatures[TaameyAshkenaz]{
  UprightFont = *-Medium,
  BoldFont = *-Bold,
  ItalicFont = *-MediumOblique,
  BoldItalicFont = *-BoldOblique,
  Extension = .ttf }

\babelfont{rm}
          [Scale=1.0, Language=Default]{TeX Gyre Schola}
\babelfont[hebrew]{rm}
          [Language=Default]{TaameyAshkenaz}

\newcommand\niqqudcolor{DarkOrange}
\newcommand\taameycolor{DarkCyan}
\newcommand\DeclareTaamey[1]{\newunicodechar{#1}{\textcolor{\taameycolor}{#1}}}
\DeclareTaamey{^^^^0591} % etnahta
\DeclareTaamey{^^^^0592} % segol
\DeclareTaamey{^^^^0593} % shalshelet
\DeclareTaamey{^^^^0594} % zakef qatan
\DeclareTaamey{^^^^0595} % zakef gadol
\DeclareTaamey{^^^^0596} % tipcha
\DeclareTaamey{^^^^0597} % revia
\DeclareTaamey{^^^^0598} % zarqa
\DeclareTaamey{^^^^0599} % pashta
\DeclareTaamey{^^^^059a} % yehiv
\DeclareTaamey{^^^^059b} % tevir
\DeclareTaamey{^^^^059c} % qeresh
\DeclareTaamey{^^^^059d} % weresh muqdam
\DeclareTaamey{^^^^059e} % gershayim
\DeclareTaamey{^^^^059f} % qarney para
\DeclareTaamey{^^^^05a0} % telishah gedola
\DeclareTaamey{^^^^05a1} % pazer
\DeclareTaamey{^^^^05a2} % atnah hafukh
\DeclareTaamey{^^^^05a3} % munah
\DeclareTaamey{^^^^05a4} % mahapakh
\DeclareTaamey{^^^^05a5} % merkha
\DeclareTaamey{^^^^05a6} % merkha kefula
\DeclareTaamey{^^^^05a7} % darga
\DeclareTaamey{^^^^05a8} % qadma
\DeclareTaamey{^^^^05a9} % telisha qetana
\DeclareTaamey{^^^^05aa} % yerah ben yomo
\DeclareTaamey{^^^^05ab} % ole
\DeclareTaamey{^^^^05ac} % iluy
\DeclareTaamey{^^^^05ad} % dehi
\DeclareTaamey{^^^^05ae} % zinor
\DeclareTaamey{^^^^05c0} % paseq
\DeclareTaamey{^^^^05c3} % sof pasuq
\DeclareTaamey{^^^^05c4} % upper dot
\DeclareTaamey{^^^^05c5} % lower dot
\DeclareTaamey{^^^^05f3} % geresh
\DeclareTaamey{^^^^05f4} % gershayim

\newcommand\DeclareNiqqud[1]{\newunicodechar{#1}{\textcolor{\niqqudcolor}{#1}}}
\DeclareNiqqud{^^^^05b0} % sheva
\DeclareNiqqud{^^^^05b1} % hataf segol
\DeclareNiqqud{^^^^05b2} % hataf paqah
\DeclareNiqqud{^^^^05b3} % hataf qamats
\DeclareNiqqud{^^^^05b4} % hiriq
\DeclareNiqqud{^^^^05b5} % tsere
\DeclareNiqqud{^^^^05b6} % segol
\DeclareNiqqud{^^^^05b7} % patah
\DeclareNiqqud{^^^^05b8} % qamatz
\DeclareNiqqud{^^^^05b9} % holam
\DeclareNiqqud{^^^^05ba} % holam haser
\DeclareNiqqud{^^^^05bb} % qubuts
\DeclareNiqqud{^^^^05bd} % meteg
\DeclareNiqqud{^^^^05c7} % qamats qatan

% Intentionally not colored: shin dot, sin dot, dagesh/mapiq, rafe, maqaf, nun hafukha

% Improves layout of paragraphs with long, un-hyphenatable words:
\emergencystretch=3em

\begin{document}
\begin{otherlanguage}{hebrew}
\section*{קדושים}

\noindent
וּֽבְקֻצְרְכֶם֙ אֶת־קְצִ֣יר אַרְצְכֶ֔ם לֹ֧א תְכַלֶּ֛ה פְּאַ֥ת שָׂדְךָ֖ לִקְצֹ֑ר וְלֶ֥קֶט קְצִֽירְךָ֖ לֹ֥א תְלַקֵּֽט׃
וְכַרְמְךָ֙ לֹ֣א תְעוֹלֵ֔ל וּפֶ֥רֶט כַּרְמְךָ֖ לֹ֣א תְלַקֵּ֑ט לֶֽעָנִ֤י וְלַגֵּר֙ תַּעֲזֹ֣ב אֹתָ֔ם אֲנִ֖י יְהֹוָ֥ה אֱלֹהֵיכֶֽם׃
וְלֹֽא־תִשָּׁבְע֥וּ בִשְׁמִ֖י לַשָּׁ֑קֶר וְחִלַּלְתָּ֛ אֶת־שֵׁ֥ם אֱלֹהֶ֖יךָ אֲנִ֥י יְהֹוָֽה׃
לֹֽא־תַעֲשֹׁ֥ק אֶת־רֵֽעֲךָ֖ וְלֹ֣א תִגְזֹ֑ל לֹֽא־תָלִ֞ין פְּעֻלַּ֥ת שָׂכִ֛יר אִתְּךָ֖ עַד־בֹּֽקֶר׃
לֹא־תְקַלֵּ֣ל חֵרֵ֔שׁ וְלִפְנֵ֣י עִוֵּ֔ר לֹ֥א תִתֵּ֖ן מִכְשֹׁ֑ל וְיָרֵ֥אתָ מֵּאֱלֹהֶ֖יךָ אֲנִ֥י יְהֹוָֽה׃
לֹא־תַעֲשׂ֥וּ עָ֙וֶל֙ בַּמִּשְׁפָּ֔ט לֹא־תִשָּׂ֣א פְנֵי־דָ֔ל וְלֹ֥א תֶהְדַּ֖ר פְּנֵ֣י גָד֑וֹל בְּצֶ֖דֶק תִּשְׁפֹּ֥ט עֲמִיתֶֽךָ׃
לֹא־תֵלֵ֤ךְ רָכִיל֙ בְּעַמֶּ֔יךָ לֹ֥א תַעֲמֹ֖ד עַל־דַּ֣ם רֵעֶ֑ךָ אֲנִ֖י יְהֹוָֽה׃
לֹֽא־תִשְׂנָ֥א אֶת־אָחִ֖יךָ בִּלְבָבֶ֑ךָ הוֹכֵ֤חַ תּוֹכִ֙יחַ֙ אֶת־עֲמִיתֶ֔ךָ וְלֹא־תִשָּׂ֥א עָלָ֖יו חֵֽטְא׃
לֹֽא־תִקֹּ֤ם וְלֹֽא־תִטֹּר֙ אֶת־בְּנֵ֣י עַמֶּ֔ךָ וְאָֽהַבְתָּ֥ לְרֵעֲךָ֖ כָּמ֑וֹךָ אֲנִ֖י יְהֹוָֽה׃
\end{otherlanguage}


This is followed by,
הַגָּ֣ר אִתְּכֶ֗ם וְאָהַבְתָּ֥ לוֹ֙ כָּמ֔וֹךָ.

\end{document}

Kedoshim

What this does not handle correctly, at least with this particular font, are shuruk and holam male. In this font, they are mapped to the contextual compositions וּ (U+FB35) and וֹ (U+FB4B). Decomposition causes the colors to be ignored. Disabling the contextual compositions breaks other rendering. Canonical normalization does not convert to the precomposed form.

In order to fix this, you could intercept the list of glyph nodes in a more complex way through a Lua callback.

Second Answer

Here’s another version that fixes shuruk and holam male. I think the handling of לוֹ֙ (lamed vav holam pashta) leaves a little something to be desired. (Edit: because one of the many default font substitutions that I did not copy over replaces pashta with qadma to the left of a lamed.)

\documentclass{article}
\tracinglostchars=3 % Panic if a font does not contain the character we need.
\usepackage[svgnames]{xcolor}
\usepackage{ luacode, luatexbase, luacolor }
\usepackage{inputnormalization}
% The input should be normalized to NFD form in order to correctly display
% ligatures.
\Uinputnormalization=2
\usepackage[bidi=basic, layout=sectioning.tabular, english]{babel}
\usepackage{fontspec}
\usepackage{microtype}
\usepackage{newunicodechar}

\babelprovide[import=he, onchar=fonts ids]{hebrew}

\begin{luacode*}
fonts.handlers.otf.addfeature {
  name = "xalt",
  type = "chainsubstitution",
  lookups = {
    {
      type = "substitution",
      data = {
        ["sheva"] = "sheva_for_finalkaf",
        ["qamats"] = "qamats_for_finalkaf",
      },
    },
  },
  data = {
    rules = {
      {
        before = { {"finalkaf"} },
        current = { {"sheva", "qamats"} },
        lookups = { 1 },
      },
    },
  },
}

fonts.handlers.otf.addfeature {
  name = "xlig", -- User-defined replacement ligatures
  type = "ligature",
  data = {
    ["vavvav"] = { "vav", "vav" },
    ["vavyod"] = { "vav", "yod" },
    ["yodyod"] = { "yod", "yod" },
    -- Of the dagesh ligatures, only vav, zayin, yod and nun are defined in
    -- Taamey Ashkenaz of which vavdagesh, AKA shuruk, is intentionally
    -- omitted.
    ["zayindagesh"] = { "zayin", "dagesh" },
    ["yoddagesh"] = { "yod", "dagesh" },
    ["nundagesh"] = { "nun", "dagesh" },
    -- vavholam is intentionally omitted.
    -- None of the rafe ligatures are defined in Taamey Ashkenaz
    ["shinshindot.thin"] = { "shin.thin", "shindot" },
    ["shinsindot.thin"] = { "shin.thin", "sindot" },
    ["zayindagesh.thin"] = { "zayin.thin", "dagesh" },
    ["yoddagesh.thin"] = { "yod.thin", "dagesh" },
    ["nundagesh.thin"] = { "nun.thin", "dagesh" },
    ["hatafsegol_meteg"] = { "hatafsegol", "zerojoin", "meteg" },
    ["hatafpatah_meteg"] = { "hatafmatah", "zerojoin", "meteg" },
    ["hatafqamats_meteg"] = { "hatafqamats", "zerojoin", "meteg" },
    ["aleflamed"] = { "alef", "zerojoin", "lamed" },
  },
}

fonts.handlers.otf.addfeature {
  name = "xkrn", -- custom kern
  type = "pair",
  data = {
    ["vav"] = {
      ["holam"] = { -- AKA holam male
        false, { 300, 0, 0, 0 } -- Tuned for Taamey Ashkenaz
      },
    },
  },
}
\end{luacode*}

\defaultfontfeatures{ Scale=MatchUppercase }

% Taamey Ashkenaz and other Culmus fonts with Taamim are available, libre, at:
% https://culmus.sourceforge.io/taamim/index.html
% If you want Hebrew letters that slant from left to right rather than right
% to left, replace the ItalicFont= and BoldItalicFont= features with
% AutoFakeSlant = 0.15
\defaultfontfeatures[TaameyAshkenaz]{
  UprightFont = *-Medium,
  BoldFont = *-Bold,
  ItalicFont = *-MediumOblique,
  BoldItalicFont = *-BoldOblique,
  RawFeature={-ccmp, -calt, +xalt, +xlig, +xkrn},
  Extension = .ttf }

\babelfont{rm}
          [Scale=1.0, Language=Default]{TaameyAshkenaz}
\babelfont[english]{rm}
          [Language=Default]{TeX Gyre Schola}

\newcommand\niqqudcolor{DarkOrange}
\newcommand\taameycolor{DarkCyan}

% In order to make shuruk behave differently from dagesh, we need some state:
\makeatletter
\newif\ifafter@vav@
\after@vav@false
\newcommand\DeclareHeBase[1]{\newunicodechar{#1}{\after@vav@false #1}}
\DeclareHeBase{^^^^05d0} % alef
\DeclareHeBase{^^^^05d1} % bet
\DeclareHeBase{^^^^05d2} % gimel
\DeclareHeBase{^^^^05d3} % dalet
\DeclareHeBase{^^^^05d4} % he
\newunicodechar{^^^^05d5}{\after@vav@true ^^^^05d5} % vav
\DeclareHeBase{^^^^05d6} % zayin
\DeclareHeBase{^^^^05d7} % het
\DeclareHeBase{^^^^05d8} % tet
\DeclareHeBase{^^^^05d9} % yod
\DeclareHeBase{^^^^05da} % final qof
\DeclareHeBase{^^^^05db} % qof
\DeclareHeBase{^^^^05dc} % lamed
\DeclareHeBase{^^^^05dd} % final mem
\DeclareHeBase{^^^^05de} % mem
\DeclareHeBase{^^^^05df} % final nun
\DeclareHeBase{^^^^05e0} % nun
\DeclareHeBase{^^^^05e1} % samekh
\DeclareHeBase{^^^^05e2} % ayin
\DeclareHeBase{^^^^05e3} % final pe
\DeclareHeBase{^^^^05e4} % pe
\DeclareHeBase{^^^^05e5} % final tsadi
\DeclareHeBase{^^^^05e6} % tsadi
\DeclareHeBase{^^^^05e7} % qof
\DeclareHeBase{^^^^05e8} % resh
\DeclareHeBase{^^^^05e9} % shin
\DeclareHeBase{^^^^05ea} % tav
\DeclareHeBase{^^^^00a0} % non-breaking space
\DeclareHeBase{^^^^25cc} % dotted circle

%Omitted: base letters that do not appear in NFD-normalized Biblical Hebrew.

% U+05BC needs to be check whether it is a dagesh, mapiq or shuruq:
\newunicodechar{^^^^05bc}{%
  \ifafter@vav@%
    {\textcolor{\niqqudcolor}{^^^^05bc}}%
    \else{^^^^05bc}%
  \fi%
}

\newcommand\DeclareTaamey[1]{\newunicodechar{#1}{\textcolor{\taameycolor}{#1}}}
\DeclareTaamey{^^^^0591} % etnahta
\DeclareTaamey{^^^^0592} % segol
\DeclareTaamey{^^^^0593} % shalshelet
\DeclareTaamey{^^^^0594} % zakef qatan
\DeclareTaamey{^^^^0595} % zakef gadol
\DeclareTaamey{^^^^0596} % tipcha
\DeclareTaamey{^^^^0597} % revia
\DeclareTaamey{^^^^0598} % zarqa
\DeclareTaamey{^^^^0599} % pashta
\DeclareTaamey{^^^^059a} % yehiv
\DeclareTaamey{^^^^059b} % tevir
\DeclareTaamey{^^^^059c} % qeresh
\DeclareTaamey{^^^^059d} % weresh muqdam
\DeclareTaamey{^^^^059e} % gershayim
\DeclareTaamey{^^^^059f} % qarney para
\DeclareTaamey{^^^^05a0} % telishah gedola
\DeclareTaamey{^^^^05a1} % pazer
\DeclareTaamey{^^^^05a2} % atnah hafukh
\DeclareTaamey{^^^^05a3} % munah
\DeclareTaamey{^^^^05a4} % mahapakh
\DeclareTaamey{^^^^05a5} % merkha
\DeclareTaamey{^^^^05a6} % merkha kefula
\DeclareTaamey{^^^^05a7} % darga
\DeclareTaamey{^^^^05a8} % qadma
\DeclareTaamey{^^^^05a9} % telisha qetana
\DeclareTaamey{^^^^05aa} % yerah ben yomo
\DeclareTaamey{^^^^05ab} % ole
\DeclareTaamey{^^^^05ac} % iluy
\DeclareTaamey{^^^^05ad} % dehi
\DeclareTaamey{^^^^05ae} % zinor
\DeclareTaamey{^^^^05c0} % paseq
\DeclareTaamey{^^^^05c3} % sof pasuq
\DeclareTaamey{^^^^05c4} % upper dot
\DeclareTaamey{^^^^05c5} % lower dot
\DeclareTaamey{^^^^05f3} % geresh
\DeclareTaamey{^^^^05f4} % gershayim

\newcommand\DeclareNiqqud[1]{\newunicodechar{#1}{\textcolor{\niqqudcolor}{#1}}}
\DeclareNiqqud{^^^^05b0} % sheva
\DeclareNiqqud{^^^^05b1} % hataf segol
\DeclareNiqqud{^^^^05b2} % hataf paqah
\DeclareNiqqud{^^^^05b3} % hataf qamats
\DeclareNiqqud{^^^^05b4} % hiriq
\DeclareNiqqud{^^^^05b5} % tsere
\DeclareNiqqud{^^^^05b6} % segol
\DeclareNiqqud{^^^^05b7} % patah
\DeclareNiqqud{^^^^05b8} % qamatz
\DeclareNiqqud{^^^^05b9} % holam
\DeclareNiqqud{^^^^05ba} % holam haser
\DeclareNiqqud{^^^^05bb} % qubuts
\DeclareNiqqud{^^^^05bd} % meteg
\DeclareNiqqud{^^^^05c7} % qamats qatan

% Alphabetic presentation forms that need special handling
%\DeclareNiqqud{^^^^fb4b}
%\DeclareNiqqud{^^^^fb35}

% Intentionally not colored: shin dot, sin dot, rafe, maqaf, nun hafukha
\makeatother

% Improves layout of paragraphs with long, un-hyphenatable words:
\emergencystretch=3em

\begin{document}
\begin{otherlanguage}{hebrew}
\section*{קדושים}

\noindent
וּֽבְקֻצְרְכֶם֙ אֶת־קְצִ֣יר אַרְצְכֶ֔ם לֹ֧א תְכַלֶּ֛ה פְּאַ֥ת שָׂדְךָ֖ לִקְצֹ֑ר וְלֶ֥קֶט קְצִֽירְךָ֖ לֹ֥א תְלַקֵּֽט׃
וְכַרְמְךָ֙ לֹ֣א תְעוֹלֵ֔ל וּפֶ֥רֶט כַּרְמְךָ֖ לֹ֣א תְלַקֵּ֑ט לֶֽעָנִ֤י וְלַגֵּר֙ תַּעֲזֹ֣ב אֹתָ֔ם אֲנִ֖י יְהֹוָ֥ה אֱלֹהֵיכֶֽם׃
וְלֹֽא־תִשָּׁבְע֥וּ בִשְׁמִ֖י לַשָּׁ֑קֶר וְחִלַּלְתָּ֛ אֶת־שֵׁ֥ם אֱלֹהֶ֖יךָ אֲנִ֥י יְהֹוָֽה׃
לֹֽא־תַעֲשֹׁ֥ק אֶת־רֵֽעֲךָ֖ וְלֹ֣א תִגְזֹ֑ל לֹֽא־תָלִ֞ין פְּעֻלַּ֥ת שָׂכִ֛יר אִתְּךָ֖ עַד־בֹּֽקֶר׃
לֹא־תְקַלֵּ֣ל חֵרֵ֔שׁ וְלִפְנֵ֣י עִוֵּ֔ר לֹ֥א תִתֵּ֖ן מִכְשֹׁ֑ל וְיָרֵ֥אתָ מֵּאֱלֹהֶ֖יךָ אֲנִ֥י יְהֹוָֽה׃
לֹא־תַעֲשׂ֥וּ עָ֙וֶל֙ בַּמִּשְׁפָּ֔ט לֹא־תִשָּׂ֣א פְנֵי־דָ֔ל וְלֹ֥א תֶהְדַּ֖ר פְּנֵ֣י גָד֑וֹל בְּצֶ֖דֶק תִּשְׁפֹּ֥ט עֲמִיתֶֽךָ׃
לֹא־תֵלֵ֤ךְ רָכִיל֙ בְּעַמֶּ֔יךָ לֹ֥א תַעֲמֹ֖ד עַל־דַּ֣ם רֵעֶ֑ךָ אֲנִ֖י יְהֹוָֽה׃
לֹֽא־תִשְׂנָ֥א אֶת־אָחִ֖יךָ בִּלְבָבֶ֑ךָ הוֹכֵ֤חַ תּוֹכִ֙יחַ֙ אֶת־עֲמִיתֶ֔ךָ וְלֹא־תִשָּׂ֥א עָלָ֖יו חֵֽטְא׃
לֹֽא־תִקֹּ֤ם וְלֹֽא־תִטֹּר֙ אֶת־בְּנֵ֣י עַמֶּ֔ךָ וְאָֽהַבְתָּ֥ לְרֵעֲךָ֖ כָּמ֑וֹךָ אֲנִ֖י יְהֹוָֽה׃
\end{otherlanguage}


This is followed by, {הַגָּ֣ר אִתְּכֶ֗ם וְאָהַבְתָּ֥ לוֹ֙ כָּמ֔וֹךָ}.

\end{document}

Kedoshim

7

I once tried to color ligatures for proofreading. I wanted them to stand out better. LaTeX automatically uses ligatures for ffl ffi and such combination of letters. However there are typesetting rules in German where the ligatures are not used. So to actually see where LaTeX inserted ligatures a discussion started on de.comp.text.tex lead to the idea of using Tex's virtual fonts to do the trick.

I managed to do this with the MiKTeX distribution back in 2006, so please bear with me if I don't get all the details right. Basically I changed the glyphs color per virtual fonts.

I used the command line tool vftovp to convert the exisiting .vf files into .vpl files. (virtual property lists) Via testfont /table I checked which ligatures where available in the font and then changed their entries in the .vpl file:

(CHARACTER O 33
    (CHARWD R 0.777781)
    (CHARHT R 0.683332)
    (MAP
        (SPECIAL pdf:direct: 1.0 0 0 rg)
        (SETCHAR O 2)
        (SPECIAL pdf:direct: 0 0 0 rg)
    )
)

then compiled them via vptovf back into .vf files and then pdflatex produced .pdfs with colored ligatures. The two special commands switch the color to red and then back to black. This a very crude hack! But it worked back then. As the whole LaTeX font business is not for the faint-hearted it probably takes some experimenting with the current TeX-distributions.

If you have to create a new virtual font you choose a font you want as a base font. In your case the Hebrew font. As I don't know much about XeTeX I continue this example with a helvetica clone (phvr8r).

tftopl phvr8r.tfm > myfont.vpl

should give you the human readable virtual property lists. Which can now be edited. For example the lines

(MAPFONT D 0 (FONTNAME phvr8r))
(MAPFONT D 1 (FONTNAME pcrr8r))

load phvr8r (our base font) and pcrr8r (Courier). Now you have to know which characters you want to change.

(CHARACTER O 65
   (CHARWD R 0.8)
   (MAP
      (SELECTFONT D 1)
      (SETCHAR O 045)
      (MOVELEFT R 0.200)
      (SELECTFONT D 0)
      (SETCHAR O 051)
      )
   )

This picks character (octal 045) from font 1 moves left a bit and picks character (octal 051) from the other font. I don't know anything about the Hebrew fonts, but if the glyphs are combined like this you could intersperse commands for color changes like in the first example.

Then you create the virtual font (a .vf and a .tfm file) via

vptovf myfont

It's ready for use now. But the tricky part is often that within the TDS (TeX Directory Structure) the font files have to be in the right subdirectory to be found by TeX. And often the filename database (FNDB) of the TeX-distribution must be refreshed after this type of change.

I don't know if XeTeX knows something like plain-TeX, there a simple test file looks like this:

\font\myfont=myfont
\hsize 2in
\myfont
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis hendrerit dictum nisi
a suscipit. Proin velit arcu, tincidunt et gravida ac, pretium vel nunc. Suspendisse
scelerisque malesuada orci, quis volutpat diam ornare nec. Duis ligula est, posuere
non auctor ut, sodales elementum lacus. Nam egestas, diam et varius ornare, nisi
mauris posuere turpis, eu adipiscing diam mauris consequat nulla. Cras id lacus non
ante malesuada sodales. 
\bye

With LaTeX and pdfLaTeX typically a pdftex.map and the font definition files .fd have to be adjusted or created. The best approach is to use the exisiting files from the base font and make small modifications to them to learn about all the pitfalls. The \pdfmapline command could be of some use in the latex document to add your modified font.

The TeX FAQ has an entry about virtual fonts.

4
  • 1
    While this is a nice post, it doesn't answer the question that's about XeTeX and OpenType fonts, for which the technique won't work.
    – egreg
    Nov 17, 2011 at 23:20
  • @egreg The original post doesn't mention OpenType fonts. Is XeTeX limited to OpenType fonts? Or are the hebrew fonts only available in OpenType?
    – uli
    Nov 17, 2011 at 23:28
  • No, it's not limited to OpenType fonts, but I can't see why the OP would use XeTeX otherwise. Let's wait; I didn't mean that you should remove the answer.
    – egreg
    Nov 17, 2011 at 23:37
  • @uli The question appears to be asking about accent signs in Biblical Hebrew distinct from vowels; that is, ta’amim. None of the 8-bit Hebrew encodings used by babel support them.
    – Davislor
    Feb 4, 2022 at 6:38

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .