2

I need an environment in which newlines are treated as newlines and that adds something at the end of lines followed by an empty line. Alltt is changing too many things, and even the font therefore i do not use it.

I provide my mwe, and an illustration done in lowriter. As I have no idea how to do it, i have not done anything for the new environment.

\documentclass[a4paper]{article}
\usepackage{fancyhdr}
\setlength{\parindent}{0pt}

\fancyhead[C]{lipsum text (dummy text)}

\begin{document} lipsum text \ (dummy text) \begin{myenv} Laborum molestias nam possimus omnis. Libero sit maiores quas asperiores nobis est nulla. Culpa sequi iste deserunt. ||

Adipisci fuga et quis dicta est et odit. Illo aut nulla qui incidunt necessitatibus nulla qui. Sunt cumque voluptatem quasi quam ||

\textbf{the peacock says ...} commodi culpa quaerat sunt. Debitis accusantium et porro et libero nulla ut est. Asperiores sed exercitationem aut. Ex sapiente ||

\textbf{the elephant says ...} Debitis nobis fugiat doloremque voluptates enim ratione. Unde ||

magni beatae magni. Unde voluptatum qui odio corporis quia. ||

\end{myenv} \end{document}

\z as defined here is the stuff I need to add at the end of each line followed by an empty line:

\newcounter{versenum}\setcounter{versenum}{1}
\newcommand{\z}{\arabic{versenum}\addtocounter{versenum}{1}}

enter image description here

2 Answers2

5

I've done this by making a custom \obeylines macro which peeks ahead looking for blank lines and handles the special case when the userverse environment finishes.

Features

  • Uses LaTeX \list environment to control vertical spacing above and below and indent within other list environments.
  • Places each verse in a \vtox box to prevent page breaks within verses.
  • Uses \devanagaridigits to print verse numbers
% TeX Program = xelatex
\documentclass{article}
\usepackage{lipsum}
\usepackage{multicol}
\usepackage{fontspec}
\usepackage{polyglossia}
\setmainlanguage{english}
\setotherlanguage{hindi}
\setmainfont{Noto Serif}
\newfontfamily\hindifont[Script=Devanagari]{Noto Serif Devanagari}
\ExplSyntaxOn
% Configuration
\dim_new:N \l__user_verse_left_margin_dim
\dim_set:Nn \l__user_verse_left_margin_dim { 3em }
\dim_new:N \l__user_verse_indent_dim
\dim_set:Nn \l__user_verse_indent_dim { 2em }
\skip_new:N \l__user_verse_skip
\skip_set:Nn \l__user_verse_skip { \medskipamount }
% Box to hold each verse to so page breaks are suppressed
\box_new:N \l__user_verse_box
% Counter to hold the verse number
\int_new:N \g__user_verse_number_int
% Increment verse number and output in required format
\cs_new_protected:Nn \__user_number_verse:
  {
    \int_gincr:N \g__user_verse_number_int
    \quad $\|$ ~
    \texthindi
      {
        \devanagaridigits { \int_use:N \g__user_verse_number_int }
      }
  }
% Set up custom \obeylines macro which calls \_user_do_obeylines:
\group_begin:
  \char_set_catcode_active:N \^^M
  \cs_gset_protected_nopar:Nn \__user_obeylines: %
    {%
      \char_set_catcode_active:N \^^M%
      \cs_set_eq:NN ^^M \__user_do_obeylines:%
    }%
\group_end:
\cs_new_protected:Npn \__user_vbox_set_top:Nw #1
  {
    \tex_setbox:D #1 \tex_vtop:D
      \c_group_begin_token
        \color_group_begin:
  }
