sile
sile copied to clipboard
Setting vertical marks
How should something like this be typeset:
This layout is correct (and generated by SILE) but the code for it is too brittle to put in an automatic template. I can massage it given specific content, but with the content being an unknown I'm having a hard time coming up with a template.
Currently I'm dropping the image with a fixed height, then skipping back negative a little more than that height, then adding lskip, then typesetting the 5 lines of data, then turning off the lskip and going back to normal.
SILE.registerCommand("qrimg", function(options, content)
SU.debug("viachristus", SILE.typesetter.frame.state.cursorY)
SILE.call("skip", { height="6.5em" })
SU.debug("viachristus", SILE.typesetter.frame.state.cursorY)
SILE.call("img", { src=qrimg, height="5.8em" })
SILE.call("skip", { height="-6.3em" })
SILE.call("set", { parameter="document.lskip",value="5em" })
end)
qrimg = "./mujdenin_gucu-url.png"
\define[command=meta:identifiers]{
\font[weight=600,style=Bold]{ISBN}: 978-605-66445-1-1\break
\font[weight=600,style=Bold]{E-kitap}: 978-605-66445-0-4\break
\font[weight=600,style=Bold]{Sertifika No}: 16231\break
\font[weight=600,style=Bold]{Sürüm:} \meta:surum\break
\font[weight=600,style=Bold]{URL:} \meta:url\break
}
\define[command=meta:url]{https://yayinlar.viachristus.com/mujdenin_gucu}
\define[command=meta:surum]{mujdenin_gucu@isbn-basvuru-22-g068e74f*}
\qrimg{}
\meta:identifiers{}
\set[parameter=document.lskip]
The trouble is I don't actually know ahead of time how much content there will be. For some instances there might only be three lines of content. Given my current code this would cause the next block to be typeset overlapping the barcode.
I tried using SILE.typesetter.frame.state.cursorY
, but this value does not get updated in between elements like I would need it to be to do math and figure out how much I moved relative to where the image was output. It updates if I call SILE.typesetter:chuck()
, but of course that screws up the page layout, notably the \vfill
at the top of the page that puts this whole thing at the bottom of the page gets eaten.
The nearest thing I found in the docs was the "sidenotes" example using the cursor position, but like I said this doesn't seem to update when I need it to.
What's the best tool for the job here?
Off the top of my head, I would try putting the text into a vbox; have a look at the simple table package. (Yeah, doing layout with tables feels as bad here as it does in HTML, but it's worth a look.) Or you could try doing what \float
does - split into two frames, then join again later. I will try and have a fiddle myself, but that may get you started.
I've tried a couple stabs at this and fallen pretty flat every time. My current hack is to look through the paragraph and count the number of penalties. If it's less than 5 I stuff a couple more in. This works for this particular use but it probably doesn't generalize very well making this a hard problem for users to solve:
if publisher then
SILE.call("skip", { height = "5.4em" })
SILE.settings.temporarily(function ()
SILE.call("img", { src = qrimg, height = "5.8em" })
SILE.call("skip", { height = "-6.3em" })
SILE.settings.set("document.lskip", SILE.nodefactory.newGlue({ width = imgUnit * 6.5 }))
if SILE.Commands["meta:identifiers"] then SILE.call("meta:identifiers") end
SILE.call("font", { weight = 600, style = "Bold" }, { "Sürüm: " })
SILE.call("font", { family = "Hack", size = "0.8em" }, SILE.Commands["meta:surum"])
SILE.call("break")
SILE.call("font", { weight = 600, style = "Bold" }, { "URL: " })
SILE.call("font", { family = "Hack", size = "0.8em" }, SILE.Commands["meta:url"])
-- Hack around not being able to output a vbox with an indent
-- See https://github.com/simoncozens/sile/issues/318
local lines = 1
for i = 1, #SILE.typesetter.state.nodes do
lines = lines + (SILE.typesetter.state.nodes[i]:isPenalty() and 1 or 0)
end
for i = lines, 5 do
SILE.call("hbox")
SILE.call("break")
end
SILE.call("par")
end)
end
The key here is that I don't know how many lines meta:identifiers
will output. It could be 0–3 leaving me needing to output 3-n blank lines to get past the reserved space. There must be a more elegant way to do this.
There would be so many ways to achieve this, with probably variants depending on how one wants to handle all the decisions left undefined in the initial question (e.g. maximum width of the text content, maximum height of the image, edge cases when these get incompatible with each other vs. the available page width, etc. -- and, why not, even min-max both blocks).
Considering the simplest case, though... Let me give it a try, to back my old claim elsewhere it was all doable with the appropriate tools...
LGTM :smiley_cat:
Here are the steps:
luarocks install --server=https://luarocks.org/dev ptable.sile
sile imageatparside.sil
Where imageatparside.sil
is:
\begin[papersize=a5,class=plain]{document}
\neverindent
\use[module=packages.parbox]
\use[module=packages.image]
\font[family=Libertinus Serif,size=12]
\lua{
SILE.registerCommand("textWithImageOnSide", function (options, content)
SILE.call("medskip")
-- Box the paragraphs, with max size = 75%lw or whathever we want
SILE.call("parbox", { width="75%lw", minimize=true, valign="bottom" }, content)
-- Steal it back...
local hbox = table.remove(SILE.typesetter.state.nodes)
-- Measure height
local H = hbox.height
-- Box the image scaled to that height
local imbox = SILE.call("hbox", {}, function ()
SILE.call("img", { src = options.src, height = H })
end)
-- Some horizontal spacing
SILE.call("kern", { width = "1spc" })
--- And then push the paragraph box back
SILE.typesetter:pushHbox(hbox)
SILE.call("medskip")
end)
}
\begin[src=sherlock.png]{textWithImageOnSide}
Lorem\par
Ipsum\par
Dolor sit amet
\end{textWithImageOnSide}
\begin[src=sherlock.png]{textWithImageOnSide}
Lorem\par
Ipsum\par
Dolor sit amet\par
More dolor\par
Ipsum ipsums
\end{textWithImageOnSide}
\end{document}
Does it answer the question?
The suggested answer currently only works well with the boxed paragraph doesn't contain migrating contents (footnotes). "Stealing" content from the typesetter queue is bad otherwise.
As an aside addition, indirectly related to the original example: instead of an image for the QR code, one may be interested in giving a try to qrcode.sile :)
A new version (with the latest "parbox.sile" module at this date), better for two reasons at least:
- Avoids the box stealing trick
- Properly handles footnotes
\begin[papersize=a6,class=book]{document}
\neverindent
\nofolios
\use[module=packages.parbox]
\use[module=packages.image]
\font[family=Libertinus Serif,size=12]
\lua{
-- DOH, we cannot use SILE.registerCommand here without deprecation warning
-- so we have to retrieve the class instance with this long-winded way
local class = SILE.documentState.documentClass
-- DOH "\use" above doesn't seem to load the package into the class:
-- local pboxer = class.packages.parbox -- NOPE!
local pboxer = SILE.require("packages.parbox") -- ERM, works, but double-loaded in a way?
-- OR (long-winded too):
-- class:loadPackage("parbox") -- ERM, works, but double-loaded in a way?
-- local pboxer = class.packages.parbox -- Aye, it's here now!
class:registerCommand("textWithImageOnSide", function (options, content)
SILE.call("medskip")
-- Box the paragraphs, with max size = 75%lw or whathever we want
local hbox, hlist = pboxer:makeParbox({ width="75%lw", minimize=true, valign="bottom" }, content)
-- Measure height
local H = hbox.height
-- Box the image scaled to that height
local imbox = SILE.call("hbox", {}, function ()
SILE.call("img", { src = options.src, height = H })
end)
-- Some horizontal spacing
SILE.call("kern", { width = "1spc" })
-- And then push the paragraph box back...
SILE.typesetter:pushHbox(hbox)
-- ... As well as any migrating content from within it, just after.
for _, h in ipairs(hlist) do
SILE.typesetter:pushHorizontal(h)
end
SILE.call("medskip")
end)
}
\begin[src=sherlock.png]{textWithImageOnSide}
Lorem\footnote{This is a footnote}\par
Ipsum\par
Dolor sit amet
\end{textWithImageOnSide}
\begin[src=sherlock.png]{textWithImageOnSide}
Lorem\par
Ipsum\par
Dolor sit amet\par
More dolor\par
Ipsum ipsums
\end{textWithImageOnSide}
\end{document}
Comments welcome, especially on how to properly handle the "DOH" commented sections in such examples. (It works, but is rather long-winded... Of course, moving the Lua bit in some package or dedicated class would avoid it all.)
Fixing #1510 should address most of this. The deprecation warnings thrown in this context are bogus because the context for their registration is known. I was trying to get unscoped registrations out out of packages, not out of documents! As an alternative you can use a \lua{}
block (or more logically a \use{}
block to return a module that is basically a lambda package. I'll try to drop an example soon because that needs documenting.