Envie (e empilhe) \marginpar para o topo ou para o final da página

Envie (e empilhe) \marginpar para o topo ou para o final da página
  • Estou procurando definir algo como \topmarginpar{<text>}, que essencialmente criaria um \marginpar{<text>}e alinharia seu topo com o topo da página atual.

  • Eu também gostaria que várias \topmarginparchamadas empilhassem esses \marginparobjetos (em vez de sobrepô-los).

  • E finalmente gostaria de criar um \bottommargincomando semelhante.

Existe algum pacote existente que fornece essa funcionalidade ou estou sozinho aqui (sou fluente em LaTeX, mas inútil em relação ao TeX bruto)?

Obrigado por qualquer sugestão...

Responder1

Esta solução trata da rotina de saída assíncrona:

  • As páginas são identificadas por números de páginas absolutos e os números de páginas corretos estão disponíveis na segunda execução do LaTeX usando rótulos.

  • Cada página recebe duas caixas coletoras que coletam as notas marginais de cima e de baixo. No momento do envio, o número absoluto da página é conhecido e as caixas coletoras da página são exibidas na área "marginpar" da página.

  • Os registradores de caixa são alocados dinamicamente e gerenciados em um pool. Após o envio da página, seus registros da caixa coletora são liberados e recolocados no pool.

  • \topskipe \maxdepthsão respeitados pela área “marginpar”.

  • Muitas notas marginais em uma página são relatadas como \vboxavisos excessivamente completos.

  • LaTeX \marginparnão é suportado, as notas marginais de diferentes tipos não se conhecem e ficarão felizmente sobrepostas umas às outras.

O documento de exemplo:

\documentclass{article}

\usepackage{atbegshi}
\usepackage{zref-abspage}
\usepackage{picture}

\makeatletter
\providecommand*{\c@zabspage}{\c@abspage}

% * User macros for configuring
%
% \tbmparItemSep is inserted between marginal notes
% \tbmparMiddleSep is inserted between top and bottom marginal notes.

\newcommand*{\tbmparItemSep}{%
  \vspace{1ex minus .5ex}%
  \hrule
  \vspace{1ex minus .5ex}%
}
\newcommand*{\tbmparMiddleSep}{%
  \vspace*{0pt plus 1fil}%
}