% Use box maintaining spacing
% Adapted from https://tex.stackexchange.com/a/463739/87678
\cs_new_protected:Nn \__user_box_use_drop:N
  {
    \vbox_set_top:Nn \l_tmpa_box { \vbox_unpack:N #1 }
    \vbox_set:Nn \l_tmpb_box { \vbox_unpack_drop:N #1 }
    \dim_set:Nn \l_tmpa_dim { \box_dp:N \l_tmpa_box - \box_dp:N \l_tmpb_box }
    \box_set_dp:Nn \l_tmpa_box { \box_dp:N \l_tmpb_box }
    \box_use_drop:N \l_tmpa_box
    \vspace* \l_tmpa_dim
  }
% Look ahead at the end of each line
\cs_new_protected:Nn \__user_do_obeylines:
  {
    \peek_meaning:NTF \__user_do_obeylines:
      {
%       add verse number and extra space if blank line found
        \__user_number_verse:
        \vbox_set_end:
        \mode_leave_vertical:
        \__user_box_use_drop:N \l__user_verse_box
        \obeyedline
        \penalty \c_zero_int
        \vspace \l__user_verse_skip
        \__user_vbox_set_top:Nw \l__user_verse_box
        \dim_set_eq:NN \hsize \linewidth
      }
      {
%       no extra space if at end of verse environment, otherwise end paragraph
        \peek_meaning:NTF \end
          { \__user_number_verse: }
          { \obeyedline }
      }
  }
% Format verses
\NewDocumentEnvironment { userverse } { }
  {
    \int_gzero:N \g__user_verse_number_int
%   User a list environment to easily set indent and handle vertical spacing
    \list { }
      {
        \skip_zero:N \parsep
        \dim_set:Nn \leftmargin
          { \l__user_verse_left_margin_dim + \l__user_verse_indent_dim }
      }
    \item
    \relax
    \__user_obeylines:
    \__user_vbox_set_top:Nw \l__user_verse_box
    \dim_set_eq:NN \hsize \linewidth
  }
  {
    \vbox_set_end:
    \box_set_wd:Nn \l__user_verse_box \linewidth
    \mode_leave_vertical:
    \__user_box_use_drop:N \l__user_verse_box
    \endlist
  }
% Outdent and format verse intros 
\NewDocumentCommand \verseintro { m }
  {
    \hspace { -\l__user_verse_indent_dim }
    \textbf {#1}
  }
\ExplSyntaxOff
\begin{document}
\lipsum[11]

\begin{userverse} Laborum molestias nam possimus omnis. Libero sit maiores quas asperiores nobis est nulla. Culpa sequi iste deserunt.

Adipisci fuga et quis dicta est et odit. Illo aut nulla qui incidunt necessitatibus nulla qui. Sunt cumque voluptatem quasi quam

\verseintro{the peacock says\ldots} commodi culpa quaerat sunt. Debitis accusantium et porro et libero nulla ut est. Asperiores sed exercitationem aut. Ex sapiente

\verseintro{the elephant says\ldots} Debitis nobis fugiat doloremque voluptates enim ratione. Unde

magni beatae magni. Unde voluptatum qui odio corporis quia. \end{userverse}

\lipsum[11]

\selectlanguage{hindi}

\begin{multicols}{2} \begin{userverse} अअअअअअ अअअअअअ

अअअअअअ
अअअअअअ

अअअअअअ
अअअअअअ

अअअअअअ
अअअअअअ

\end{userverse} \end{multicols}

\selectlanguage{english}

\lipsum[11]

\end{document}

output

David Purton
  • 25,884
  • I will take some time to look at it. Looking at your result, it's perfect. it. What i wanted was to make latex print the text it finds, the way alltt does but only for carriage returns. So far latex produces a new line only when it finds an empty line. I want an environment in which this behaviour does not occur. \obeylines seems to honour carriage returns but it does not reproduce the empty lines it finds. So in the output the spacing between the verses do not appear. Also there is my need to put a verse number as I described (though you did it). – user1850133 Nov 26 '22 at 13:22
  • can you add comments to explain the code. Actually in my original document I use a foreign language which involves the use of polyglossia. Numbers are non-arabic and therefore, i was using \devanagarinumeral instead of arabic in my post. Here with expl3 i'm stuck. – user1850133 Nov 26 '22 at 13:37
  • @user1850133, I added comments and wrapped in a list environment for smarter vertical spacing and indent handling. \__user_number_verse: is what formats the verse numbering. You can put whatever you want in it and call it whatever you want. You could move it outside the expl3 block. – David Purton Nov 26 '22 at 14:42
  • could you add a feature to avoid getting a verse broken on two pages? – user1850133 Nov 26 '22 at 16:31
  • @user1850133 I boxed up each verse in a \vbox to avoid page breaks. You have to keep track of the \prevdepth for spacing to be correct when stacking the verses on top of each other. – David Purton Nov 27 '22 at 07:54
  • you did quite a lot of changes. I've not been able to add the macro to print verse number in a foreign script. The macro is this one: \newcommand{\devanagarinumeral}[1]{% \devanagaridigits{\number\csname c@#1\endcsname}} – user1850133 Nov 28 '22 at 05:03
  • @user1850133, I showed using \devanaragridigits to print the verse numbers. The main language is still English, so I wrapped it in \foreignlanguage{hindi}{…} but presumably your whole poem uses Devanagari script, so this won't be necessary. – David Purton Nov 28 '22 at 10:20
  • well, now i have to learn expl3 since obviously i might need in the future to add new features. – user1850133 Dec 03 '22 at 05:40
  • @user1850133 expl3 is a lot easier to learn than TeX. I can do things in expl3 that I would have no hope of achieving without it. Pretty much everything you need is found in texdoc xparse, texdoc expl3 and texdoc interface3. – David Purton Dec 03 '22 at 05:44
  • could you try putting the verses in a multicol environment, 2 columns, with first verse in devanagari (put अअअअअअ both lines) and also the one verse which will appear at the top of the second column. I see misalignment by 1~2 mm. I tried with the basic verse environment and without any such environment, i.e. neither verse nor userverse, in both cases alignment if fine. – user1850133 Dec 05 '22 at 11:28
  • @user1850133, it might be easier to ask a new question referencing this one which demonstrates the issue. I'm guessing a bit as to your exact set up. – David Purton Dec 06 '22 at 02:04
  • @user1850133 Actually, I can reproduce this. It's probably something to do with the \prevdepth hacks – David Purton Dec 06 '22 at 02:09
  • @user1850133, I swapped the \vbox construction for \vtop and used a different method for preserving the space between boxes. It still may have problems. It's getting a bit beyond my pay grade, sorry. – David Purton Dec 06 '22 at 14:02
  • your solution works perfectly for me. – user1850133 Dec 13 '22 at 16:28
  • I'm trying to move your code into a .sty file, but I would like to be able to set \devanagaridigits for the verse numbers from my .tex file. Can you show a solution? – user1850133 Dec 17 '22 at 06:31
  • @user1850133, it's better to ask a new question. From this comment I'm not really sure exactly what you want. – David Purton Dec 17 '22 at 07:09
  • I'm back on this. Could you add comments that would help a novice in expl3 syntax? – user1850133 Feb 03 '23 at 13:46
  • @user1850133, Your best bet is to look through texdoc expl3 which explains the variable naming scheme. Then search for the functions used in texdoc interface3, which gives you their syntax. – David Purton Feb 03 '23 at 14:34
  • it's not expl3 my problem now. It seems you used some pure tex language. the '\relax' is to close the list environment opened by \list? the '\peak_meaning:NTF __user_do_obeylines:' used inside '\cs_new_protected:Nn __user_do_obeylines:' definition... – user1850133 Feb 04 '23 at 05:35
  • @user1850133, \relax does nothing and can be used when the syntax expects something (in this case after \item). \list is a LaTeX macro documented in texdoc source2e. \peek_meaning:NTF is an expl3 function documented in texdoc interface3. Here it looks ahead for an end of line (which has been redefined to be \__user_do_obey_lines:). But comments are not supposed to be for extended discussion on this site. You can ask new questions about specific things like: "What does this function do?" (Although search first in case they have already been asked.) – David Purton Feb 04 '23 at 09:14
3

You can do it with regex replacement.

I inject \obeylines in the myenv environment prior to the reading of the argument, so each endline is transformed into an active ^^M.

Then initial ^^M tokens are removed; similarly, trailing ^^M are replaced by \addstanzanumber. Next, sequences of two or more ^^M are replaced by \addstanzanumber\par. The remaining ^^M are replaced by \\ and the resulting token list is fed to verse.

Initial or trailing blank lines in myenv are therefore ignored.

\documentclass[a4paper]{article}
\usepackage[T1]{fontenc}

\ExplSyntaxOn % a trick to set a token list to the active endline character \group_begin: \char_set_catcode_active:n { 13 } \tl_const:Nn \c_myenv_blank_line_tl { ^^M } \group_end:

\cs_generate_variant:Nn \seq_set_split:Nnn { NV } \tl_new:N \l_myenv_body_tl

\NewDocumentEnvironment{myenv}{+b} { \setcounter{stanzanumber}{0} \myenv_make:n { #1 } \begin{verse} \tl_use:N \l_myenv_body_tl \end{verse}
} {}

\cs_new_protected:Nn \myenv_make:n { \tl_set:Nn \l_myenv_body_tl { #1 } % remove any number of ^^M at the beginning \regex_replace_once:nnN { \A \u{c_myenv_blank_line_tl}* } { } \l_myenv_body_tl % replace any number of ^^M at the end with \addstanzanumber \regex_replace_once:nnN { \u{c_myenv_blank_line_tl}* \Z } { \c{addstanzanumber} } \l_myenv_body_tl % replace any number greater than two of ^^M with \addstanzanumber \regex_replace_all:nnN { \u{c_myenv_blank_line_tl}{2,} }% pairs ^^M^^M { \c{addstanzanumber}\c{par} } % replace with \addstanzanumber\par \l_myenv_body_tl % replace any remaining ^^M with \ \regex_replace_all:nnN { \u{c_myenv_blank_line_tl} } % single ^^M { \c{\} } % replace with \ \l_myenv_body_tl } \ExplSyntaxOff

\AtBeginEnvironment{myenv}{\obeylines} \newcounter{stanzanumber} \newcommand{\addstanzanumber}{~||~\stepcounter{stanzanumber}\thestanzanumber} \newcommand{\says}[1]{\hspace{-2em}\textbf{#1}}

\begin{document}

\begin{myenv} Laborum molestias nam possimus omnis. Libero sit maiores quas asperiores nobis est nulla. Culpa sequi iste deserunt.

Adipisci fuga et quis dicta est et odit. Illo aut nulla qui incidunt necessitatibus nulla qui. Sunt cumque voluptatem quasi quam

\says{the peacock says\dots} commodi culpa quaerat sunt. Debitis accusantium et porro et libero nulla ut est. Asperiores sed exercitationem aut. Ex sapiente

\says{the elephant says\dots} Debitis nobis fugiat doloremque voluptates enim ratione. Unde

magni beatae magni. Unde voluptatum qui odio corporis quia. \end{myenv}

\end{document}

enter image description here

egreg
  • 1,121,712
  • could you try in a {multicols}{2} environment? I would like that the empty line height is exactly equal to \baselineskip. – user1850133 Mar 21 '23 at 11:46