8

This is my first time playing with ConText and, while I'd like to think I have some decent programming experience, TeX code is something I've never worked with before (and am sufficiently intimidated by).

Long story short, I'm trying to get a three-column layout with vertical rules separating each column, like the journal Science:

enter image description here

I've played a bit around with columnsets and pagecolumns (as suggested on the wiki), but I'm struggling to get the rules how I'd like them. The wiki suggests using some METAFUN code for drawing rules, but unfortunately it's not quite what I'm looking for:

  1. There is an unneeded rule on the right side of the page – for N number of columns, I'll always want N - 1 rules separating them
  2. The rules continue past the end of the text, even drawing when there is no text in a column at all!

Here is where I'm at:

% Margins
\setuplayout[
  backspace=13mm, width=middle,
  topspace=13mm, height=middle,
  header=0mm, footer=0mm]

% Page Columns
\startuseMPgraphic{verticalrule}
  if CurrentColumn < 3 :
    draw rightboundary OverlayBox shifted (4mm,0);
    setbounds currentpicture to OverlayBox enlarged 2.5mm;
  fi
\stopuseMPgraphic

\defineoverlay
  [verticalrule]
  [\useMPgraphic{verticalrule}]

\startpagecolumns[n=3,background=verticalrule]

Gives me: enter image description here And on the second page, showing off the second issue: enter image description here

I'm honestly struggling with getting the height of the actual text (not the whole text area, but the non-blank space) and can't quite figure out a way to get around the extra rule during my playing around with METAFUN. Maybe I need this all to be a page background and not a column overlay? How would that help with my issue #2 though?

I'd greatly appreciate any help you could offer!

Thanks in advance!

Brooks

EDIT:

