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

Enhance parallel package with overflow frame alignment, footnote handling and improved documentation #2216

Draft
wants to merge 31 commits into
base: master
Choose a base branch
from
Draft
Changes from 1 commit
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
ab528f7
feat(layout): resize and center main parallel frames
no-vici Jan 15, 2025
1b5ba3c
feat(layout): add parallel frames for footnotes under main frames
no-vici Jan 15, 2025
a112bb0
feat(layout): resize folio frame to accommodate footnotes
no-vici Jan 15, 2025
3c131aa
feat(layout): register footnote frames with the parallel package
no-vici Jan 15, 2025
d4547e3
feat(layout): enhance frame management with footnote support
no-vici Jan 15, 2025
b43a94d
docs(comments): add descriptive comments for typesetter pools and nul…
no-vici Jan 15, 2025
309d258
feat(layout): add utility functions for frame and line height calcula…
no-vici Jan 15, 2025
dc65468
feat(layout): add createDummyContent function for overflow frame hand…
no-vici Jan 15, 2025
6bdb280
feat(layout): add balanceFramesWithDummyContent function for frame he…
no-vici Jan 15, 2025
76dbd72
refactor(layout): improve addBalancingGlue function for frame balancing
no-vici Jan 15, 2025
822ab59
feat(layout): add addParskipToFrames function for flexible vertical f…
no-vici Jan 15, 2025
b39badd
feat(command): add \parskip command for paragraph spacing
no-vici Jan 15, 2025
c956df4
refactor(command): enhance \sync command with parskip handling and mo…
no-vici Jan 15, 2025
e62e758
refactor(layout): enhance parallelPagebreak with overflow handling an…
no-vici Jan 15, 2025
87f5707
chore(package): load necessary packages for footnote handling in para…
no-vici Jan 15, 2025
dbe9129
feat(footnotes): initialize typesetters for footnote frames in parall…
no-vici Jan 15, 2025
55c0d85
feat(footnotes): add \smaller and \footnoteNumber commands for stylin…
no-vici Jan 15, 2025
7d64213
feat(footnotes): add \parallel_footnote:rule command for custom footn…
no-vici Jan 15, 2025
e597937
feat(footnotes): add measureStringWidth function to calculate string …
no-vici Jan 15, 2025
2d399c2
feat(footnotes): add \parallel_footnote:constructor command for footn…
no-vici Jan 15, 2025
12ea0eb
feat(footnotes): add \parallel_footnote command for managing footnote…
no-vici Jan 15, 2025
8784dc6
feat(footnotes): add functions for generating footnote IDs and calcul…
no-vici Jan 15, 2025
22018f9
feat(footnotes): add typesetFootnotes function to handle footnote lay…
no-vici Jan 15, 2025
1099151
docs(parallel): enhance package documentation with detailed explanati…
no-vici Jan 15, 2025
1560349
fix(footnotes): resolve repeating note issue in typesetFootnotes func…
no-vici Jan 16, 2025
c78bde3
feat(logging): add log helper function for streamlined debugging
no-vici Jan 16, 2025
9154935
refactor(layout): improve line height calculation and dummy content g…
no-vici Jan 18, 2025
24e6b6d
refactor(parallel): refactor for modularity and maintainability
no-vici Jan 22, 2025
ccbb79b
style: Reformat Lua with stylua
no-vici Jan 22, 2025
50a5956
fix(footnotes): relocate footnoteManager:typesetFootnotes to parallel…
no-vici Jan 23, 2025
47e73c7
fix(footnotes): fix missing footnotes and footnote marker
no-vici Jan 23, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 21 additions & 15 deletions packages/parallel/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ package._name = "parallel"

-- Helper function for logging
local log = function(...)
local args = {...}
local args = { ... }
SU.debug(package._name, unpack(args))
end

Expand Down Expand Up @@ -63,43 +63,49 @@ end
-- The height calculation includes the glyph heights and the baseline skip setting.
local calculateLineHeight = function(sampleText)
local glyphs = SILE.shaper:shapeToken(sampleText, SILE.font.loadDefaults({}))
local baselineSkip = SILE.settings:get("document.baselineskip").height
return glyphs[1].height + glyphs[2].depth + baselineSkip:tonumber()
local lineSkip = SILE.settings:get("document.lineskip").height
return glyphs[1].height + glyphs[2].depth + lineSkip:tonumber()
end

-- Generate dummy content to fill overflowed frames up to the specified height.
-- Create dummy content to fill up the overflowed frames.
local createDummyContent = function(height, frame, offset)
-- Retrieve the typesetter for the given frame.
-- Get the typesetter for the frame
local typesetter = typesetterPool[frame]

-- Determine the line height using a sample line or fallback to baseline and line skip settings.
-- local lineHeight = calculateLineHeight("hg")
-- Calculate precise line height by typesetting a sample line
-- local lineHeight = calculateLineHeight("hp")

-- If lineHeight cannot be calculated, use document's baselineSkip and lineSkip as fallback.
-- log("Precise lineHeight after simulation = ", lineHeight)

