Returning to LyX

Udi Fogiel via lyx-devel <[email protected]> Sun, 05 Jul 2026 15:03:53 +0000
Newsgroups gmane.editors.lyx.devel
Message-ID <wfFg0zOL6gIUhBvIacze1NemhN3Y5Mq7u73ngDcikxaPt6IaA4_pKD_Wtul_VFnSz2SXf97ZhLjic4ZPb7WEcqdnm-p_DjQCuQOCzbGspS8=@proton.me>
Hi all,

After a long absence, I'd like to return to LyX development. I had to step away several years ago because of personal issues, and I disappeared without saying anything. I'm sorry for that.

I'd like to get back involved and will first catch up on the changes that have happened while I was away. If that is okay with everyone, let me know if there are areas where help is currently needed.

Note that my gmail account no longer exists, so
I'm not reachable in my older address. [email protected]
is my current address. Also I don't have access to the bug tracker.

I attached several patches reflecting changes upstream.

* The first patch reflect a change made in LaTeX's 2026 June release. Tubulars no longer use math around them: https://github.com/latex3/latex2e/pull/2008/changes/97ebd0eedf8a0f4d68bc8ae194e44f2a1abeac67, and this affect columns and text order.

* The second patch fixes a mistake I made in fe57b19294, delimiters mirroring is something that should be handled by the font renderer. For example, if one choose to use harfbuzz the delimiters are mirrored by luaotfload, and then the output with LyX is wrong.

* The third one refelects the fact that now polyglossia in LuaTeX uses unibidi-lua for UBA, so short LTR strings embedded in RTL are visually ordered without an explicit markup

* The fourth one reflect changes in babel-hebrew, and should fix #5927 and #7797.

* The fifth is about the discussion at https://github.com/LaTeX-Package-Repositories/geometry/issues/20 (but now that I compose this email I realize LyX use lmargin/rmargin even with two sided documents, there the margins should not be flipped because RTL books are opened from right to left, so

bool const luatex_rtl = (rp.flavor == Flavor::LuaTeX
                         || rp.flavor == Flavor::DviLuaTeX)
                        && language->rightToLeft();

should be

bool const luatex_rtl = (rp.flavor == Flavor::LuaTeX
                         || rp.flavor == Flavor::DviLuaTeX)
                        && language->rightToLeft()
                        && sides == OneSide;

)

* the sixth is also about a change in babel-hebrew

Best,
Udi

-- 
lyx-devel mailing list
[email protected]
https://lists.lyx.org/mailman/listinfo/lyx-devel
0005-mirror-left-and-right-margins-in-LuaTeX-with-main-RT.patch (application/octet-stream, 1.8 KB)
From 4c49ec0486939cfeb3669ddc1252ea086a615ecd Mon Sep 17 00:00:00 2001
From: udifogiel <[email protected]>
Date: Fri, 26 Jun 2026 12:50:49 +0300
Subject: [PATCH 5/6] mirror left and right margins in LuaTeX with main RTL
 language

see https://github.com/LaTeX-Package-Repositories/geometry/issues/20
for further details
---
 src/BufferParams.cpp | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/src/BufferParams.cpp b/src/BufferParams.cpp
index 88c37e26a4..226e9bcfbb 100644
--- a/src/BufferParams.cpp
+++ b/src/BufferParams.cpp
@@ -2525,14 +2525,22 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features,
 				gs << "," << g_options;
 			// output this only if use_geometry is true
 			if (use_geometry) {
+				// In LuaTeX with RTL main language, lmargin/rmargin are
+				// reversed relative to the user's visual intent.
+				OutputParams const & rp = features.runparams();
+				bool const luatex_rtl = (rp.flavor == Flavor::LuaTeX
+				                         || rp.flavor == Flavor::DviLuaTeX)
+				                        && language->rightToLeft();
 				if (!topmargin.empty())
 					gs << ",tmargin=" << Length(topmargin).asLatexString();
 				if (!bottommargin.empty())
 					gs << ",bmargin=" << Length(bottommargin).asLatexString();
 				if (!leftmargin.empty())
-					gs << ",lmargin=" << Length(leftmargin).asLatexString();
+					gs << "," << (luatex_rtl ? "rmargin" : "lmargin")
+					   << "=" << Length(leftmargin).asLatexString();
 				if (!rightmargin.empty())
-					gs << ",rmargin=" << Length(rightmargin).asLatexString();
+					gs << "," << (luatex_rtl ? "lmargin" : "rmargin")
+					   << "=" << Length(rightmargin).asLatexString();
 				if (!headheight.empty())
 					gs << ",headheight=" << Length(headheight).asLatexString();
 				if (!headsep.empty())
-- 
2.43.0
0001-update-RTL-tabular-code.patch (application/octet-stream, 2.9 KB)
From 1721b8325c4b9e620f3156bee8c519c435c081b6 Mon Sep 17 00:00:00 2001
From: udifogiel <[email protected]>
Date: Fri, 26 Jun 2026 07:14:39 +0300
Subject: [PATCH 1/6] update RTL tabular code

The LaTeX kernel don't use math in tabulars anymore,
so currently tabulars are typset with RTL columns in
RTL context, in all engines. There is also no need to wrap
the cell content with a direction command.
---
 src/insets/InsetTabular.cpp | 31 ++++---------------------------
 1 file changed, 4 insertions(+), 27 deletions(-)

diff --git a/src/insets/InsetTabular.cpp b/src/insets/InsetTabular.cpp
index 51df839cde..66b1a57e70 100644
--- a/src/insets/InsetTabular.cpp
+++ b/src/insets/InsetTabular.cpp
@@ -3485,12 +3485,10 @@ void Tabular::TeXRow(otexstream & os, row_type row,
 	bool const row_colored = row_info[row].color != "default"
 			&& lcolor.isKnownLyXName(row_info[row].color);
 
-	// The bidi package (loaded by polyglossia with XeTeX) reverses RTL table columns
-	// Luabibdi (used by LuaTeX) behaves like classic
+	// RTL table columns are always reversed
 	bool const bidi_rtl =
 		runparams.local_font
-		&& runparams.local_font->isRightToLeft()
-		&& buffer().params().useBidiPackage(runparams);
+		&& runparams.local_font->isRightToLeft();
 	bool const ct = !buffer().params().output_changes;
 	idx_type lastcell =
 		bidi_rtl ? getFirstCellInRow(row, ct) : getLastCellInRow(row, ct);
@@ -3566,23 +3564,6 @@ void Tabular::TeXRow(otexstream & os, row_type row,
 
 		os.texrow().forceStart(par.id(), 0);
 
-		bool rtl = par.isRTL(buffer().params())
-			&& !par.empty()
-			&& getPWidth(cell).zero()
-			&& !runparams.isFullUnicode();
-
-		if (rtl) {
-			string const lang =
-				par.getParLanguage(buffer().params())->lang();
-			if (lang == "farsi")
-				os << "\\textFR{";
-			else if (lang == "arabic_arabi")
-				os << "\\textAR{";
-			// currently, remaining RTL languages are
-			// arabic_arabtex and hebrew
-			else
-				os << "\\R{";
-		}
 		// pass to the OutputParams that we are in a cell and
 		// which alignment we have set.
 		// InsetNewline needs this context information.
@@ -3639,8 +3620,6 @@ void Tabular::TeXRow(otexstream & os, row_type row,
 		}
 
 		runparams.encoding = newrp.encoding;
-		if (rtl)
-			os << '}';
 
 		if (color_last_multirow)
 			os << '}';
@@ -3712,12 +3691,10 @@ void Tabular::latex(otexstream & os, OutputParams const & runparams) const
 			os << "\\begin{turn}{" << convert<string>(rotate) << "}\n";
 	}
 
-	// The bidi package (loaded by polyglossia with XeTeX) swaps the column
-	// order for RTL (#9686). Thus we use this list.
+	// RTL table columns are always reversed. Thus we use this list.
 	bool const bidi_rtl =
 		runparams.local_font
-		&& runparams.local_font->isRightToLeft()
-		&& buffer().params().useBidiPackage(runparams);
+		&& runparams.local_font->isRightToLeft();
 	list<col_type> columns;
 	list<col_type> logical_columns;
 	for (col_type cl = 0; cl < ncols(); ++cl) {
-- 
2.43.0
0004-use-LRE-RLE-instead-of-L-R-with-babel-hebrew.patch (application/octet-stream, 2.7 KB)
From 60f2db01b31d5278eacdc6b452c862edcbc0dcd8 Mon Sep 17 00:00:00 2001
From: udifogiel <[email protected]>
Date: Fri, 26 Jun 2026 08:35:46 +0300
Subject: [PATCH 4/6] use \LRE/\RLE instead of \L/\R with babel-hebrew

This avoids a conflict with hyperref.
This together with upstream fixes #5927.
---
 src/Font.cpp                    | 18 ++++++++++++++----
 src/Paragraph.cpp               |  2 +-
 src/insets/InsetSpecialChar.cpp |  2 ++
 src/output_latex.cpp            |  4 ++--
 4 files changed, 19 insertions(+), 7 deletions(-)

diff --git a/src/Font.cpp b/src/Font.cpp
index 93ee481aaa..35a762ccd4 100644
--- a/src/Font.cpp
+++ b/src/Font.cpp
@@ -305,11 +305,21 @@ int Font::latexWriteStartChanges(otexstream & os, BufferParams const & bparams,
 				count += 9;
 			}
 			if (isRightToLeft()) {
-				os << "\\R{";
-				count += 3;
+				if (language()->lang() == "hebrew") {
+					os << "\\RLE{";
+					count += 5;
+				} else {
+					os << "\\R{";
+					count += 3;
+				}
 			} else {
-				os << "\\L{";
-				count += 3;
+				if (base.language()->lang() == "hebrew") {
+					os << "\\LRE{";
+					count += 5;
+				} else {
+					os << "\\L{";
+					count += 3;
+				}
 			}
 		} else if (!language()->babel().empty()) {
 			string const tmp =
diff --git a/src/Paragraph.cpp b/src/Paragraph.cpp
index 11981ec707..eb1f5043e2 100644
--- a/src/Paragraph.cpp
+++ b/src/Paragraph.cpp
@@ -1121,7 +1121,7 @@ void Paragraph::Private::latexInset(BufferParams const & bparams,
 			close_brace = 1;
 		} else {
 			// babel classic
-			os << "\\L{";
+			os << "\\LRE{";
 			if (disp_env)
 				os << safebreakln;
 			close_brace = 1;
diff --git a/src/insets/InsetSpecialChar.cpp b/src/insets/InsetSpecialChar.cpp
index 95594c11bf..9460acb584 100644
--- a/src/insets/InsetSpecialChar.cpp
+++ b/src/insets/InsetSpecialChar.cpp
@@ -269,6 +269,8 @@ void InsetSpecialChar::latex(otexstream & os, OutputParams const & rp) const
 		if (getLocalOrDefaultLang(rp)->lang() == "arabic_arabi"
 		    || getLocalOrDefaultLang(rp)->lang() == "farsi")
 			lswitch = "\\textLR{";
+		else if (getLocalOrDefaultLang(rp)->lang() == "hebrew")
+			lswitch = "\\LRE{";
 	}
 
 	if (sc_.need_protect && rp.moving_arg)
diff --git a/src/output_latex.cpp b/src/output_latex.cpp
index f16a1c6199..3b92d83ea2 100644
--- a/src/output_latex.cpp
+++ b/src/output_latex.cpp
@@ -1122,9 +1122,9 @@ void TeXOnePar(Buffer const & buf,
 					os << "\\textLR{";
 				// remaining RTL languages currently is hebrew
 				else if (par_language->rightToLeft() && !runparams.isFullUnicode())
-					os << "\\R{";
+					os << "\\RLE{";
 				else
-					os << "\\L{";
+					os << "\\LRE{";
 				++state->LR_switch_;
 			}
 			// With CJK, the CJK tag has to be closed first (see below)
-- 
2.43.0
0002-don-t-mirror-delimiters-in-unicode-engines.patch (application/octet-stream, 2.3 KB)
From 34919dd8c24539de5210935ce402784bcc45376e Mon Sep 17 00:00:00 2001
From: udifogiel <[email protected]>
Date: Fri, 26 Jun 2026 07:49:08 +0300
Subject: [PATCH 2/6] don't mirror delimiters in unicode engines

mirroring is applied at the font level with correct
setup. This is not something that LyX can track or should
bother with. For example luaotfload applies mirroring
if HarfBuzz is used, so LyX should not mirror in this case,
but it currently does.
---
 src/Paragraph.cpp | 22 ++++++++--------------
 1 file changed, 8 insertions(+), 14 deletions(-)

diff --git a/src/Paragraph.cpp b/src/Paragraph.cpp
index 4ea7bbd633..044bdb3ea5 100644
--- a/src/Paragraph.cpp
+++ b/src/Paragraph.cpp
@@ -2081,11 +2081,9 @@ char_type Paragraph::getUChar(BufferParams const & bparams,
 
 	// Return unchanged character
 	// 1. in all LTR languages
-	// 2. if we use XeTeX (both with babel and polyglossia)
-	// 3. if we use LuaTeX with babel
+	// 2. in unicode engines (XeTeX, LuaTeX)
 	if (!getFontSettings(bparams, pos).isRightToLeft()
-	    || rp.flavor == Flavor::XeTeX
-	    || (rp.use_babel && rp.flavor == Flavor::LuaTeX))
+	    || rp.isFullUnicode())
 		return c;
 
 	// For the remaining cases, we need to account for some special cases.
@@ -2108,16 +2106,12 @@ char_type Paragraph::getUChar(BufferParams const & bparams,
 	char_type uc = c;
 
 	// These are the cases where we need to mirror delimiters in RTL context
-	// in the remaining cases (polyglossia + LuaTeX or classic [pdf]latex):
-	// 1. With polyglossia and LuaTeX (luabidi) parentheses and brackets
-	//    need to be mirrored in RTL, regardless of the language, or script.
-	// 2. In the languages that follow, parentheses need to be mirrored
-	//    in classic (pdf)latex
-	bool const reverseparens = (rp.use_polyglossia || lang == "hebrew");
-	// 3. In all RTL languages except for those that follow, brackets
-	//    need to be mirrored in classic (pdf)latex
-	bool const reversebrackets = rp.use_polyglossia
-			|| (lang != "arabic_arabtex"
+	// in classic (pdf)latex:
+	// 1. In the languages that follow, parentheses need to be mirrored
+	bool const reverseparens = (lang == "hebrew");
+	// 2. In all RTL languages except for those that follow, brackets
+	//    need to be mirrored
+	bool const reversebrackets = (lang != "arabic_arabtex"
 			    && lang != "arabic_arabi"
 			    && lang != "farsi");
 
-- 
2.43.0
0006-avoid-colorbox-patch-with-babel-hebrew.patch (application/octet-stream, 927 B)
From 45a5045051df550082a161a0c50508e90129cc94 Mon Sep 17 00:00:00 2001
From: udifogiel <[email protected]>
Date: Fri, 26 Jun 2026 15:08:22 +0300
Subject: [PATCH 6/6] avoid colorbox patch with babel-hebrew

not needed anymore.
---
 src/insets/InsetBox.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/insets/InsetBox.cpp b/src/insets/InsetBox.cpp
index e1a1b0aa05..f927a3bbcd 100644
--- a/src/insets/InsetBox.cpp
+++ b/src/insets/InsetBox.cpp
@@ -349,7 +349,8 @@ void InsetBox::latex(otexstream & os, OutputParams const & runparams) const
 	string maybeEndL;
 	bool needEndL = false;
 	if (!runparams.isFullUnicode()
-	    && runparams.local_font && runparams.local_font->isRightToLeft()) {
+	    && runparams.local_font && runparams.local_font->isRightToLeft()
+	    && runparams.local_font->language()->lang() != "hebrew") {
 		maybeBeginL = "\\beginL";
 		maybeEndL = "\\endL";
 	}
-- 
2.43.0
0003-remove-insline-forceLTR-decorations-with-polyglossia.patch (application/octet-stream, 3.8 KB)
From db901e58090cb047b483d903ea55765f1e544719 Mon Sep 17 00:00:00 2001
From: udifogiel <[email protected]>
Date: Fri, 26 Jun 2026 08:16:29 +0300
Subject: [PATCH 3/6] remove insline forceLTR decorations with
 polyglossia+LuaTeX

polyglossia now use unibidi-lua which handles these situations.
display environments are still wraped with \begin{LTR}...\end{LTR}.
---
 src/Font.cpp      | 30 ++++++++----------------------
 src/Paragraph.cpp | 12 ++++--------
 2 files changed, 12 insertions(+), 30 deletions(-)

diff --git a/src/Font.cpp b/src/Font.cpp
index a0c4820a05..93ee481aaa 100644
--- a/src/Font.cpp
+++ b/src/Font.cpp
@@ -428,9 +428,9 @@ int Font::latexWriteStartChanges(otexstream & os, BufferParams const & bparams,
 	}
 	*/
 	// If the current language is Hebrew, Arabic, or Farsi
-	// the numbers are written Left-to-Right. ArabTeX package
-	// and bidi (polyglossia with XeTeX) reorder the number automatically
-	// but the packages used for Hebrew and Farsi (Arabi) do not.
+	// the numbers are written Left-to-Right. Unicode engines and ArabTeX
+	// reorder numbers automatically, but the packages used for Hebrew
+	// and Farsi (Arabi) in classic (pdf)latex do not.
 	if (!bparams.useBidiPackage(runparams)
 	    && !runparams.pass_thru
 	    && bits_.number() == FONT_ON
@@ -438,14 +438,7 @@ int Font::latexWriteStartChanges(otexstream & os, BufferParams const & bparams,
 	    && (language()->lang() == "hebrew"
 		|| language()->lang() == "farsi"
 		|| language()->lang() == "arabic_arabi")) {
-		if (runparams.use_polyglossia) {
-			// LuaTeX/luabidi
-			// \LR needs extra grouping
-			// (possibly a LuaTeX bug)
-			os << "{\\LR{";
-			count += 6;
-		} else if (!runparams.isFullUnicode()) {
-			// not needed with babel/lua|xetex
+		if (!runparams.isFullUnicode()) {
 			os << "{\\beginL ";
 			count += 9;
 		}
@@ -606,9 +599,9 @@ int Font::latexWriteEndChanges(otexstream & os, BufferParams const & bparams,
 	}
 
 	// If the current language is Hebrew, Arabic, or Farsi
-	// the numbers are written Left-to-Right. ArabTeX package
-	// and bidi (polyglossia with XeTeX) reorder the number automatically
-	// but the packages used for Hebrew and Farsi (Arabi) do not.
+	// the numbers are written Left-to-Right. Unicode engines and ArabTeX
+	// reorder numbers automatically, but the packages used for Hebrew
+	// and Farsi (Arabi) in classic (pdf)latex do not.
 	if (!bparams.useBidiPackage(runparams)
 	    && !runparams.pass_thru
 	    && bits_.number() == FONT_ON
@@ -616,14 +609,7 @@ int Font::latexWriteEndChanges(otexstream & os, BufferParams const & bparams,
 	    && (language()->lang() == "hebrew"
 		|| language()->lang() == "farsi"
 		|| language()->lang() == "arabic_arabi")) {
-		if (runparams.use_polyglossia) {
-			// LuaTeX/luabidi
-			// luabidi's \LR needs extra grouping
-			// (possibly a LuaTeX bug)
-			os << "}}";
-			count += 2;
-		} else if (!runparams.isFullUnicode()) {
-			// not needed with babel/lua|xetex
+		if (!runparams.isFullUnicode()) {
 			os << "\\endL}";
 			count += 6;
 		}
diff --git a/src/Paragraph.cpp b/src/Paragraph.cpp
index 044bdb3ea5..11981ec707 100644
--- a/src/Paragraph.cpp
+++ b/src/Paragraph.cpp
@@ -1109,16 +1109,12 @@ void Paragraph::Private::latexInset(BufferParams const & bparams,
 			if (disp_env) {
 				os << "\\begin{LTR}";
 				close_env = "LTR";
-			} else {
-				if (runparams.flavor == Flavor::LuaTeX) {
-					// luabidi's \LRE needs extra grouping
-					// (possibly a LuaTeX bug)
-					os << '{';
-					close_brace = 1;
-				}
+			} else if (bparams.useBidiPackage(runparams)) {
+				// XeTeX/bidi: wrap inline insets
 				os << "\\LRE{";
-				close_brace += 1;
+				close_brace = 1;
 			}
+			// luabidi: inline insets are not wrapped
 		} else if (running_font.language()->lang() == "farsi"
 			 || running_font.language()->lang() == "arabic_arabi") {
 			os << "\\textLR{" << termcmd;
-- 
2.43.0