Here is some code for a minimal working example! The text is a bit bigger and has some ragged bottom edges (what's that all about?) after I removed the custom font:

% Common Imports
\usemodule[ipsum]

% Basic Document Setup
\mainlanguage[en]
\setuppapersize[A4]

% Margins
\setuplayout[
  backspace=13mm, width=middle,
  topspace=13mm, height=middle,
  header=0mm, footer=0mm]

% Page Columns
\startuseMPgraphic{verticalrule}
  if CurrentColumn < 3 :
    draw rightboundary OverlayBox shifted (4mm,0);
    setbounds currentpicture to OverlayBox enlarged 2.5mm;
  fi
\stopuseMPgraphic

\defineoverlay
  [verticalrule]
  [\useMPgraphic{verticalrule}]

\startpagecolumns[n=3,background=verticalrule]

% Dummy Text
\starttext
\setupalign[tolerant,stretch,hanging,hz]
\ipsum
\ipsum

\ipsum
\ipsum

\ipsum

\ipsum
\ipsum

\ipsum
\stoptext

DOUBLE EDIT: Here is my output for context -version:

mtx-context     | ConTeXt Process Management 1.04
mtx-context     |
mtx-context     | main context file: /usr/share/texmf-dist/tex/context/base/mkiv/context.mkiv
mtx-context     | current version: 2021.03.05 19:11
mtx-context     | main context file: /usr/share/texmf-dist/tex/context/base/mkxl/context.mkxl
mtx-context     | current version: 2021.03.05 19:11

And when I run Wolfgang's suggested code with context Article.tex:

resolvers       | formats | executing runner 'run luatex format': /usr/bin/luatex --jobname="Article" --fmt=/home/tll/.texlive/texmf-var/luatex-cache/context/0399a8df3aef8d154781d0a9c2b8e28d/formats/luatex/cont-en.fmt --lua=/home/tll/.texlive/texmf-var/luatex-cache/context/0399a8df3aef8d154781d0a9c2b8e28d/formats/luatex/cont-en.lui cont-yes.mkiv --c:currentrun=1 --c:fulljobname="./Article.tex" --c:input="./Article.tex" --c:kindofrun=1 --c:maxnofruns=9 --c:texmfbinpath="/usr/bin"
This is LuaTeX, Version 1.15.0 (TeX Live 2022/Arch Linux) 
 system commands enabled.
open source     > level 1, order 1, name '/usr/share/texmf-dist/tex/context/base/mkiv/cont-yes.mkiv'
system          > 
system          > ConTeXt  ver: 2021.03.05 19:11 MKIV  fmt: 2022.5.17  int: english/english
system          > 
system          > 'cont-new.mkiv' loaded
open source     > level 2, order 2, name '/usr/share/texmf-dist/tex/context/base/mkiv/cont-new.mkiv'
system          > beware: some patches loaded from cont-new.mkiv
close source    > level 2, order 2, name '/usr/share/texmf-dist/tex/context/base/mkiv/cont-new.mkiv'
system          > files > jobname 'Article', input './Article', result 'Article'
fonts           > latin modern fonts are not preloaded
languages       > language 'en' is active
open source     > level 2, order 3, name '/home/spinning/tll/Documents/University/Y4S2/Literature Review/ConTeXt (RIP)/Article.tex'
fonts           > preloading latin modern fonts (second stage)
fonts           > 'fallback modern-designsize rm 12pt' is loaded

Underfull \hbox (badness 1342) in paragraph at lines 1--36
<1: lmroman12-regular @ 12.0pt> sem-per quis fringilla ut, viverra

Underfull \hbox (badness 1342) in paragraph at lines 1--36
<1: lmroman12-regular @ 12.0pt> sem-per quis fringilla ut, viverra

Underfull \hbox (badness 1342) in paragraph at lines 1--36
<1: lmroman12-regular @ 12.0pt> sem-per quis fringilla ut, viverra

Underfull \hbox (badness 1342) in paragraph at lines 1--36
<1: lmroman12-regular @ 12.0pt> sem-per quis fringilla ut, viverra

Underfull \hbox (badness 1342) in paragraph at lines 1--36
<1: lmroman12-regular @ 12.0pt> sem-per quis fringilla ut, viverra

Underfull \hbox (badness 1342) in paragraph at lines 1--36
<1: lmroman12-regular @ 12.0pt> sem-per quis fringilla ut, viverra

Underfull \hbox (badness 1342) in paragraph at lines 1--36
<1: lmroman12-regular @ 12.0pt> sem-per quis fringilla ut, viverra

Underfull \hbox (badness 1342) in paragraph at lines 1--36
<1: lmroman12-regular @ 12.0pt> sem-per quis fringilla ut, viverra

Underfull \hbox (badness 1342) in paragraph at lines 1--36
<1: lmroman12-regular @ 12.0pt> sem-per quis fringilla ut, viverra
metapost        > initializing instance 'metafun:1' using format 'metafun' and method 'default'
metapost        > loading 'metafun' as '/usr/share/texmf-dist/metapost/context/base/mpiv/metafun.mpiv' using method 'default'
metapost        > initializing number mode 'scaled'
backend         > xmp > using file '/usr/share/texmf-dist/tex/context/base/mkiv/lpdf-pdx.xml'
pages           > flushing realpage 1, userpage 1, subpage 1
pages           > flushing realpage 2, userpage 2, subpage 2
close source    > level 2, order 3, name '/home/spinning/tll/Documents/University/Y4S2/Literature Review/ConTeXt (RIP)/Article.tex'
close source    > level 1, order 3, name '/usr/share/texmf-dist/tex/context/base/mkiv/cont-yes.mkiv'

mkiv lua stats  > used config file: selfautodir:/share/texmf-dist/web2c/texmfcnf.lua
mkiv lua stats  > used cache path: readable+writable: '/home/tll/.texlive/texmf-var/luatex-cache/context/0399a8df3aef8d154781d0a9c2b8e28d'
mkiv lua stats  > resource resolver: loadtime 0.055 seconds, 0 scans with scantime 0.000 seconds, 0 shared scans, 31 found files, scanned paths: <none>
mkiv lua stats  > stored bytecode data: 454 modules (0.176 sec), 96 tables (0.008 sec), 550 chunks (0.183 sec)
mkiv lua stats  > traced context: maxstack: 1422, freed: 2, unreachable: 1420
mkiv lua stats  > cleaned up reserved nodes: 57 nodes, 9 lists of 442
mkiv lua stats  > node memory usage: 17 attribute, 6 attribute_list, 3 glue, 51 glue_spec, 3 kern, 4 penalty, 2 temp, 2 user_defined
mkiv lua stats  > node list callback tasks: 8 unique task lists, 7 instances (re)created, 374 calls
mkiv lua stats  > driver time: 0.050 seconds 
mkiv lua stats  > used backend: pdf (backend for directly generating pdf output)
mkiv lua stats  > jobdata time: 0.001 seconds saving, 0.000 seconds loading
mkiv lua stats  > callbacks: internal: 842, file: 229, direct: 2, late: 18, function 627, total: 1718 (859 per page)
mkiv lua stats  > randomizer: resumed with value 0.47691170172766
mkiv lua stats  > positions: 3 collected, 3 regular, 0 special
mkiv lua stats  > result saved in file: Article.pdf, compresslevel 3, objectcompresslevel 1
mkiv lua stats  > loaded patterns: en::1, load time: 0.000
mkiv lua stats  > loaded fonts: 2 files: latinmodern-math.otf, lmroman12-regular.otf
mkiv lua stats  > font engine: otf 3.113, afm 1.513, tfm 1.000, 7 instances, 3 shared in backend, 3 common vectors, 0 common hashes, load time 0.106 seconds 
mkiv lua stats  > metapost: 0.001 seconds, loading: 0.010, execution: 0.001, n: 8, average: 0.001, instances: 1, luacalls: 56, memory: 2.851 M
mkiv lua stats  > used platform: linux-64, type: unix, binary subtree: usr
mkiv lua stats  > used engine: luatex version: 1.15, functionality level: 7509, banner: this is luatex, version 1.15.0 (tex live 2022/arch linux)
mkiv lua stats  > used hash slots: 46520 of 65536 + 200000
mkiv lua stats  > lua properties: engine: lua 5.3, used memory: 147 MB, ctx: 147 MB, max: unknown MB, hash chars: min(64,40), symbol mask: utf (τεχ)
mkiv lua stats  > runtime: 0.396 seconds, 2 processed pages, 2 shipped pages, 5.051 pages/second
system          | total runtime: 0.572 seconds

And end up with the same issue of rules after the last column: enter image description here

8
  • can you post a complete minimal working example (MWE)? it would help us to help you if we could compile something directly.
    – Thruston
    Commented Apr 23, 2022 at 14:56
  • Good catch @Thruston! I've added one in an edit! Let me know if you'd like it a bit more minimal as well :)
    – Brooks
    Commented Apr 23, 2022 at 15:04
  • In the second page that you show, do you want a line to the right of the small piece of column or not, even if there is no text in the column to the right of it? (I mean only for the part where there is text)
    – mickep
    Commented Apr 24, 2022 at 10:18
  • @mickep That's what I originally had in mind – Ideally I'd balance the columns on the last page (I think that's something I'll need to do manually?) so that there would still be three columns, but they wouldn't go to the bottom of the page – just as far as the text goes. Maybe that would mean tying the METAFUN graphics to paragraphs instead of whole columns?
    – Brooks
    Commented Apr 25, 2022 at 9:36
  • I think balancing is not supported in the pagecolumns setup.
    – mickep
    Commented Apr 25, 2022 at 10:50

