[NTG-context] Count (and limit) glyphs per line?

Benjamin Buchmuller benjamin.buchmuller at gmail.com
Mon Jul 18 23:24:54 CEST 2022


Dear Hans,

This is the friendly reminder you requested for the "crappyspecs" parbuilder as per your example in early July.

With 

ConTeXt  ver: 2022.07.06 21:42 LMTX  fmt: 2022.7.8

I get 

tex error > tex error on line 12 in file ./test-wrapping2.tex: Undefined control sequence \crappyspeccount

potentially as there is no crappyspec parbuilder yet?

\defineparbuilder [crappyspec] % implemented in the builder namespace 
\defineparbuilder [default] % implemented in the builder namespace
\setmainparbuilder[crappyspec]
\setuptolerance[verytolerant,stretch] \dontcomplain
\protected\def\CrappyTraced
{\par \strut \rlap \bgroup\infofont
(\enspace
max = \the\crappyspeccount \quad step = \the\crappyspecstep \quad hsize = \the\hsize \quad used = \the\crappyspecdimen \enspace
)
\egroup \par}
\starttext
\crappyspeccount60 \samplefile{tufte} \CrappyTraced \par
\crappyspeccount40 \samplefile{tufte} \CrappyTraced \par % \crappyspecstep 2pt \samplefile{tufte} \CrappyTraced \par
\samplefile{tufte} \CrappyTraced \startitemize
\startitem
\samplefile{tufte} \CrappyTraced
        \stopitem
        \startitem
\samplefile{ward} \CrappyTraced \stopitem
    \stopitemize
\startnarrower[6*left,right] \samplefile{tufte} \CrappyTraced
    \stopnarrower
\starthanging [distance=4em,n=2] {test} \samplefile{tufte} \CrappyTraced
\stophanging
\page % \stoptext \setuppapersize[landscape,letter]
\samplefile{knuth} \CrappyTraced \startitemize[width=5em]
\startitem
\samplefile{knuth} \CrappyTraced
        \stopitem
        \startitem
{\smallcaps \darkblue \samplefile{knuth}} \CrappyTraced \stopitem
    \stopitemize
\crappyspeccount60 \startitemize[width=5em]
\startitem
\samplefile{knuth} \CrappyTraced
        \stopitem
        \startitem
{\smallcaps \darkgreen \samplefile{knuth}} \CrappyTraced \stopitem
    \stopitemize
    \page



Thank you once again for your help!


Benjamin

--

The problem with these thing is that there is more involved than just counting, like font features, hyphenation, current paragrph properties, etc. and you don't want interference with other features. You also want the paragraphs to look somewhat ok. Folks who enforce such demands on authors never wonder where the tools do do that come from (publishers and probably most designers are not interested in that anyway: thinking probably stops at the number '120').
Attached a proof of concept that gives an idea. No upload as first we need to do some wrapping up of math. Not that we needed something in the engine other than the linebreak helper to accept direct modes (no need to go back and forth then and i only want write this crap once). Could be a module although it's only some 70 lines of code in the end. Maybe it makes a nice (lmtx) demo for the ctx meeting too.
Here we work per paragraph not per line which looks better on the average. One could mess with parshapes but why bother.
(The todo in the name refers to the fact that it might do into a th elow level paragraph manual.)
(No more time now but we can add later; remind me if I forget.)

Hans

-----------------------------------------------------------------
                                          Hans Hagen | PRAGMA ADE
              Ridderstraat 27 | 8061 GH Hasselt | The Netherlands
       tel: 038 477 53 69 | www.pragma-ade.nl | www.pragma-pod.nl
-----------------------------------------------------------------

> On Jun 26, 2022, at 18:32, Max Chernoff <mseven at telus.net> wrote:
> 
> On 2022-06-26 9:59 a.m., Benjamin Buchmuller wrote:
>> Hi Max,
>> Thank you so much for your help and pointing me to the documents; always a lot of things to learn in TeX!
> 
> No problem :)
> 
>> I'm afraid that including the hyphen width doesn't solve the issue yet. It seems to move the problem to other parts of the text.
> 
> Ah, too bad. My next step would have been to insert \penalty10000's (prevent breaks) at the potential breaks before/after the "selected" break, but Hans provided a _much_ better solution that you should use instead.
> 
>> My guess is that one could equivalently have said "local max_length = 111", right?
> 
> Not really; the hyphen is added to the accumulated width, not the accumulated character count.
> 
>> I made the following MWE (reproducible also online) to illustrate what I see:
> 
> My code assumes that each line has _roughly_ "max_length" characters before it runs. These lines each only have ~60 characters, so I'm not entirely surprised that there are issues. Just use Hans's solution, which is much less of a total hack than this is :)
> 
>> * Running with hsize only makes the problem worse in itemizations, so I think localhsize is the way to go. My guess, localhsize is the width of the "text" part of a paragraph, for example, excluding the symbols in the itemization.
> 
> I forgot about \leftskip. Replace "tex.hsize" with "tex.hsize - tex.leftskip.width" and everything should work properly. Using localhsize would also work, whenever it's non-zero.
> 
>> I'm wondering if I do understand the second while loop correctly:
> 
> So how it works is the outer loop goes through each "node" in the paragraph. If it is a glyph or glue, then we increment the character counter by one and the width by the node's width.
> 
> If we have exceeded the maximum character count or maximum width, then we switch directions and start going backwards through each node, starting at the character that was too long. If one of the previous 5 characters is glue, then force a break there; otherwise, we force a break at the nearest glue or hyphen.
> 
> Now we reset the width and character counters and return to the outer loop, which continues until the end of the paragraph.
> 
>> for disc we add hyphen.width, do we?)
> 
> Once we're trying to make a break, it's too late to add anything to the width. Instead, I'm just adding the width of a hyphen unconditionally at the very beginning. (Of course, this code actually has a bug: I add the hyphen width at the very beginning, but then I reset the total width to zero each loop. That's probably why this wasn't working before).
> 
>> * The other cases still seem a bit obscure to me, and I tried to trace where each of them might be triggered:
>>                        if n.id == glue_id then
>>                            local penalty = node.new "penalty"
>>                            penalty.penalty = -10000
>>                            node.insertbefore(head, n, penalty)
>> 						   context.inrightmargin("glue")
>>                            break
>>                        end
> 
> When going backwards, if we find any glue, break there, since breaking at a space is always preferred to breaking at a hyphen.
> 
>>                        if not end_disc and n.id == disc_id then
>> 					   	   context.inrightmargin("disc")
>>                            end_disc = n
>>                        end
> 
> Save the location of the potential hyphen closest to the maximum length, just in case we need it later.
> 
>>                        if end_disc and back_chars >= 5 then
>> 					       context.inrightmargin("end")
>>                            end_disc.penalty = -10000
>>                            break
>>                        end
> 
> We've already went back 5 characters from the maximum length and we haven't found any spaces to break at; if we have already found a potential hyphen, let's force break there.
> 
>>                        if n.id == glyph_id then
>>                            context.inrightmargin("glyph")
>> 						   back_chars = back_chars + 1
>>                        end
> 
> Count how many characters we've went backwards by.
> 
> (Oh, and be really careful when using "context()" inside Lua engine callbacks. If you had done something like "context.vbox('some text')", you would have triggered the paragraph builder while inside the paragraph builder, which could lead to an infinite loop)
> 
>> I'm maybe doing this wrong, but I see these conditions triggered more often than probably expected for a 25 line document?
> 
> Without running this code below, I'd guess that either "glue" or "end" should trigger for every line, "disc" should come before whenever "end" is triggered", and "glyph" is probably triggered about 3 times per line.
> 
>> Many thanks again!
> 
> (self-promotion warning) If these Lua callbacks interest you, I use quite a few of them in my "lua-widow-control" module.
> 
> https://github.com/gucci-on-fleek/lua-widow-control/blob/master/source/lua-widow-control.lua
> 
> There's lots of comments in the code, so hopefully it's not quite as cryptic as my character limiting code.
> 
> Let me know if you have any other questions.
> 
> -- Max



More information about the ntg-context mailing list