-- If lineHeight could not be calculated, fall back to baselineSkip and lineSkip of the document
if not lineHeight then
local baselineSkip = SILE.settings:get("document.baselineskip").height or SILE.types.length({ length = 0 })
Copy link
Member

@Omikhleia Omikhleia Jan 18, 2025

Choose a reason for hiding this comment

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

SILE.types.length({ length = 0 }) -> you can just write SILE.types.length() (zero by default)

Copy link
Author

Choose a reason for hiding this comment

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

Thank you for your tip. I will use that in the future. I can't find a manual for SILE's internals, and find it hard to get things right. I should come back to farming rather than diving into this crazy field ;)

local lineSkip = SILE.settings:get("document.lineskip").height or SILE.types.length({ length = 0 })
Copy link
Member

Choose a reason for hiding this comment

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

The problem here is that depending on the linespacing algorithm (the default one is more or less the TeX one), the lineskip is used conditionally (when boxes are greater than the baselineskip and would collide vertically), not always. In most cases (i.e. regular text with decent settings for the baselineskip), the lineskip isn't applied.

Copy link
Author

Choose a reason for hiding this comment

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

Unfortunately, the default one doesn't work as expected and eliminating the lineskip makes the frame alignment even worse. With the same thought, I eliminated it in the calculation of the fallback, it messes the alignment up too. Here is a reasonable visual example with lineskip accounted for. Without lineskip, the outcome would look like this:

image

lineHeight = baselineSkip:tonumber() + lineSkip:tonumber()
-- log("Precise lineHeight based on document.baselineskip = ", lineHeight)
end

-- Calculate the number of lines required to fill the specified height.
local numLines = math.floor(height:tonumber() / lineHeight)
-- Calculate the number of lines needed
local requiredLines = height:tonumber() / lineHeight
local numLines = math.floor(requiredLines)
local remainingHeight = requiredLines - numLines

-- Ensure offset is valid; warn if it exceeds the number of lines.
-- Validate offset
offset = offset or 0
if offset >= numLines then
if numLines <= offset then
SU.warn("Offset is larger than the number of lines available; no dummy content will be generated.")
return
end

-- Fill the frame with dummy content using white-colored text to avoid visible output.
-- Add dummy content to fill the frame
SILE.call("color", { color = "white" }, function()
Copy link
Member

@Omikhleia Omikhleia Jan 18, 2025

Choose a reason for hiding this comment

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

Not sure I understand what you are trying with this dummy content, but "white-colored" text is a dubious solution.

  • If you really want N blank lines, one way would be to use zerohbox nodes instead of some dummy text (i.e. IIRC, SILE.typesetter:pushHorizontal(SILE.types.node.zerohbox()). It should work, normally... But if failing report a bug and in the meantime, typeset a non-breaking space in that case (luautf8.char(0x00A0) or one of his friends). Still a dummy text/space, but no need for coloring it ;)
  • Theoretically, the better way would likely be to push a SILE.types.node.vkern() with the appropriate height, directly to the output queue. These nodes exists in the code base, but don't have a decent API and are only used in a fairly bad package (linespacing). I had some notes about them and vglues in general (there's more that meets the eyes here)... I'll look for them and open an issue... Another option in the meantime might, if the vkern does not work well, woulf be to hack a vglue so that its "discardable" and "explicit" properties are both false. That what a vkern should be, with some extra considerations...

Copy link
Author

Choose a reason for hiding this comment

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

I was trying for fun without much knowledge about how SILE handle things behind the scene. Dummy white coloured text was the only solution I could think of, but the dirty way works quite ok. I knew it would not be a proper way to handle overflow.

image

I did not know that SILE.types.node.vkern() can do the job reasonably with some manual adjustment. I will do some test with it and the zerobox.

Very grateful for these tricks!

Copy link
Author

Choose a reason for hiding this comment

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

  • Theoretically, the better way would likely be to push a SILE.types.node.vkern() with the appropriate height, directly to the output queue. These nodes exists in the code base, but don't have a decent API and are only used in a fairly bad package (linespacing). I had some notes about them and vglues in general (there's more that meets the eyes here)... I'll look for them and open an issue... Another option in the meantime might, if the vkern does not work well, woulf be to hack a vglue so that its "discardable" and "explicit" properties are both false. That what a vkern should be, with some extra considerations...

Would you mind having a play with my Parallel Typesetting settings?

I've found that using real dummy text or hard glue works best for me. I’ve tried all of your suggestions, and they work quite well with some manual adjustments in my document, such as adding \skip or phantom text for overflow frames. However, the hard glue patch seems to require the least intervention overall.

My understanding of SILE is still a work in progress, so sometimes I don't know how or why something works when I mess around with it. You flagged it as dubious… 😉 For me, it was just a stroke of luck!

typesetter:typeset("sile")
for i = 1, numLines - offset do
SILE.call("break")
-- Add dummy content and a line break
typesetter:typeset("sile")
SILE.call("break")
end
end)
typesetter:pushExplicitVglue(SILE.types.length(remainingHeight))
end

-- Balance the heights of frames by adding dummy content to shorter frames.
Expand Down