3 Answers 3

2

After a discussion with Hans (and with useful input from Wolfgang), the solution below came out. It works well if columns are not balanced. I did not do the main work, so I prefer not to take the credit for it, and mark it as CW.

\setupbodyfont[dejavu,9pt]

\setuphead[subject][
    style=bold,
    before={\blank[line]},
    after={\blank[none]},
]


\setuplayout[
    backspace=13mm,
    width=middle,
    topspace=13mm,
    height=middle,
    header=0mm,
    footer=0mm,
    grid=yes,
]


\startuseMPgraphic{verticalrule}
save a, b, c, p, q, r ;
path b ; b := positioncolumnbox(CurrentColumn) ;
draw image (
    path p ; p := rightboundary b shifted (\frameddimension{distance}/2,0) ;
    numeric c ; c := positioncolumn(positionregion("allendshere")) ;
    if (positionpage("allendshere") = RealPageNumber):
        if (CurrentColumn < c) :
            draw p withcolor black withpen pencircle scaled 1 ;
        elseif (CurrentColumn > c) :
            % nothing
        else :
            if CurrentColumn < NOfColumns :
                pair a ; a := positionxy ("allendshere") ;
                path q ; q := (0,ypart a) -- (PaperWidth,ypart a) ;
                path r ; r := p cutbeforefirst q ;
                draw r withcolor black withpen pencircle scaled 1 ;
            fi ;
        fi ;
    else :
        if CurrentColumn < NOfColumns :
            draw p withcolor black withpen pencircle scaled 1 ;
        fi ;
    fi ;
) shifted -llcorner b ;
setbounds currentpicture to OverlayBox ;
\stopuseMPgraphic

