2

I’m typesetting a part of the bible and want to put the verse numbers at the margin rather than typesetting them inline. To separate a verse I use an asterisk.

When it comes to very short verses, sometimes two verses start in the same line, so that I have two marginnotes on top of each other. Using marginpars is no option because they are stacked vertically and that confuses the reader.

At the moment, I proof-read the text and add some extra space. However if I change anything in linewidth or font size I have to proof-read again because the situation could have changed. So I’m looking for an automated way to do that.

Below I add a MWE to show what I mean.

\documentclass[9pt,a5paper,twoside=false]{scrbook}
\usepackage{polyglossia}
\usepackage{geometry}
\usepackage{changepage}
\usepackage[colaction]{multicol}
\usepackage{marginnote}

\setmainlanguage{german}

% Footnotes
\deffootnote[1.5em]{1.5em}{1em}{\textsuperscript{\thefootnotemark}\ }

\newcommand{\strong}[1]{\textbf{#1}}

\makeatletter
\strictpagecheck
\let\old@mn@@@marginnote\@mn@@@marginnote
\long\def\@mn@@@marginnote[#1]#2[#3]{%
  \if@twoside
    \checkoddpage%
    \ifoddpage%
      {\begingroup\docolaction{\reversemarginpar}{}{}\old@mn@@@marginnote[{#1}]{#2}[{#3}]\endgroup}%
    \else{%
      {\begingroup\docolaction{}{}{\reversemarginpar}\old@mn@@@marginnote[{#1}]{#2}[{#3}]\endgroup}}%
    \fi%
  \else%
    \begingroup\docolaction{\reversemarginpar}{}{}\old@mn@@@marginnote[{#1}]{#2}[{#3}]\endgroup
  \fi%
}
\makeatother

\newcounter{biblechapter}
\newcounter{bibleverse}[biblechapter]

\newcommand{\marginbiblechapter}{\marginnote{\huge\bfseries\thebiblechapter}[0.6ex]}

\newcommand{\verseseparator}{\textasteriskcentered}
\newcommand{\marginbibleverse}{\verseseparator\marginnote{\small\thebibleverse}}
\newcommand{\omitmarginbibleverse}[1]{(\verseseparator)\marginnote{\small(\thebibleverse)}\footnote{#1}}
\newcommand{\added}[1]{\ensuremath{\langle}#1\ensuremath{\rangle}}

\newcommand{\printbiblechapter}{\marginbiblechapter}
\newcommand{\printbibleverse}{\marginbibleverse}
\newcommand{\omitbibleverse}[1]{\omitmarginbibleverse{#1}}

\newcommand{\book}[2]{\chapter*{#1}}
\makeatletter
\newcount\biblechap@svdopt
\newcount\biblevrs@svdopt
\newcommand{\adjustchapter}[1]{
  \biblechap@svdopt=#1 %
  \ifnum\c@biblechapter=\biblechap@svdopt\else\advance\biblechap@svdopt by -1\fi%
  \setcounter{biblechapter}{\the\biblechap@svdopt}%
}
\newcommand{\adjustverse}[1]{
  \biblevrs@svdopt=#1 %
  \ifnum\c@bibleverse=\biblevrs@svdopt\else\advance\biblevrs@svdopt by -1\fi%
  \setcounter{bibleverse}{\the\biblevrs@svdopt}%
}
\newenvironment{biblechapter}[1][\thebiblechapter]{%
  \adjustchapter{#1}\refstepcounter{biblechapter}\printbiblechapter%
}{}
\renewcommand{\verse}[1][\thebibleverse]{%
  \adjustverse{#1}\refstepcounter{bibleverse}%
  \ifnum\c@bibleverse=1\else\printbibleverse\fi%
}%
\newcommand{\omitverse}[2][\thebibleverse]{%
  \adjustverse{#1}\refstepcounter{bibleverse}%
  \ifnum\c@bibleverse=1\else\omitbibleverse{#2}\fi%
}
\makeatother

\newcommand{\Title}{Das Evangelium nach Markus}

\geometry{
  top=3cm,
  bottom=3cm,
  inner=1.25cm,
  outer=1.25cm,
  footskip=0.7cm,
  marginparsep=0.8em,
}

\title{\Title}
\date{\Date}
\author{\Author}

\begin{document}

\book{\Title}{Markus}

\begin{multicols}{2}%

% Chapter 4
\begin{biblechapter}
\verse Und wieder fing er an, am See zu lehren. Und es versammelt sich eine sehr große Volksmenge zu ihm, so dass er in ein Boot stieg und auf dem See saß; und die ganze Volksmenge war am See auf dem Land.

\verse {…}

\verse Und er spricht zu ihnen: Begreift ihr dieses Gleichnis nicht? Und wie wollt ihr all die Gleichnisse verstehen?

\verse Der Sämann sät das Wort.
\verse Die an dem Weg aber sind die, bei denen das Wort gesät wird und, wenn sie es hören, sogleich der Satan kommt und das Wort wegnimmt, das in sie hinein gesät worden ist.
\verse Und ebenso sind die, die auf das Steinige gesät werden, die, wenn sie das Wort hören, es sogleich mit Freuden aufnehmen,
\verse und sie haben keine Wurzel in sich, sondern sind \added{Menschen} des Augenblicks; wenn nachher Bedrängnis oder Verfolgung um des Wortes willen entsteht, ärgern sie sich sogleich.
\verse Und andere sind die unter die Dornen Gesäten, es sind die, die das Wort gehört haben,
\verse und die Sorgen der Zeit und der Betrug des Reichtums und die Begierden nach den übrigen Dingen kommen hinein und ersticken das Wort, und es bringt keine Frucht.
\verse Und die auf die gute Erde Gesäten sind jene, die das Wort hören und aufnehmen und Frucht bringen: eines dreißig- und eines sechzig- und eines hundert\added{fach}.

\verse {…}

\verse Und wer einem dieser Kleinen, die an mich glauben, Anlass zur Sünde gibt, für den wäre es besser, wenn ein Mühlstein um seinen Hals gelegt und er ins Meer geworfen würde.
\verse Und wenn deine Hand dir Anlass zur Sünde gibt, so hau sie ab! Es ist besser für dich, als Krüppel in das Leben hineinzugehen, als mit zwei Händen in die Hölle zu kommen, in das unauslöschliche Feuer.
\omitverse{Spätere Handschriften ergänzen: wo ihr Wurm nicht stirbt und das Feuer nicht erlischt.}
\verse Und wenn dein Fuß dir Anlass zur Sünde gibt, so hau ihn ab! Es ist besser für dich, lahm in das Leben hineinzugehen, als mit zwei Füßen in die Hölle geworfen zu werden.
\omitverse{Spätere Handschriften ergänzen: in das unauslöschliche Feuer, wo ihr Wurm nicht stirbt und das Feuer nicht erlischt.}
\verse Und wenn dein Auge dir Anlass zur Sünde gibt, so wirf es weg! Es ist besser für dich, einäugig in das Reich Gottes hineinzugehen, als mit zwei Augen in die Hölle geworfen zu werden,
\verse «wo ihr Wurm nicht stirbt und das Feuer nicht erlischt».
\verse Denn jeder wird mit Feuer gesalzen werden.
\end{biblechapter}

\end{multicols}

\end{document}

The numbers of verse 4/5, 14/15 and 16/17 appear overlapping. I already had the idea of creating temporary variables storing the y-Position and the width of the current marginnote box, comparing it with the values of the next marginnote. But at that moment I only can shift the second one, which may be okay for the right margin, but not for the left, because that way they would appear in wrong order. Is there any way to hook into the line break algorithm or to delay the output of the first marginnote? Something equivalent to AddEverypageHook for line breaks could be right.

I’m a fairly good programmer, but I don’t have enough knowledge of LaTeX’s internals to find my way.

By the way, I am using XeLaTeX, if that makes any difference, because in the final document there will be different fonts, but I stripped them for the example as they are not important here.

  • I’m not sure you did understand me right. I do not want the second note to appear below the first, but next to each other. I already read tex.stackexchange.com/questions/279037/… . That’s what I want. – complanar Apr 18 '16 at 20:13
  • There is apparently an \EveryLine command in ConText (see tex.stackexchange.com/questions/104293/…). The lineno package supports a \linelabel command. It should be possible to store x,y locations in the aux file and (on the second run) have the everypage convert them into notes, consolidating those with the same y values. – John Kormylo Apr 18 '16 at 20:50
  • @JohnKormylo I guess a ConTeXt solution is not going to work given the XeTeX requirement. The lineno package seems more hopeful. But why not just consolidate those with the same line numbers, in that case? Why store the x and y values? Presumably, you just need to get the order correct by using a prefix/suffix-type rule depending on whether the note is right or left? Not at all straightforward, though.... – cfr Apr 19 '16 at 1:04
  • I was proposing three different approaches. The aux file solution I know will work. It is basically the same as tex.stackexchange.com/questions/215322/… only much simpler (no least squares). It will still take some time. – John Kormylo Apr 19 '16 at 11:38
3

The following provides macro \marginmerge{<text>} which will functions like \marginnote except that it only does a single line of text and will combine (at most) two overlapped calls into one line, inserting a "/" between them.

I did not use your Not So Minimal Working Example to save space. It should be noted that if you do not use geometry you will need to add \pdfpageheight=\paperheight and \pdfpagewidth=\paperwidth to the preamble.

Note: I broke \MMoverlap into a separate macro because I was having trouble keeping track of the \if \fi matchups.

\documentclass[twocolumn]{article}
\usepackage[showframe]{geometry}
\usepackage{everypage}
%\tracingmacros=1

\newcounter{abspage}
\AddEverypageHook{\MarginMergePage}

\makeatletter
% allocate global registers
\newcount\MM@total
\newcount\MM@index

\newif\ifMM@left
\newif\ifMM@same

\newcommand{\marginmerge}[1]% #1 = text for margin note
{\pdfsavepos
  \protected@write\@auxout{\let\theabspage=\relax}{\string\newmarginmerge{\theabspage}%
    {\noexpand\number\pdflastxpos}{\noexpand\number\pdflastypos}{#1}}%
}%

\newcommand{\newmarginmerge}[4]% #1 = page, #2 = x, #3 = y, #4 = text
{\global\advance\MM@total by \@ne
  \expandafter\gdef\csname MM@page\the\MM@total\endcsname{#1}%
  \expandafter\gdef\csname MM@x\the\MM@total\endcsname{#2}%
  \expandafter\gdef\csname MM@y\the\MM@total\endcsname{#3}%
  \expandafter\gdef\csname MM@text\the\MM@total\endcsname{#4}%
}%

\newcommand{\MarginMergePage}% cursor 1in from top left corner
{\stepcounter{abspage}%
\bgroup% allocate local registers
  \countdef\index=1
  \countdef\next=2
  \dimendef\xleft=0 % left margin
  \dimendef\xright=1 % right margin
  \dimendef\xcenter=2 % between columns
  \dimendef\x=3
  \dimendef\y=4
% locate x offsets
  \ifodd\c@page
    \setlength{\xleft}{\oddsidemargin}%
  \else
    \setlength{\xleft}{\evensidemargin}%
  \fi
  \setlength{\xcenter}{\xleft}%
  \addtolength{\xcenter}{1in}% to left side of page
  \addtolength{\xcenter}{0.5\textwidth}%

  \setlength{\xright}{\xleft}%
  \addtolength{\xright}{\textwidth}%
  \addtolength{\xright}{\marginparsep}%

  \addtolength{\xleft}{-\marginparsep}%
% check for notes
  \MM@lefttrue
  \index=\MM@index
  \loop\ifnum\index<\MM@total
    \advance\index by \@ne
    \edef\theindex{\the\index}%
    \ifnum\value{abspage}<\csname MM@page\theindex\endcsname\relax
      \index=\MM@index
    \else
      \x=\csname MM@x\theindex\endcsname sp\relax
      \ifdim\x>\xcenter \MM@leftfalse\fi
      \y=\csname MM@y\theindex\endcsname sp\relax
      \def\text{\csname MM@text\theindex\endcsname}%
      \MMoverlap% check for overlap
    \fi
  \fi
  \ifnum\index>\MM@index
    \advance\y by -\paperheight
    \advance\y by 1in
    \ifMM@left \raisebox{\y}[0pt][0pt]{\rlap{\hspace{\xleft}\llap{\text}}}%
    \else \raisebox{\y}[0pt][0pt]{\rlap{\hspace{\xright}\text}}%
    \fi
    \global\MM@index=\index
  \repeat
\egroup}

\newcommand{\MMoverlap}{% check for overlap
\ifnum\index<\MM@total\relax
  \next=\index
  \advance\next by \@ne
  \edef\thenext{\the\next}%
  \ifnum\c@page=\csname MM@page\thenext\endcsname\relax
    \MM@sametrue
    \ifMM@left
      \x=\csname MM@x\thenext\endcsname sp\relax
      \ifdim\x>\xcenter \MM@samefalse\fi
    \fi
    \ifMM@same
      \ifdim\y=\csname MM@y\thenext\endcsname sp\relax
        \def\text{\csname MM@text\theindex\endcsname/\csname MM@text\thenext\endcsname}% yes
        \index=\next
      \fi
    \fi
  \fi
\fi}
\makeatother

\begin{document}
\noindent
Left column \marginmerge{\small 1}
with overlap. \marginmerge{2}\\
Not overlapped.\marginmerge{3}

\newpage
\noindent
Right column \marginmerge{4}
with overlap. \marginmerge{5}\\
Not overlapped.\marginmerge{6}
\end{document}

demo

  • Fantastic! There is only one problem: I’m not able to format the text in any way, for example to make it \small or bold. The code is to complicated for me as I don’t understand much of ...@... and friends :-). Is it possible to store the combined text in a variable and print it with a normal marginnote? – complanar Apr 20 '16 at 19:06
  • Sorry about that. I forgot you can't use \edef with protected text. It's fixed now. (The only difference between this and \marginnote is that you haven't seen the source code for \marginnote.) – John Kormylo Apr 21 '16 at 4:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.