% * Debug messages
%
\newcommand*{\tbmparDebug}[1]{%
  \typeout{[tbmpar] #1}%
}

% * Label management to remember absolute page number
%
% \tbmpar@PageByLabel stores and loads absolute page number from
% label and defines \tbmpar@page with absolute page number or
% zero if the label is not yet available.

\newcount\c@tbmpar@item
\c@tbmpar@item\z@

\newcommand*{\tbmpar@PageByLabel}{%
  \global\advance\c@tbmpar@item\@ne
  \zref@labelbyprops{tbmpar\the\c@tbmpar@item}{abspage}%
  \edef\tbmpar@page{%
    \zref@extractdefault{tbmpar\the\c@tbmpar@item}{abspage}{0}%
  }%
  \zref@refused{tbmpar\the\c@tbmpar@item}%
  \tbmparDebug{Item \the\c@tbmpar@item\space on page \tbmpar@page}%
}

% * Box register management

\newcount\c@tbmpar@box
\c@tbmpar@box\z@

\let\tbmpar@boxfreelist\@empty

% Get a new free box register either from the free list or,
% if the free list is empty, allocate a new box register.
\newcommand*{\tbmpar@NextBox}[1]{%
  \@next#1\tbmpar@boxfreelist{%  
    \tbmparDebug{Reused box: #1}%
  }{%
    \global\advance\c@tbmpar@box\@ne
    \expandafter\newbox\csname tbmpar@box\the\c@tbmpar@box\endcsname
    \edef#1{\csname tbmpar@box\the\c@tbmpar@box\endcsname}%
    \tbmparDebug{New box: #1}%
  }%
}
% Put free box in free list.
\newcommand*{\tbmpar@FreeBox}[1]{%
  \begingroup
    \let\@elt\relax
    \xdef\tbmpar@boxfreelist{%
      \tbmpar@boxfreelist
      \@elt#1%
    }%
    \tbmparDebug{Free box: #1}%
  \endgroup
}

\newsavebox{\tbmpar@box}

% Each marginpar is put in a box that is initialized as
% parbox/minipage.
\newcommand*{\tbmparBoxSetup}{}
\newcommand{\tbmpar@VBox}[1]{% 
  \vbox{%
    \color@begingroup
    \hsize\marginparwidth
    \edef\tbmpar@restore@ifminipage{%
      \if@minipage
        \noexpand\@minipagetrue
      \else
        \noexpand\@minipagefalse
      \fi
    }%   
    \@parboxrestore
    \@marginparreset
    \tbmparBoxSetup 
    #1%
    \tbmpar@restore@ifminipage
    \color@endgroup
  }%
}   

% Macro \tbmpar@marginpar looks for the page, where the margin note
% belongs to, stores the note in a box and appends the box to the  
% note collector register of the page.
% Each page is assigned a box collector registers that collect
% the top notes and a register that collect the bottom notes. 
% The name of the box register is \tbmpar@<top|bot>box<page>. 
\newcommand{\tbmpar@marginpar}[4]{%
  \ifhmode
    \@bsphack
  \fi
  \tbmpar@PageByLabel
  \ifnum\tbmpar@page>\z@
    \setbox\tbmpar@box\tbmpar@VBox{#4}%
    \@ifundefined{tbmpar@#1box\tbmpar@page}{%
      \tbmpar@NextBox\tbmpar@currbox
      \global\expandafter\let
          \csname tbmpar@#1box\tbmpar@page\endcsname
          \tbmpar@currbox
      \global\setbox\tbmpar@currbox=\vbox{%
        \unvbox\tbmpar@box
      }%
    }{% 
      \tbmparDebug{Use box: \tbmpar@currbox}%
      \expandafter\let\expandafter\tbmpar@currbox
          \csname tbmpar@#1box\tbmpar@page\endcsname
      \global\setbox\tbmpar@currbox\tbmpar@VBox{%   
        \unvbox#2%
        \par
        \begingroup
          \tbmparItemSep
        \endgroup
        \unvbox#3%
      }%
    }%  
  \fi   
  \ifhmode
    \@esphack
  \fi
}
\newcommand*{\topmarginpar}{%
  \tbmpar@marginpar{top}\tbmpar@currbox\tbmpar@box
}
\newcommand*{\botmarginpar}{%
  \tbmpar@marginpar{bot}\tbmpar@box\tbmpar@currbox
}

% At shipout time we look for the box collector registers of this
% page and set these boxes in the marginpar box with respecting  
% \topskip and \maxdepth.
\AtBeginShipout{%
  \AtBeginShipoutUpperLeft{%
    \put(%
      \dimexpr 1in+\oddsidemargin+\textwidth+\marginparsep\relax,%
      -\dimexpr 1in+\topmargin+\headheight+\headsep+\textheight\relax
    ){%
      \begingroup
        \global\let\tbmpar@inuse=N%
        \setbox\tbmpar@box=\tbmpar@VBox{%
          \penalty-\@M
          \edef\tbmpar@tmp{tbmpar@topbox\the\value{zabspage}}%
          \@ifundefined{\tbmpar@tmp}{%
          }{%
            \expandafter\let\expandafter\tbmpar@currbox
                \csname\tbmpar@tmp\endcsname
            \unvbox\tbmpar@currbox
            \tbmpar@FreeBox\tbmpar@currbox
            \global\let\tbmpar@inuse=Y%   
          }%
          \endgraf
          \tbmparMiddleSep
          \edef\tbmpar@tmp{tbmpar@botbox\the\value{zabspage}}%
          \@ifundefined{\tbmpar@tmp}{%
          }{%
            \expandafter\let\expandafter\tbmpar@currbox
                \csname\tbmpar@tmp\endcsname
            \unvbox\tbmpar@currbox
            \tbmpar@FreeBox\tbmpar@currbox
            \global\let\tbmpar@inuse=Y%   
          }%
        }%  
        \ifx\tbmpar@inuse Y%
          \splittopskip=\topskip
          \setbox0=\vsplit\tbmpar@box to\z@
          \boxmaxdepth=\maxdepth
          \setbox\tbmpar@box=\vbox to\textheight{%
            \unvbox\tbmpar@box
          }%
          \box\tbmpar@box
        \fi
      \endgroup
    }%
  }%  
}     

\makeatother

% Testing

\usepackage[
  a5paper,   
  left=10mm, 
  right=10mm,
  marginparwidth=40mm,
  includemp,
]{geometry}
\usepackage{microtype}  
\usepackage[T1]{fontenc}
\usepackage{lmodern}

\clubpenalty=10000
\flushbottom
\settodepth\maxdepth{g}  
\setlength{\fboxsep}{1ex}
\usepackage{lipsum}
\usepackage{color}

\newcommand*{\shortlipsum}[1]{%
  \begingroup
    \long\def\y##1. ##2\@nil{##1.}%
    \edef\x{\csname lipsum@\romannumeral#1\endcsname}%
    \expandafter\y\x. \@nil
  \endgroup
}

\begin{document}
  \topmarginpar{\color{blue}\shortlipsum{1}}
  \lipsum[1]
  \botmarginpar{%
    Show effect of \texttt{\textbackslash maxdepth}:
    $\displaystyle\sum_{\textstyle i=\frac{a}{g}}^\infty i = x$}
  \lipsum[2]
  \topmarginpar{\shortlipsum{3}}%
  \botmarginpar{Second bottom marginal note}%
  \lipsum[3-4]
  \noindent a\botmarginpar{a} b\botmarginpar{b} c \botmarginpar{c} d\\
  e\\f\par
  \topmarginpar{\fbox{\shortlipsum{5}}}
  \lipsum[5]
  Text with footnote and marginal note\footnote{Marginal note X}.
  \topmarginpar{This is marginal note X}
  \lipsum[6]
  \botmarginpar{\shortlipsum{7}}
  \lipsum[7]  
\end{document}

Resultado

Responder2

Aqui está uma tentativa usandoMartin Scharrerpacote detikzpagenodes. Como utiliza as remember picture, overlayopções deTikZ, você precisará compilar duas vezes para acertar. Ele fornece os dois comandos \marpartope \marparbotambos levam dois argumentos: o conteúdo e a cor do texto, que pode ser facilmente expandido para ser mais personalizável.

Código

\documentclass{scrartcl}
\usepackage[left=15mm,top=40mm,bottom=40mm,right=50mm,a4paper]{geometry}
\usepackage{tikzpagenodes}
\usepackage{xifthen}

\usepackage{lipsum}

\setlength{\marginparwidth}{40mm}
\pagestyle{empty}

\def\myyshifttop{0}
\def\mypagetop{0}

\newcommand{\marpartop}[2]% content, color
{   \begin{tikzpicture}[remember picture, overlay]
        \ifthenelse{\thepage=\mypagetop}{}{\xdef\myyshifttop{0}}        
        \xdef\mypagetop{\thepage}
        \node[below right, yshift=\myyshifttop, text width=\marginparwidth-4pt, inner sep=2pt, #2] (tempnode) at (current page marginpar area.north west) {#1};
        \path (current page marginpar area.north west);
        \pgfgetlastxy{\tempxone}{\tempyone}
        \path (tempnode.south west);
        \pgfgetlastxy{\tempxtwo}{\tempytwo}
        \pgfmathsetmacro{\diffy}{(\tempytwo-\tempyone)}
        \xdef\myyshifttop{\diffy}
    \end{tikzpicture}
}

\def\myyshiftbot{0}
\def\mypagebot{0}

\newcommand{\marparbot}[2]% content, color
{   \begin{tikzpicture}[remember picture, overlay]
        \ifthenelse{\thepage=\mypagebot}{}{\xdef\myyshiftbot{0}}        
        \xdef\mypagebot{\thepage}
        \node[above right, yshift=\myyshiftbot, text width=\marginparwidth-4pt, inner sep=2pt, #2] (tempnode) at (current page marginpar area.south west) {#1};
        \path (current page marginpar area.south west);
        \pgfgetlastxy{\tempxone}{\tempyone}
        \path (tempnode.north west);
        \pgfgetlastxy{\tempxtwo}{\tempytwo}
        \pgfmathsetmacro{\diffy}{(\tempytwo-\tempyone)}
        \xdef\myyshiftbot{\diffy}
    \end{tikzpicture}
}

\begin{document}
\marpartop{On a journey to find the cure for a Tatarigami's curse, Ashitaka finds himself in the middle of a war between the forest gods and Tatara, a mining colony.}{red}
\marparbot{What begins as an open and shut case of murder soon becomes a mini-drama of each of the jurors' prejudices and preconceptions about the trial, the accused, and each other.}{orange!50!gray}
\lipsum[1-3]
\marparbot{The defense and the prosecution have rested and the jury is filing into the jury room to decide if a young Spanish-American is guilty or innocent of murdering his father.}{green!50!gray}
\marpartop{In this quest he also meets San, the Mononoke Hime.}{blue}
\lipsum[4-7]
\marpartop{On a journey to find the cure for a Tatarigami's curse, Ashitaka finds himself in the middle of a war between the forest gods and Tatara, a mining colony.}{red}
\marparbot{What begins as an open and shut case of murder soon becomes a mini-drama of each of the jurors' prejudices and preconceptions about the trial, the accused, and each other.}{orange!50!gray}
\lipsum[8]
\marpartop{In this quest he also meets San, the Mononoke Hime.}{blue}
\marparbot{The defense and the prosecution have rested and the jury is filing into the jury room to decide if a young Spanish-American is guilty or innocent of murdering his father.}{green!50!gray}
\lipsum[9-12]
\end{document}

Resultado

insira a descrição da imagem aqui

Responder3

(Nota: é uma resposta da comunidade porque a ideia original é de Tom Bombadil.)

Aqui está uma variante da resposta de Tom Bombadil com algumas melhorias:

  • As duas macros são nomeadas \topmarginpare \botmarginparconforme solicitado.

  • Seu primeiro argumento (opcionale vazio por padrão) são algumas opções para o nó TikZ (para permitir preenchimento, desenho de borda, etc.). O segundo argumento é o conteúdo do parágrafo.

  • Existem alguns cálculos para corrigir um bug na tikzpagenodespágina par (edit: Martin Scharer é muito reativo: uma versão com bug corrigido está chegando ao CTAN.).

  • O código usa letoperações (e calca biblioteca TikZ). Acho que é mais legível que as chamadas PGF.

Limites:

  • \botmarginpardeve empilhar os parágrafos na ordem inversa!
  • Se uma pilha estiver cheia, ela não continuará na próxima página. Ele transborda para cima ou para baixo na página.
  • Se \topmarginpar(ou \bormarginpar) for chamado no último parágrafo de uma página, o parágrafo de margem poderá aparecer na próxima página e não na ordem correta.

Aqui estão duas páginas (ímpares e pares):

primeira página (ímpar) segunda página (par)

O código (com meus comentários):

\documentclass[twoside]{report}
\usepackage{tikzpagenodes}
\usepackage{xifthen}
\usetikzlibrary{calc}

\def\myyshifttop{0}
\def\mypagetop{0}
\newcommand{\topmarginpar}[2][]{% tikz options of node, content
  \begin{tikzpicture}[remember picture, overlay]
    % reset position on new page
    \ifthenelse{\thepage=\mypagetop}{}{\xdef\myyshifttop{0}\xdef\mypagetop{\thepage}}
    % a big path with many actions
    \path let
    % patch for bug in tikzpagenodes with even pages
    \p1=(current page marginpar area.north west),
    \p2=(current page marginpar area.north east)
    in \pgfextra{
      \pgfmathsetmacro{\xw}{\x1<\x2?\x1:\x2}
      \pgfmathsetmacro{\yw}{\y1<\y2?\y1:\y2}
      \edef\coord{\xw pt,\yw pt}
    }
    % draw topmarginpar
    node[below right, yshift=\myyshifttop, text width=\marginparwidth-4pt, inner sep=2pt, #1]
    (tempnode) at (\coord) {#2}
    % next position
    let \p1=(\coord), \p2=(tempnode.south west) in \pgfextra{
      \pgfmathsetmacro{\diffy}{(\y2-\y1)}
      \xdef\myyshifttop{\diffy}
    };
  \end{tikzpicture}%
}

\def\myyshiftbot{0}
\def\mypagebot{0}
\newcommand{\botmarginpar}[2][]{% tikz options of node, content
  \begin{tikzpicture}[remember picture, overlay]
    % reset position on new page
    \ifthenelse{\thepage=\mypagebot}{}{\xdef\myyshiftbot{0}\xdef\mypagebot{\thepage}}
    % a big path with many actions
    \path let
    % patch for bug in tikzpagenodes with even pages
    \p1=(current page marginpar area.south west),
    \p2=(current page marginpar area.south east)
    in \pgfextra{
      \pgfmathsetmacro{\xw}{\x1<\x2?\x1:\x2}
      \pgfmathsetmacro{\yw}{\y1<\y2?\y1:\y2}
      \edef\coord{\xw pt,\yw pt}
    }
    % draw botmarginpar
    node[above right, yshift=\myyshiftbot, text width=\marginparwidth-4pt, inner sep=2pt, #1]
    (tempnode) at (\coord) {#2}
    % next position
    let \p1=(\coord), \p2=(tempnode.north west) in \pgfextra{
      \pgfmathsetmacro{\diffy}{(\y2-\y1)}
      \xdef\myyshiftbot{\diffy}
    };
  \end{tikzpicture}%
}

\usepackage{lipsum}

\begin{document}
\topmarginpar[red,fill=yellow!30]{On a journey to find the cure for a
  Tatarigami's curse, Ashitaka finds himself in the middle of a war
  between the forest gods and Tatara, a mining colony.}%
\botmarginpar{What begins as an open and shut case of murder soon
  becomes a mini-drama of each of the jurors' prejudices and
  preconceptions about the trial, the accused, and each other.}%
\lipsum[1-3]%
\botmarginpar[font=\itshape\footnotesize,text=green!50!black]{The
  defense and the prosecution have rested and the jury is filing into
  the jury room to decide if a young Spanish-American is guilty or
  innocent of murdering his father.}%
\topmarginpar[blue]{In this quest he also meets San, the Mononoke
  Hime.}%
\lipsum[4-7]%
\topmarginpar[red]{On a journey to find the cure for a Tatarigami's
  curse, Ashitaka finds himself in the middle of a war between the
  forest gods and Tatara, a mining colony.}%
\botmarginpar[orange!50!gray]{What begins as an open and shut case of
  murder soon becomes a mini-drama of each of the jurors' prejudices and
  preconceptions about the trial, the accused, and each other.}%
\lipsum[8]%
\topmarginpar[blue]{In this quest he also meets San, the Mononoke
  Hime.}%
\botmarginpar[green!50!gray]{The defense and the prosecution have rested
  and the jury is filing into the jury room to decide if a young
  Spanish-American is guilty or innocent of murdering his father.}%
\lipsum[9-12]
\end{document}

Responder4

Fiz um hack simples no marginfitpacote e consegui uma solução simples.

\documentclass[11pt,a4paper]{article}

%% Needs marginfit package to be loaded before:
\RequirePackage{marginfit}

\makeatletter
%% Top marginpar:
\def\marginfit@writepost#1{%
    \write\@auxout{\string\@newl@bel{label@marginfit}{#1}{47040224}}%  %% Note it from aux file entry with @t
}
\def\marginpart{%
    \global\advance\c@marginfit@w\@ne%
    \expandafter\marginfit@writepost\expandafter{\the\c@marginfit@w @m}%
    \@ifnextchar[\marginfit@mpar@ii\marginfit@mpar@i%
}
%% Bottom marginpar:
\def\marginfit@writeposb#1{%
    \write\@auxout{\string\@newl@bel{label@marginfit}{#1}{0}}%  %% Minimum is 0
}
\def\marginparb{%
    \global\advance\c@marginfit@w\@ne%
    \expandafter\marginfit@writeposb\expandafter{\the\c@marginfit@w @m}%
    \@ifnextchar[\marginfit@mpar@ii\marginfit@mpar@i%
}
\makeatother


\begin{document}

\marginpar{OM O MO M M M M M M}

ABC

ABC

ABC

ABC

ABC

ABC

ABC

ABC

ABC


\marginpar{OM O MO M M M M M M\par\medskip\hrule}


ABC


\marginpart{To be Top OM O MO M M M M M M}


\newpage  %--------------------------------------------------------------------


ABC

ABC

ABC

ABC

ABC

ABC

ABC

ABC

ABC


\marginpart{Top OM O MO M M M M M M}

\marginparb{Bottom OM O MO M M M M M M}


ABC

ABC

ABC

ABC

\end{document}

Ele fornece dois novos comandos \marginpartpara marginpar superior e \marginparbmarginpar inferior. O número 47040224 nele é \pdflastypospara a margem superior. Este número pode ser ainda maior, marginfitdefinirá sua posição no posicionamento final para que não ultrapasse a borda superior. Experimente valores mais altos dependendo do tamanho da página. O melhor método para descobrir é primeiro criar um único marginpar no início do documento para que fique no topo e examinar sua entrada no arquivo aux.

É assim que funciona: marginfito pacote salva a posição de todos os marginpars com um rótulo atribuído. Acabei de pegar emprestado o código que grava os ypos do marginpar atual no arquivo aux e configurei o ypos para o máximo (e mínimo 0 para o marginpar inferior).

informação relacionada