\defineoverlay[verticalrule][\useMPgraphic{verticalrule}]

\setupalign[verytolerant,stretch]

\starttext

\startpagecolumns[n=3,background=verticalrule]
    \dorecurse{10}{%
        \subject[title={Foo \recurselevel}]
        \samplefile{lorem}
    }
    \hpos{allendshere}{}
\stoppagecolumns

\stoptext

columns with rules

3
  • Thanks for posting this answer here! When you run the code from this answer, do you definitely get the output you've shown? If so, I think I make have some deeper issues with my ConTeXt installation... I'm getting a disastrously long error log here: gist.github.com/TheLostLambda/9db5e83958a1d2b79f4fd0452193b4cc What ConTeXt version do you have? My install and this website (live.contextgarden.net) don't seem to work.
    – Brooks
    Commented May 18, 2022 at 13:48
  • 1
    Yes, I get exactly that. Maybe you have a too old installation? The one at contextgarden is also a bit old.
    – mickep
    Commented May 18, 2022 at 14:19
  • It was indeed too old of a ConTeXt implementation! It actually seems quite difficult to get LMTX installed nicely via a package, but a manual installation fixes things for me! Thanks for posting this and thanks to Hans and Wolfgang for helping make it happen!
    – Brooks
    Commented May 24, 2022 at 9:20
3
+250

The pagecolumns has support for rules to separate the columns but the code for the rules itself is commented and can't be used.

If the code where enabled you could just enable the separator with separator=rule but this is not an option at the moment.

\setuplayout [tight]

\starttext

\startpagecolumns [n=3,align={verytolerant,stretch},separator=rule]
\dorecurse{10}{\samplefile{lorem}}
\stoppagecolumns

\stoptext

What you can do is to create your own background for each column and use the two register \nofcolumns and \mofcolumns to check of the current column is also the last column.

Both register are available in MetaFun with their own name which can be used in a if-condition.

% \startuseMPgraphic {ColumnSeparator}
%     \ifnum\mofcolumns<\nofcolumns
%         draw rightboundary (OverlayBox rightenlarged \measure{ColumnOffset});
%         setbounds currentpicture to OverlayBox;
%     \fi
% \stopuseMPgraphic

\startuseMPgraphic {ColumnSeparator}
    if CurrentColumn < NOfColumns:
        draw rightboundary (OverlayBox rightenlarged \measure{ColumnOffset});
        setbounds currentpicture to OverlayBox;
    fi
\stopuseMPgraphic

\defineoverlay [ColumnSeparator] [\useMPgraphic{ColumnSeparator}]

\setuplayout [tight]

\definemeasure [ColumnDistance] [1em]
\definemeasure [ColumnOffset]   [\measure{ColumnDistance}/2]

\starttext

\startpagecolumns [n=3,align={verytolerant,stretch},background=ColumnSeparator,distance=\measure{ColumnDistance}]
\dorecurse{10}{\samplefile{lorem}}
\stoppagecolumns

\stoptext

enter image description here

enter image description here

3
  • This looks like an excellent bunch of code, but unfortunately I'm having some troubles with it. Copying your example and running it still gives me a line after the last column. Is there a way to inspect the value of things like NOfColumns? Is this something to do with my context version? (added to question) or the way I'm running it? (context Article.tex) I'll also add the output I'm getting from Context to the question now. Thanks for this, hopefully we can get it working with a tweak or two on my end!
    – Brooks
    Commented May 17, 2022 at 8:48
  • There is currently no way to stop the rules on empty columns, even checking the registers which are used to store the content for each column doesn't work. To get proper support for rules in pagecolumns (by enabling the existing separator code from my first example) and additional features like no rules before empty columns you should move your question to the ConTeXt mailing list. Commented May 17, 2022 at 10:13
  • Thanks for the clarification! It looks like fixing the empty column problem would require some of the fancier lua solutions below – my issue was that my output was different from the one you show! I had rules to the right of my third column still (I added a screenshot in my second edit). Not sure why I'm getting a different result using the same code?
    – Brooks
    Commented May 18, 2022 at 13:19
