1

I am writing a document where each paragraph is categorized. I'm encoding the category as a simple numeric variable: 1, 2, 3, &c. On each page I want to know and possibly display the highest category value of any content on the page. I'm using a simple counter and wrapping each paragraph in a macro that updates the counter value if the paragraph category is higher than anything encountered so far. I'm resetting the counter every page using either everypage or everyshi. So far it almost works.

Also, I really, really don't want paragraphs to be broken across pages. When I set

\widowpenalties 1 10000

to try to enforce this, the category counter behaves in the strangest way. This strange behavior is my trouble.

The page category counter always resets two paragraphs too late into the next page. I'm completely mystified. Can anyone please shed light on this?

I'll happily accept suggestions of other ways to accomplish what I want, too.

Here is a not-very-minimal working example. It's 9 pages long. There are four pages at the beginning that show the behavior without setting widowpenalties. Then there's a separation page before four more identical pages with widowpenalties turned up to 10,000.

When you run the code, each paragraph is marked with two like 2-1 The first number is the highest category yet encountered on that page and the second number is that paragraph's category. To see the odd behavior, notice that n page 2, you'll see the first two paragraphs marked 1-1 but on page 7 you'll see the exact same two paragraphs marked 3-1.


    \documentclass{article}

    \usepackage{xifthen}
    \usepackage{lipsum}
    \usepackage{etoolbox}
    \usepackage{everypage}


    \newcommand\settext[3]{\csdef{#1#2}{#3}}
    \newcommand\gettext[2]{\csuse{#1#2}}

    \newcounter{pageCategoryCntr}
    \setcounter{pageCategoryCntr}{1}
    \AddEverypageHook{\setcounter{pageCategoryCntr}{1}}
    %\zmakeperpage{pageCategoryCntr}
    %\EveryShipout{\setcounter{pageCategoryCntr}{1}}

    \newcommand{\category}[2]{
      \ifthenelse{#1 > \value{pageCategoryCntr}}{
        \setcounter{pageCategoryCntr}{#1}
      }{}
      \thepageCategoryCntr\ - #1 -
      #2
    }

    \begin{document}
    \category{1}{\lipsum[2]}

    \category{1}{\lipsum[3]}

    \category{1}{\lipsum[2]}

    \category{3}{\lipsum[3]}

    \category{1}{\lipsum[2]}

    \category{1}{\lipsum[3]}

    \category{1}{\lipsum[4]}

    \category{1}{\lipsum[5]}

    \category{1}{\lipsum[2]}

    \category{1}{\lipsum[3]}

    \category{1}{\lipsum[2]}

    \category{1}{\lipsum[3]}

    \category{3}{\lipsum[2]}

    \category{1}{\lipsum[3]}

    \category{1}{\lipsum[4]}

    \category{1}{\lipsum[5]}

    \newpage
    \widowpenalties 1 10000
    \clubpenalties 1 10000
    \raggedbottom
    Now another copy of the same but we set the widowpenalties and
    clubpenalties to avoid breaking paragraphs across pages.
    \newpage
    \category{1}{\lipsum[2]}

    \category{1}{\lipsum[3]}

    \category{1}{\lipsum[2]}

    \category{3}{\lipsum[3]}

    \category{1}{\lipsum[2]}

    \category{1}{\lipsum[3]}

    \category{1}{\lipsum[4]}

    \category{1}{\lipsum[5]}

    \category{1}{\lipsum[2]}

    \category{1}{\lipsum[3]}

    \category{1}{\lipsum[2]}

    \category{1}{\lipsum[3]}

    \category{3}{\lipsum[2]}

    \category{1}{\lipsum[3]}

    \category{1}{\lipsum[4]}

    \category{1}{\lipsum[5]}
    \end{document}
6
  • see tex.stackexchange.com/questions/94699/…
    – touhami
    Commented Jul 15, 2016 at 7:25
  • Hm, are you suggesting enclosing each paragraph in one of the absolutely unbreakable environments given in the first answer to that question? Normally that wouldn't be particularly reasonable in my 100-page document... But I do have to categorize every paragraph anyway! I'll give it a try.
    – Austin A.
    Commented Jul 15, 2016 at 12:35
  • One paragraph I understand, since the paragraph is written first then tested to see if it will fit on the current page. If not, it is shipped to the next page. Maybe the second paragraph is due to the club test. Anyway, check out the ifoddpage package. Commented Jul 15, 2016 at 13:08
  • BTW, if you would like the absolute highest category per page rather than the highest so far, that also be done using the aux file. Commented Jul 15, 2016 at 13:18
  • @John Kormylo, I am ultimately interested in the absolute highest category per page and displaying that value on the page. The specific trouble I'm having is that a high-value page bleeds over into the next page , and0 then the next, & c. If there's another way of getting the information I want using the aux file, please do share!
    – Austin A.
    Commented Jul 15, 2016 at 13:31

2 Answers 2

2

Here is the aux file solution. Interestingly, you need to use at least \leavevmode to put \protected@write INSIDE the paragraph instead of before it.

I really only need one counter (CategoryCntr), but using \setcounter is a easier to understand.

\documentclass{article}

%\usepackage{xifthen}
\usepackage{lipsum}
%\usepackage{etoolbox}
%\usepackage{everypage}

\newcommand\settext[3]{\csdef{#1#2}{#3}}
\newcommand\gettext[2]{\csuse{#1#2}}

\newcounter{CategoryMax}% used to compute max category per page
\newcounter{CategoryCntr}% increment every \category
\newcounter{CategoryPage}% used instead of the page counter

\newcommand{\newcategory}[3]{% #1 = index, #2 = category, #3 = page
  \expandafter\gdef\csname CategoryPage#1\endcsname{#3}% save page per index
  \ifnum\value{CategoryPage}=#3\relax
    \ifnum\value{CategoryMax}<#2\relax \setcounter{CategoryMax}{#2}\fi
  \else
    \ifnum\value{CategoryPage}>0\relax
      \expandafter\xdef\csname CategoryMax\theCategoryPage\endcsname{\theCategoryMax}% save max category
    \fi
    \setcounter{CategoryMax}{#2}%
    \setcounter{CategoryPage}{#3}%
  \fi
}
\AtBeginDocument{% save last page 
  \ifnum\value{CategoryPage}>0\relax
    \expandafter\xdef\csname CategoryMax\theCategoryPage\endcsname{\theCategoryMax}%
  \fi
}
\makeatletter
\newcommand{\category}[2]{% #1 = category, #2 = text
  \par\leavevmode
  \stepcounter{CategoryCntr}%
  \protected@write\@auxout{}{\string\newcategory{\theCategoryCntr}{#1}{\thepage}}%
  %\protected@write\@auxout{\let\arabic\relax}{\string\newcategory{\theCategoryCntr}{#1}{\arabic{page}}}%
  \@ifundefined{CategoryPage\theCategoryCntr}{?? - #1 - #2}{%
    \setcounter{CategoryPage}{\csname CategoryPage\theCategoryCntr\endcsname}%
    {\csname CategoryMax\theCategoryPage\endcsname} - #1 - #2
  }%
}
\makeatother

\begin{document}
\widowpenalties 1 10000
\clubpenalties 1 10000
\raggedbottom

\category{1}{\lipsum[2]}

\category{1}{\lipsum[3]}

\category{1}{\lipsum[2]}

\category{3}{\lipsum[3]}

\category{1}{\lipsum[2]}

\category{1}{\lipsum[3]}

\category{1}{\lipsum[4]}

\category{1}{\lipsum[5]}

\category{1}{\lipsum[2]}

\category{1}{\lipsum[3]}

\category{1}{\lipsum[2]}

\category{1}{\lipsum[3]}

\category{3}{\lipsum[2]}

\category{1}{\lipsum[3]}

\category{1}{\lipsum[4]}

\category{1}{\lipsum[5]}

\end{document}
3
  • Thank you for posting this. I was hoping to see what this aux file wizardry would look like! This solution by the way seems to naturally adapt to \newpage commands.
    – Austin A.
    Commented Jul 15, 2016 at 16:24
  • Take a look at the aux file to see what it is doing. Commented Jul 15, 2016 at 16:28
  • In my actual application, this solution is the one I went with. Once you get the idea, it extends very easily to categorizing other things such as figures and tables. It's also simple to keep track of an overall maximum category value in the document. This is a really cool trick. Thank you for posting it!
    – Austin A.
    Commented Jul 19, 2016 at 0:21
3

Here is a solution.

\documentclass{article}
\usepackage{lipsum}
\usepackage{everypage}

\newcounter{pageCategoryCntr}
\AddEverypageHook{\setcounter{pageCategoryCntr}{1}}

\newcommand{\category}[2]{% 
\par \penalty-100\begingroup %from needspace package
\setbox0=\vbox{#2}%
\ifdim\ht0>\dimexpr\pagegoal-\pagetotal\relax
    \break
\fi\endgroup
\ifnum #1 > \value{pageCategoryCntr}
    \setcounter{pageCategoryCntr}{#1}%
\fi  
\thepageCategoryCntr\ - #1 - #2
}

\begin{document}

\category{1}{\lipsum[2]}

\category{1}{\lipsum[3]}

\category{1}{\lipsum[2]}

\category{3}{\lipsum[3]}

\category{1}{\lipsum[2]}

\category{1}{\lipsum[3]}

\category{1}{\lipsum[4]}

\category{1}{\lipsum[5]}

\category{1}{\lipsum[2]}

\category{1}{\lipsum[3]}

\category{1}{\lipsum[2]}

\category{1}{\lipsum[3]}

\category{3}{\lipsum[2]}

\category{1}{\lipsum[3]}

\category{1}{\lipsum[4]}

\category{1}{\lipsum[5]}

\end{document}
4
  • Yep, that works perfectly!
    – Austin A.
    Commented Jul 15, 2016 at 15:10
  • Hm, there's an odd behavior: adding a \newpage does not reset the value of pageCategoryCntr. It seems that you have to manually reset the counter with every page break.
    – Austin A.
    Commented Jul 15, 2016 at 15:32
  • Same issue with e.g. new chapters.
    – Austin A.
    Commented Jul 15, 2016 at 15:39
  • @AustinA. answer updated.
    – touhami
    Commented Jul 15, 2016 at 16:04

You must log in to answer this question.

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