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

Max Chernoff mseven at telus.net
Fri Jun 24 07:44:44 CEST 2022


> I've been confronted with the following 'intriguing' formatting requirement for a document:

"Intriguing" is definitely right here. I suspect these guidelines were 
made for typewriters and haven't been updated since.

> to limit the number of glyphs per line to 112. 

112 characters per line sounds much too long anyways.

 From "The Elements of Typographic Style":
 > Anything from 45 to 75 characters is widely regarded as a satisfactory
 > length of line for a single-column page set in a serifed text face
 > in a text size. The 66-character line (counting both letters and
 > spaces) is widely regarded as ideal. For multiple-column work, a
 > better average is 40 to 50 characters.
 >
 > If the type is well set and printed, lines of 85 or 90 characters
 > will pose no problem in discontinuous texts, such as bibliographies,
 > or, with generous leading, in footnotes. But even with generous
 > leading, a line that averages more than 75 or so characters is likely
 > to be too long for continuous reading.

If you use something like

     \setuplayout[width=80\averagecharwidth]

then your lines will for sure have fewer than 112 characters and will 
probably be more readable too.

> I'm nevertheless curious if there is a Lua/TeX solution to this "problem"?

Option 1: Use a monospaced font. Then 112 characters per line <=> page 
width = 112em.

Option 2: A hacky Lua solution

     \startluacode
         local max_length = 112

         local glyph_id = node.id "glyph"
         local disc_id = node.id "disc"
         local glue_id = node.id "glue"

         function userdata.limiter(head)
             language.hyphenate(head)

             local chars = 0
             local width = 0
             local n = head
             while n do
                 if n.id == glyph_id or n.id == glue_id then
                     chars = chars + 1
                     width = width + n.width - (n.shrink or 0)
                 end

                 if chars >= max_length or width > tex.hsize then
                     local back_chars = 0
                     local end_disc = nil

                     while n do
                         if n.id == glue_id then
                             local penalty = node.new "penalty"
                             penalty.penalty = -10000
                             node.insertbefore(head, n, penalty)
                             break
                         end

                         if not end_disc and n.id == disc_id then
                             end_disc = n
                         end

                         if end_disc and back_chars >= 5 then
                             end_disc.penalty = -10000
                             break
                         end

                         if n.id == glyph_id then
                             back_chars = back_chars + 1
                         end

                         n = n.prev
                     end

                     width = 0
                     chars = 0
                 end

                 n = n.next
             end

             return head
         end

         nodes.tasks.appendaction(
             "processors",
             "before",
             "userdata.limiter"
         )
     \stopluacode

     \setuppapersize[landscape,letter]
     \showframe

     \starttext
         \setupalign[flushleft]

         \setupbodyfont[14pt]
         \samplefile{knuth}

         \setupbodyfont[12pt]
         \samplefile{knuth}

         \setupbodyfont[10pt]
         \samplefile{knuth}

         \page
         \setupalign[normal]

         \setupbodyfont[14pt]
         \samplefile{knuth}

         \setupbodyfont[12pt]
         \samplefile{knuth}

         \setupbodyfont[10pt]
         \samplefile{knuth}
     \stoptext

This code will ensure that no line ever exceeds "max_length" characters. 
It uses a greedy algorithm instead of the standard TeX algorithm for 
line breaking, but it still produces mostly decent results.

-- Max


More information about the ntg-context mailing list