3

An extension of Wolfgang's answer that uses Lua callbacks to measure the column heights.

(Edited to fix a small bug)

\doifnot{\contextmark}{LMTX}{
    \errmessage{"LMTX/MkXL is required to compile this file."}
}

\startluacode
    local saved_data = job.passes.define("column_height")
    local previous_data = job.passes.getcollected("column_height")
    local bigpoint = tex.sp("1bp")

    -- Run this code every time we output a column:
    callback.register("pre_output_filter", function(head)
        -- Measure the height of the column (head.next to avoid the topskip)
        local column_height = node.vpack(head.next).height
        local pagenumber = tex.count.realpageno
        -- Get the natural width of the baselineskip
        local baselineskip = tex.skip.baselineskip.width

        -- Save the column height in the .tuc file
        saved_data[pagenumber] = saved_data[pagenumber] or {}
        table.insert(
            saved_data[pagenumber],
            -- Clamp the height to multiples of baselineskip
            math.ceil(column_height / baselineskip) * baselineskip
        )

        return head
    end)

    function column_height(col)
        local pagedata = previous_data[tex.count.realpageno]
        if not pagedata then
            mp.numeric(0)
            texio.write_nl("Please rerun!")
            return
        end
        -- Get the minimum height of the column on either side of the line
        local minimum = math.max(pagedata[col], pagedata[col + 1])
        mp.numeric(-minimum / bigpoint)
    end
\stopluacode

% Draw the separator rules:
\startuseMPgraphic {ColumnSeparator}
    if CurrentColumn < NOfColumns:
        draw urcorner OverlayBox shifted (\measure{ColumnOffset}, 0pt) --
             urcorner OverlayBox shifted
                 (\measure{ColumnOffset}, lua.column_height(CurrentColumn));
        setbounds currentpicture to OverlayBox;
    fi
\stopuseMPgraphic

\defineoverlay [ColumnSeparator] [\useMPgraphic{ColumnSeparator}]

\setuplayout [tight]

\definemeasure [ColumnDistance] [1em]
\definemeasure [ColumnOffset]   [\measure{ColumnDistance}/2]

\starttext

\startpagecolumns [n=3,align={verytolerant,stretch},background=ColumnSeparator,distance=\measure{ColumnDistance}]
\dorecurse{10}{\samplefile{lorem}}
\stoppagecolumns

\stoptext

Page 1 of the output

Page 2 of the output

Above, the separator rule is the length of the shortest of the two columns. If you replace math.min with math.max, the rule will be the length of the longest of the two.

This also works with columns of different heights:

% Insert everything before `\starttext` from the code above,
% EXCEPT with `math.min` replaced with `math.max`
\setuppapersize[legal, landscape]

\starttext

\startpagecolumns [n=5,align={verytolerant,stretch},background=ColumnSeparator,distance=\measure{ColumnDistance}]

\dorecurse{2}{\samplefile{zapf}\par}
\column
\dorecurse{1}{\samplefile{lorem}\par}
\column
\dorecurse{2}{\samplefile{bryson}\par}
\column
\dorecurse{3}{\samplefile{knuth}\par}
\stoppagecolumns

\stoptext

Second example

2
  • Also looks like a stellar solution, but unfortunately it's not compiling for me either (gist.github.com/TheLostLambda/3cea71c09c874dad778a4c531ca7d1ef) Is there some sort of version issue or broken install that I've got going on?
    – Brooks
    Commented May 18, 2022 at 13:52
  • 1
    @Brooks It looks like you're compiling your document with MkIV. Unfortunately, it looks like CurrentColumn/\mofcolumns always just returns 1 with MkIV. Can you try compiling with LMTX/MkXL? I think that should make all of the answers here work correctly. Commented May 19, 2022 at 1:10

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .