![Отправлять (и размещать) \marginpar в верхней или нижней части страницы](https://rvso.com/image/254706/%D0%9E%D1%82%D0%BF%D1%80%D0%B0%D0%B2%D0%BB%D1%8F%D1%82%D1%8C%20(%D0%B8%20%D1%80%D0%B0%D0%B7%D0%BC%D0%B5%D1%89%D0%B0%D1%82%D1%8C)%20%5Cmarginpar%20%D0%B2%20%D0%B2%D0%B5%D1%80%D1%85%D0%BD%D0%B5%D0%B9%20%D0%B8%D0%BB%D0%B8%20%D0%BD%D0%B8%D0%B6%D0%BD%D0%B5%D0%B9%20%D1%87%D0%B0%D1%81%D1%82%D0%B8%20%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D1%8B.png)
Я хочу определить что-то вроде
\topmarginpar{<text>}
, что по сути создаст\marginpar{<text>}
и выровняет его верх по верху текущей страницы.Я также хотел бы, чтобы несколько
\topmarginpar
вызовов размещали эти\marginpar
объекты друг на друге (а не накладывали друг на друга).И наконец, я хотел бы создать подобную
\bottommargin
команду.
Есть ли какой-то существующий пакет, предоставляющий такую функциональность, или я предоставлен сам себе (я свободно владею LaTeX, но бесполезен в работе с сырым TeX)?
Спасибо за любые предложения...
решение1
Это решение касается процедуры асинхронного вывода:
Страницы идентифицируются по абсолютным номерам страниц, а правильные номера страниц доступны во втором запуске LaTeX с использованием меток.
Каждая страница получает два ящика-коллектора, которые собирают заметки на полях сверху и снизу. Во время отправки абсолютный номер страницы известен, и ящики-коллекторы для страницы выводятся в области "marginpar" страницы.
Регистры ящиков динамически выделяются и управляются в пуле. После отправки страницы ее регистры ящиков коллектора освобождаются и возвращаются в пул.
\topskip
и\maxdepth
уважаются за область «marginpar».Слишком большое количество заметок на полях страницы рассматривается как
\vbox
предупреждение о переполнении.LaTeX
\marginpar
не поддерживается, заметки на полях разных типов не знают друг друга и будут с легкостью накладываться друг на друга.
Пример документа:
\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}
решение2
Вот попытка использованияМартин Шаррер's пакетtikzpagenodes. Поскольку он использует remember picture, overlay
опцииТикЗ, вам нужно будет скомпилировать дважды, чтобы сделать это правильно. Он предоставляет две команды \marpartop
, и \marparbot
обе принимают два аргумента: содержимое и цвет текста, это можно легко расширить, чтобы сделать более настраиваемым.
Код
\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}
Результат
решение3
(Примечание: это ответ сообщества, поскольку оригинальная идея принадлежит Тому Бомбадилу.)
Вот вариант ответа Тома Бомбадила с несколькими улучшениями:
Оба макроса названы
\topmarginpar
так,\botmarginpar
как и требовалось.Их первый аргумент (необязательныйи пусто по умолчанию) — некоторые опции для узла TikZ (разрешить заполнение, рисование границы и т. д.). Их второй аргумент — содержимое абзаца.
Есть некоторые расчеты для исправления ошибки
tikzpagenodes
с четной страницей (редактирование: Мартин Шарер очень оперативно реагирует: версия с исправленной ошибкой выходит на CTAN.).Код использует
let
операции (иcalc
библиотеку TikZ). Я думаю, что он более читабельный, чем вызовы PGF.
Ограничения:
\botmarginpar
следует располагать абзацы в обратном порядке!- Если стопка заполнена, она не переходит на следующую страницу. Она переполняется вверх или вниз страницы.
- Если
\topmarginpar
(или\bormarginpar
) вызывается в последнем абзаце страницы, абзац на полях может появиться на следующей странице и не в правильном порядке.
Вот две страницы (четная и нечетная):
Код (с моими комментариями):
\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}
решение4
Я сделал простой взлом marginfit
пакета и получил простое решение.
\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}
Он предоставляет две новые команды \marginpart
для верхнего marginpar и \marginparb
нижнего marginpar. Число 47040224 в нем относится \pdflastypos
к самому верхнему marginpar. Это число может быть еще больше, marginfit
оно установит его положение в конечном размещении так, чтобы оно не пересекало верхний край. Попробуйте более высокие значения в зависимости от размера вашей страницы. Лучший способ выяснить это — сначала создать один marginpar в начале документа, чтобы он был на самом верху, и посмотреть на его запись в файле aux.
Вот как это работает: marginfit
пакет сохраняет позицию y всех marginpars с назначенной меткой. Я просто позаимствовал код, который записывает ypos текущего marginpar в aux-файл и сделал так, чтобы установить ypos на максимум (и минимум 0 для нижнего marginpar).