Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hbox fixes for RTL languages #1822

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

mahmadzaid
Copy link

Closes #1796 (for me at least).

I know that bidi.reorder() was deprecated, and here I'm trying to export bidi.splitNodelistIntoBidiRuns() as well, as I'm not able to find a better way.

Packages that call typesetter:makeHbox() and wraps the results somehow are responsible for calling the above two exported functions, as found in the rules and rotate packages, like so:

diff --git a/packages/rules/init.lua b/packages/rules/init.lua
index 22f6885..28bb5d1 100644
--- a/packages/rules/init.lua
+++ b/packages/rules/init.lua
@@ -3,6 +3,8 @@ local base = require("packages.base")
 local package = pl.class(base)
 package._name = "rules"
 
+local bidi = require("packages.bidi")
+
 local function getUnderlineParameters ()
   local ot = require("core.opentype-parser")
   local fontoptions = SILE.font.loadDefaults({})
@@ -211,6 +213,8 @@ function package:registerCommands ()
     SU.deprecated("\\boxaround (undocumented)", "\\framebox (package)", "0.12.0")
 
     local hbox, hlist = SILE.typesetter:makeHbox(content)
+    hbox.value = bidi.splitNodelistIntoBidiRuns(hbox.value, SILE.typesetter)
+    hbox.value = bidi.reorder(nil, hbox.value, SILE.typesetter)
     -- Re-wrap the hbox in another hbox responsible for boxing it at output
     -- time, when we will know the line contribution and can compute the scaled width
     -- of the box, taking into account possible stretching and shrinking.

@mahmadzaid mahmadzaid changed the title Hbox fixes Hbox fixes for RTL languages Jun 25, 2023
@Omikhleia
Copy link
Member

Packages that call typesetter:makeHbox() and wraps the results somehow are responsible for calling the above two exported functions

That's a very strong assumption which is hard to guarantee in all packages that might build and use hboxes, no? I'd expect boxes to work without that kind of weird magic being exposed to calling packages! 🤕

@mahmadzaid
Copy link
Author

Correct, they work fine as they did before, but with no bidi, since, well, bidi is never used on the content, they're already shaped in typesetter:makeHbox(). The manual, which I assume has enough cases, can be built with these changes and seems to look fine where rules and rotate are used.

I tried to handle the split into bidi runs and reordering in the bidi package itself, and it works, but I found that I have to deal with every new way of wrapping hboxes a new package would come up with, for example you have these in the rules package:

-- Re-wrap the hbox in another hbox responsible for boxing it at output
-- time, when we will know the line contribution and can compute the scaled width
-- of the box, taking into account possible stretching and shrinking.
SILE.typesetter:pushHbox({
inner = hbox,
width = hbox.width,

And this in the rotate package:
SILE.typesetter:pushHbox({
value = { orig = origbox, theta = theta},
height = height,

@mahmadzaid
Copy link
Author

mahmadzaid commented Jun 26, 2023

On a similar note, to have this done properly, UBA should be a core part (hopefully in the future), and in our case it would be called in typesetter:makeHbox(), there is this old discussion (#76) for reference.

Sorry if I come off as strongly opinionated 😟

@alerque alerque modified the milestones: v0.14.10, v0.x.y Jun 29, 2023
@Omikhleia
Copy link
Member

Omikhleia commented Jul 16, 2023

Packages that call typesetter:makeHbox() and wraps the results somehow are responsible for calling the above two exported functions (...)

Really, I don't think this would be acceptable (and I'm not going to do it in my 3rd party packages, which do a lot of hbox constructions). We'd need to solve the core issue, not monkey-patch it in unrelated packages.

@mahmadzaid
Copy link
Author

Really, I don't think this would be acceptable (and I'm not going to do it in my 3rd party packages, which do a lot of hbox constructions).

And you don't have to. Your packages should work as they did before.

We'd need to solve the core issue, not monkey-patch it in unrelated packages.

I know it's not ideal, and as I mentioned in my last comment, bidi should be a core part, but since it's an optional package, I didn't think it would be fine to call it from typesetter:makeHbox(), which, has its share of hacks as per the comments.

If nothing else, this is, at least, supposed to fix our basic \hbox with bidi text.

Thank you for taking the time to look into this.

@Omikhleia
Copy link
Member

Omikhleia commented Jul 16, 2023

but since it's an optional package (...)

BTW, bidi being loaded by the plain class, it's unlikely it's that "optional" in many workflows -- most other classes are most likely derived from plain, at least to get the very basic and general commands it provides.

@Omikhleia
Copy link
Member

Omikhleia commented Jul 16, 2023

Plus, bidi overrides typesetter methods when on, and restores them when off...

sile/packages/bidi/init.lua

Lines 235 to 249 in 83d1423

function package:bidiEnableTypesetter (typesetter)
if typesetter.nobidi_boxUpNodes and self.class._initialized then
return SU.warn("BiDi already enabled, nothing to turn on")
end
typesetter.nobidi_boxUpNodes = typesetter.boxUpNodes
typesetter.boxUpNodes = bidiBoxupNodes
end
function package:bidiDisableTypesetter (typesetter)
if not typesetter.nobidi_boxUpNodes and self.class._initialized then
return SU.warn("BiDi not enabled, nothing to turn off")
end
typesetter.boxUpNodes = typesetter.nobidi_boxUpNodes
typesetter.nobidi_boxUpNodes = nil
end

So why not have the weird hacks there, similarly?

@mahmadzaid
Copy link
Author

So why not have the weird hacks there, similarly?

A very good point, I did not think of that. This seems like a much better way.
Please take a look at the last commit. Thank you :)

@alerque
Copy link
Member

alerque commented Oct 6, 2023

Hey @mahmadzaid thanks for the contribution! This came it at a time that I was pretty overwhelmed, but I'm sorry the ball seems to have gotten dropped a little. I'll be giving it a go and figuring out how to get it in the next minor or major release.

Maybe you can answer a question though: I don't see any tests being changed here. Is there a specific test case we can add to our suite that would show what this is fixing and make sure it doesn't break again without warning us?

@Omikhleia Since you reviewed it before do you have anything to add or suggestions about versioning?

local bidiMakeHbox = function (typesetter, content)
local hbox, hlist = typesetter:nobidi_makeHbox(content)
hbox.value = splitNodelistIntoBidiRuns(hbox.value, typesetter)
hbox.value = package.reorder(nil, hbox.value, typesetter)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling a package method with a nil self pointer and bypassing inheritance has heavy code smell.

@Omikhleia
Copy link
Member

@Omikhleia Since you reviewed it before do you have anything to add or suggestions about versioning?

TBH, I don't really understand what it does and how bidi reordering is supposed to work. A review from someone actually using mixed LTR/RTL languages would be welcome. I am a bit embarrassed that bibi is enabled by default via the plain class, so this will be running even in contexts it's not needed, and we have to be careful.

Copy link
Member

@alerque alerque left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I messed with this a bit locally because I'm not sure why the CI runs in this PR didn't even run, but what I found is that it isn't passing regression tests. This doesn't mean it is wrong, the test expectations themselves might actually be wrong. But it wasn't clear to me why they were different and whether the old or new result was correct.

At the very least before we merge this we have to either update the existing expectations with the new results and identify why the changes and note that the new results are the correct expectations, OR make it match the existing expectations.

Failed tests:
❌ tests/bug-1343-dotfill-stretch.sil
❌ tests/bug-1580.sil
❌ tests/bug-192.sil
❌ tests/bug-243.sil
❌ tests/bug-262.sil
❌ tests/bug-479-line-disappear.sil
❌ tests/bug-892.sil
❌ tests/feat-875-leaders-alignment.sil
❌ tests/twoside.sil

@Omikhleia
Copy link
Member

I'm not sure why the CI runs in this PR didn't even run

I think first time contributors don't get the CI to be triggered?

@Omikhleia Omikhleia marked this pull request as draft October 7, 2023 21:41
@alerque
Copy link
Member

alerque commented Oct 7, 2023

I think first time contributors don't get the CI to be triggered?

First time contributors don't trigger CI jobs automatically, but it usually shows repo admins a button to approve them so they run manually. I don't have that for this PR (or perhaps I hit it earlier, I don't remember) the jobs show up as triggered but never finished ("waiting for status to be reported"). That's a bit unusual.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

MWE: \hbox in RTL issue
3 participants