
В документе с широкими полями я хочу предложить среду рисунка, которая простирается в marginpar-area. Кроме того, я хочу, чтобы текст подписи также размещался в этой области, либо над рисунком, либо под ним, в зависимости от размещения рисунка. Кроме того, это должно работать на четных и нечетных страницах (используя scrbook
в моем случае, но twoside
опция также будет иметь тот же эффект).
То, чего я хочу добиться в конечном итоге, лучше всего проиллюстрируют следующие рисунки (очень быстрые рисунки в Inkscape, не обращайте внимания на разницу в размерах):
Прежде всего, я хотел бы собрать ссылки из \cite
-макросов и вывести их на поле внизу (это работает и, следовательно, не показано в MWE). Широкий рисунок должен иметь свою подпись на поле тоже. Если широкий рисунок появляется внизу, я хотел бы, чтобы ссылки были над подписью рисунка на поле. Может случиться, что есть что-то, что выводится также на верхнем поле. Как вы видите из рисунков, это должно работать в twoside
режиме.
Чтобы определить, где находится фигура, я использую код, предложенный Дэвидом Карлайлом. Чтобы разместить подпись на полях, внизу или вверху страницы, я используюкод, предложенный Хайко Обердиеком. Чтобы проверить, является ли текущая страница четной или нечетной, если используется \ifthispageodd
from KOMA-Script
, а чтобы убедиться, что рисунок выходит за пределы внешнего поля, я использую addmargin*
среду from KOMA-Script
.
За исключением нескольких предупреждений, с которыми я решил разобраться позже, подход работает так, как и ожидалось, на нечетных страницах, но содержимое моей новой widefigure
среды не отображается на четных страницах, хотя в соответствующей области остается место.
Проблема в том, что оба фрагмента кода, упомянутые выше, каким-то образом работают с выходной процедурой (словомизмененияв заголовке может быть не совсем корректным), что для меня является черным ящиком. Я внес только незначительные изменения в код (переименовал David's \floatswitch
в \@floatswitch
, \foo
в \@helper
и изменил аргументы длины макроса \put
Heiko, который использовал для размещения заметок на полях).
Может ли кто-нибудь объяснить, почему содержимое иллюстраций не отображается на четных страницах (или, что еще лучше, сделать так, чтобы оно отображалось)?
MWE длинный из-за фрагментов кода, упомянутых выше. Я попытался добавить некоторые комментарии (в свой код), которые помогут понять подход, код Heiko также был прокомментирован.
\documentclass[paper=a4,11pt]{scrbook}
\usepackage[left=1in,top=1in,headsep=2\baselineskip,
textwidth=26pc,marginparsep=2pc,marginparwidth=12pc,
textheight=44\baselineskip,headheight=\baselineskip]{geometry}
% It took me a while to figure out the (x)color here is not merely used for
% providing color...
\usepackage{xcolor,atbegshi,picture,zref-abspage}
\usepackage{lipsum}
\makeatletter
%\input{../features/widefigures.tex}
% This magic has been posted by David Carlisle on TeX-SX:
% https://tex.stackexchange.com/questions/56017/formatting-floats-differently-based-on-placement
\def\@floatswitch#1#2#3#4{%
\def\@elt##1{\global\value{##1}\the\value{##1}\relax}%
\edef\FS@ckpt{\cl@@ckpt}%
\let\@elt\relax
\hbox to 3sp{%
\vbox{{\FS@ckpt#1\par}}%
\vbox{{\FS@ckpt#2\par}}%
\vbox{{\FS@ckpt#3\par}}%
\vbox{{\FS@ckpt#4\par}}%
\hss}\aftergroup\break}
\def\FS@checkswitch#1{%
\ifdim\wd#1=3sp %
\setbox\z@\box#1%
\begingroup
\vbadness\maxdimen
\setbox\z@\vsplit\z@ to \textheight
\setbox\z@\vbox{\unvbox\z@\global\setbox\@ne\lastbox}%
\setbox\z@\vbox{\unvbox\@ne\unskip\global\setbox\@ne\lastbox}
\setbox\z@\hbox{\unhbox\@ne\unskip
\FS@junk
\global\setbox\@ne\lastbox}%
\endgroup
\else
\global\setbox\@ne\box#1%
\fi}
\def\@comflelt#1{%
\FS@checkswitch#1%
\setbox\@tempboxa
\vbox{\unvbox\@tempboxa\box\@ne\vskip\floatsep}}
\let\saved@cflt\@cflt
\def\@cflt{%
\def\FS@junk{\setbox\z@\lastbox\setbox\z@\lastbox\setbox\z@\lastbox}%
\saved@cflt}
\let\FS@junk\relax
\let\saved@cflb\@cflb
\def\@cflb{%
\def\FS@junk{\setbox\z@\lastbox\setbox\z@\lastbox}%
\saved@cflb}
\def\@helper#1\box\@currbox#2!!{%
\def\@addtocurcol{%
#1%
\let\FS@junk\relax
\FS@checkswitch\@currbox
\box\@ne
#2}}%
\expandafter\@helper\@addtocurcol!!
\def\@wtryfc #1{%
\global\setbox\@outputbox\vbox{%
\unvbox\@outputbox
\vskip\@fpsep
\def\FS@junk{\setbox\z@\lastbox}%
\FS@checkswitch#1%
\box\@ne}}
%
% Provide a macro that allows typesetting into the margin
%
\newcommand\@wide[2]{%#1%
\begin{addmargin*}[0cm]{#1}#2\end{addmargin*}%
}
%
% An environment to deal with figure-like content
%
\newsavebox{\@ContentCollectorBox}
\newenvironment{@CollectContentAndCaption}[1][\linewidth]{%
% This environment collects its content in the box \@ContentCollectorBox,
% the width of the box is given by the optional argument #1 (defaults to
% \linewidth)
% Furthermore, it redefines the \caption macro and saves its content in the
% macros \@CurrCaptionLong and \@CurrCaptionShort.
%
% The box and the two macros are available globally.
\begingroup
\begin{lrbox}{0\null\global\setbox\@ContentCollectorBox}%
\begin{minipage}{#1}
\renewcommand\caption[2][]{%
\gdef\@CurrCaptionLong{##2}%
\ifx\\##1\\
\gdef\@CurrCaptionShort{##2}%
\else
\gdef\@CurrCaptionShort{##1}%
\fi
}%
}{%
\end{minipage}
\end{lrbox}
\endgroup
}
%
% An environment providing a floating figure which extends in the margin.
%
\newenvironment{widefigure}[1][]{%
% Store the optional argument. If provided, add square brackets around.
\ifx\\#1\\
\def\@rgOne{}%
\else
\def\@rgOne{[#1]}%
\fi
%
% Start the environment defined above
\begin{@CollectContentAndCaption}[\dimexpr\textwidth+\marginparsep+\marginparwidth]%
}{%
% End the environment
\end{@CollectContentAndCaption}%
% Start the figure-environment with the optional argument from
% the \begin{widefigure} part. I use \figure here to avoid an
% \expandafter-orgy to expand the argument
\expandafter\figure\@rgOne
% \@floatswitch is a command from within the magic, I just added the @
\@floatswitch{%
% If you comment the \topmarginpar-commands here, the figure content
% will appear at the desired position.
%
% Output the caption.
\topmarginpar{\@CurrCaptionLong}%
% add a rule to the topmargin-collection with a width of zero and the
% height+depth of the box containing the content without the caption
% from the environment used above.
\topmarginpar{\rule{0pt}{%
\dimexpr\ht\@ContentCollectorBox+\dp\@ContentCollectorBox}}%
%
%
% use the content of the widefigure-environment.
%%%
%%% This does not appear on even pages
%%%
\@wide{\wd\@ContentCollectorBox}{\usebox{\@ContentCollectorBox}}%
}{%
% Same as above. If you comment the \botmarginpar-commands here, the
% figure content will appear at the desired position.
%
\botmarginpar{\@CurrCaptionLong}%
\botmarginpar{\rule{0pt}{%
\dimexpr\ht\@ContentCollectorBox+\dp\@ContentCollectorBox}}%
% The bottom-content appears only on odd pages
\@wide{\wd\@ContentCollectorBox}{\usebox{\@ContentCollectorBox}}%
}{%
\@wide{\wd\@ContentCollectorBox}{\usebox{\@ContentCollectorBox}}%
}{%
\@wide{\wd\@ContentCollectorBox}{\usebox{\@ContentCollectorBox}}%
}
\endfigure
}
%
% This magic has been posted by Heiko Oberdiek
% https://tex.stackexchange.com/questions/69517/send-and-stack-marginpar-to-the-top-or-the-bottom-of-the-page
\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#3%
\par
\begingroup
\tbmparItemSep
\endgroup
\unvbox#2%
}%
}%
\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.
\def\@marginparxpos{0pt}\def\@marginparypos{0pt}%
\AtBeginShipout{%
\AtBeginShipoutUpperLeft{%
%
% I added the following definition of the corresponding lengths to be able
% to distinguish odd and even pages.
%
\ifthispageodd{%
\def\@marginparxpos{\dimexpr
1in+\oddsidemargin+\textwidth+\marginparsep\relax}%
\def\@marginparypos{-\dimexpr
1in+\topmargin+\headheight+\headsep+\textheight\relax}%
}{%
\def\@marginparxpos{\dimexpr
1in+\evensidemargin-\marginparsep-\marginparwidth\relax}%
\def\@marginparypos{-\dimexpr
1in+\topmargin+\headheight+\headsep+\textheight\relax}%
}
\put(%
\@marginparxpos,\@marginparypos%
){%
\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
\begin{document}
%
% If you comment the next line, there is an additional error. If someone could
% solve this, help is appreciated. But currently that's not the main issue.
\topmarginpar{}
\begin{widefigure}[t]
\color{green}\rule{\linewidth}{2cm}
\caption{Top placement of figure, odd page.}
\end{widefigure}
\botmarginpar{Bottom margin note seems to be fine.}
\lipsum[2-5]
% Now we are on an even page
\topmarginpar{\lipsum[57]}
\botmarginpar{\lipsum[57]}
\lipsum[1-6]
% On an odd page, again
\begin{widefigure}[b]
\color{green}\rule{\linewidth}{2cm}
\caption{Bottom placement, odd page}
\end{widefigure}
\lipsum[1-3]
% Now we are on an even page!
\begin{widefigure}[t]
\color{red}\rule{\linewidth}{2cm}
\caption[A normal caption]{Top placement, even page. Figure vanished!}
\end{widefigure}
\lipsum
\begin{widefigure}[b]
\color{red}\rule{\linewidth}{2cm}
\caption[A normal caption]{Bottom placement, even page. Figure vanished!}
\end{widefigure}
\lipsum[1-12]
\end{document}
решение1
Я изменил решение, включив подписи. Макрос \gettruepage теперь также возвращает местоположение x,y (для проверки низа или верха). Обратите внимание, поскольку он использует aux-файл, для правильной работы требуется два запуска.
\documentclass[paper=a4,11pt]{scrbook}
\usepackage[left=1in,top=1in,headsep=2\baselineskip,
textwidth=26pc,marginparsep=2pc,marginparwidth=12pc,
textheight=44\baselineskip,headheight=\baselineskip]{geometry}
% It took me a while to figure out the (x)color here is not merely used for
% providing color...
\usepackage{xcolor}
\usepackage{lipsum}
\usepackage{environ}% create an environment using \BODY
% The \gettruepage marcro returns the page number (as \truepage} in places where \thepage won't,
% such as inside floats or paragraphs split over 2 pages.
\pdfpageheight=\paperheight
\pdfpagewidth=\paperwidth
\newcounter{truepageindex}
\newcount{\truepage}% returns page
\newlength{\truex}% returns distance from left side of text area
\newlength{\truey}% returns distance from top of text area
\newcommand{\newtruepage}[4]% #1 = \thetruepageindex, #2 = \thepage, #3 = \pdflastxpos, #4 = \pdflastypos
{\global\expandafter\edef\csname truepage#1\endcsname{#2}%
\global\expandafter\edef\csname truex#1\endcsname{#3}%
\global\expandafter\edef\csname truey#1\endcsname{#4}}
\makeatletter
\newcommand{\gettruepage}{\stepcounter{truepageindex}%
\pdfsavepos
\protected@write\@auxout{}{\string\newtruepage{\thetruepageindex}{\thepage}
{\noexpand\number\pdflastxpos}{\noexpand\number\pdflastypos}}%
\@ifundefined{truepage\thetruepageindex}%
{\truepage=\c@page \truex=0pt \truey=0pt}%
{\truepage=\csname truepage\thetruepageindex\endcsname\relax
\truex=\csname truex\thetruepageindex\endcsname sp\relax
\truey=\csname truey\thetruepageindex\endcsname sp\relax
\truey=\dimexpr \paperheight-\truey-1in-\topmargin-\headheight-\headsep\relax
\advance\truex by -1in
\ifodd\truepage\relax\advance\truex by -\oddsidemargin
\else \advance\truex by -\evensidemargin
\fi}%
}%
\makeatother
% caption handler
\newif{\iffakecaption}
\newcommand{\fakecaptiontext}{}% reserve name
\makeatletter
\newcommand{\fakecaption}[2][\@empty]% #1 = short caption (optional), #2 = long caption
{\global\fakecaptiontrue
\refstepcounter{figure}%
\ifx#1\@empty\def\fakecaptiontext{#2}%
\else\def\fakecaptiontext{#1}%
\fi
\addcontentsline{lof}{figure}{\string\numberline {\thefigure}{\ignorespaces \fakecaptiontext}}%
\gdef\fakecaptiontext{#2}}
\makeatother
% widefigure environment
\newsavebox{\widefigurebox}
\newsavebox{\widefigurecaption}
\newlength{\widefiguretest}% compares middle of figure to middle of text area
\newlength{\widefigureup}% baseline to baseline for caption above
\newlength{\widefiguredown}% baseline to baseline for caption below
\newlength{\widefiguresize}% total height of figure plus caption plus extra \marginparpush
\newcount{\widefigurelast}% last page with figure on bottom
\NewEnviron{widefigure}[1][\empty]{%
\figure[#1]%
\let\oldcaption=\caption
\let\caption=\fakecaption
\fakecaptionfalse
\savebox{\widefigurebox}{\begin{minipage}{\dimexpr \textwidth+\marginparsep+\marginparwidth}%
\BODY\end{minipage}}%
\let\caption=\oldcaption
\settoheight{\widefigureup}{\usebox{\widefigurebox}}%
\settodepth{\widefiguredown}{\usebox{\widefigurebox}}%
\widefiguretest=\dimexpr .5\textheight-.5\widefigureup +.5\widefiguredown\relax
\advance\widefigureup by \marginparpush
\iffakecaption
\savebox{\widefigurecaption}{\parbox{\marginparwidth}%
{\raggedright\textbf{Figure \thefigure: }\fakecaptiontext}}%
\settodepth{\widefiguresize}{\usebox{\widefigurecaption}}%
\advance\widefigureup by \widefiguresize
\settoheight{\widefiguresize}{\usebox{\widefigurecaption}}%
\advance\widefiguredown by \widefiguresize
\advance\widefiguredown by \marginparpush
\fi
\gettruepage
\leavevmode% needed for \rlap and \llap
\ifodd\truepage\relax
\rlap{\usebox{\widefigurebox}}%
\iffakecaption
\ifdim\truey>\widefiguretest\relax
\rlap{\hspace{\textwidth}\hspace{\marginparsep}%
\raisebox{\widefigureup}[0pt][0pt]{\usebox{\widefigurecaption}}}%
\global\widefigurelast=\truepage
\else
\rlap{\hspace{\textwidth}\hspace{\marginparsep}%
\raisebox{-\widefiguredown}[0pt][0pt]{\usebox{\widefigurecaption}}}%
\fi
\fi
\else
\rlap{\hspace{-\marginparwidth}\hspace{-\marginparsep}\usebox{\widefigurebox}}%
\iffakecaption
\ifdim\truey>\widefiguretest\relax
\llap{\raisebox{\widefigureup}[0pt][0pt]{\usebox{\widefigurecaption}}%
\hspace{\marginparsep}}%
\global\widefigurelast=\truepage
\else
\llap{\raisebox{-\widefiguredown}[0pt][0pt]{\usebox{\widefigurecaption}}%
\hspace{\marginparsep}}%
\fi
\fi
\fi
\advance\widefigureup by \widefiguredown
\global\widefiguresize=\widefigureup
\endfigure}
\begin{document}
%
% If you comment the next line, there is an additional error. If someone could
% solve this, help is appreciated. But currently that's not the main issue.
%\topmarginpar{}
\begin{widefigure}[t]
\color{green}\rule{\linewidth}{2cm}% percent needed to prevent blank line at botttom
\caption{Top placement of figure, odd page.}\label{test}
\end{widefigure}
This is a label test for Figure \ref{test}.
The height of the figure plus caption is \the\widefiguresize.
\lipsum[2-5]
% Now we are on an even page
\lipsum[1-6]
% On an odd page, again
\begin{widefigure}[b]
\color{green}\rule{\linewidth}{2cm}%
\caption{Bottom placement, odd page}
\end{widefigure}
\lipsum[1-3]
% Now we are on an even page!
\begin{widefigure}[t]
\color{red}\rule{\linewidth}{2cm}%
\caption[A normal caption]{Top placement, even page. Figure vanished!}
\end{widefigure}
\lipsum
\begin{widefigure}[b]
\color{red}\rule{\linewidth}{2cm}%
\caption[A normal caption]{Bottom placement, even page. Figure vanished!}
\end{widefigure}
\lipsum[1-3]
\listoffigures
\end{document}
решение2
Я нашел нечто, что кажется решением. Хотя оно еще не завершено и встречаются некоторые недовыполненные поля, оно более или менее делает то, что я хочу. Интервалы тоже кажутся неправильными.
Цель этого решения заключалась в том, что я понял (после довольно долгого времени попыток понять код Дэвида), что \@cflt
и \@cflb
расширяются в зависимости от позиционирования float. Дэвид использует это для определения различных версий, \FS@junk
которые отбрасывают ненужные блоки, а я использую это для установки длины, которая равна \vspace*
d либо сверху, либо снизу блока, содержащего содержимое поля.
Это работает до тех пор, пока на одной странице не появятся два float, поскольку я использую только одну длину для хранения вертикального размера float, который просто перезаписывается вторым float. Здесь решением может быть использование соответствующего счетчика (рисунок/таблица) или введение нового.
Более того, я больше не использую механизм, основанный на метках красного цвета, для определения того, является ли страница четной или нечетной, что не приводит к жалобам на неопределенные метки (коду Герберта нужны метки и, следовательно, два запуска, но при втором запуске предупреждений нет).
Вот MWE (опять же длинный, оба фрагмента кода, ссылки на которые приведены выше, включены). Я добавил несколько комментариев к коду Дэвида (насколько я его понимаю), возможно, это поможет другим лучше его понять.
\documentclass[paper=a4,11pt]{scrbook} \usepackage[T1]{fontenc} \usepackage[left=1in,top=1in,headsep=2\baselineskip, textwidth=26pc,marginparsep=2pc,marginparwidth=12pc, textheight=44\baselineskip,headheight=\baselineskip]{геометрия} \usepackage{xcolor,atbegshi,picture,zref-abspage,ragged2e} \usepackage{липсум} \makeatletter % Это волшебство было опубликовано Дэвидом Карлайлом на TeX-SX: % http://tex.stackexchange.com/questions/56017/formatting-floats-differently-based-on-placement % % Макрос \@floatswitch ожидает 4 аргумента, которые являются содержимым float %, который должен быть набран, если он расположен сверху (#1), снизу (#2), на одном % страница(#3) или здесь (#4) % \def\@floatswitch#1#2#3#4{% % (угадайте) Эти строки каким-то образом работают со счетчиками, чтобы избежать этого % счетчик увеличивается более чем на единицу. \def\@elt##1{\global\value{##1}\the\value{##1}\relax}% \edef\FS@ckpt{\cl@@ckpt}% \let\@elt\relax % Контрторговля заканчивается здесь, но используется \FS@ckpt \hbox в 3sp{% \vbox{{\FS@ckpt#1\par}}% \vbox{{\FS@ckpt#2\par}}% \vbox{{\FS@ckpt#3\par}}% \vbox{{\FS@ckpt#4\par}}% \hss}\aftergroup\break% } % % Здесь выбрано правильное поле. Макрос, который выполняет эту работу, — \FS@junk, %, который выбрасывает неиспользуемые коробки. % \def\FS@checkswitch#1{%# \ifdim\wd#1=3sp % \setbox\z@\box#1% \begingroup \vbadness\maxdimen \setbox\z@\vsplit\z@ в \textheight \setbox\z@\vbox{\unvbox\z@\global\setbox\@ne\lastbox}% \setbox\z@\vbox{\unvbox\@ne\unskip\global\setbox\@ne\lastbox}% \setbox\z@\hbox{\unhbox\@ne\unskip \FS@мусор \global\setbox\@ne\lastbox}% \endgroup \еще \глобальный\setbox\@ne\box#1% \фи} % Я понятия не имею о следующем макросе \def\@comflelt#1{% \FS@checkswitch#1% \setbox\@tempboxa \vbox{\unvbox\@tempboxa\box\@ne\vskip\floatsep}} % Очевидно (ну, это всего лишь обоснованное предположение), \@cflt используется, когда float % расположен в верхней части страницы. Когда я это понял, моя проблема была % почти решено. \let\saved@cflt\@cflt \def\@cflt{% \def\FS@мусор{% % Поскольку плавающий элемент расположен в верхней части страницы, мы увеличиваем % \tmbparTopSepLength для получения некоторого пространства в верхней части поля % содержащий содержимое поля \setlength{\tbmparTopSepLength}{\baselineskip}% \addtolength{\tbmparTopSepLength}{\captionlength}% \addtolength{\tbmparTopSepLength}{\contentlength}% \global\tbmparTopSepLength=\tbmparTopSepLength \setbox\z@\lastbox\setbox\z@\lastbox\setbox\z@\lastbox}% \сохранено@cflt} \let\FS@junk\relax % Это (еще одна догадка) команда, которая раскрывается, если число с плавающей точкой % размещается внизу страницы. Метод тот же, что и выше. \let\saved@cflb\@cflb \def\@cflb{% \def\FS@мусор{% \setlength{\tbmparBottomSepLength}{2\baselineskip}% \addtolength{\tbmparBottomSepLength}{\captionlength}% \addtolength{\tbmparBottomSepLength}{\contentlength}% \global\tbmparBottomSepLength=\tbmparBottomSepLength \setbox\z@\lastbox\setbox\z@\lastbox}% \сохранено@cflb} % Опять же, без понятия. \def\@helper#1\box\@currbox#2!!{% \def\@addtocurcol{% #1% \let\FS@junk\relax \FS@checkswitch\@currbox \box\@ne #2}}% \expandafter\@helper\@addtocurcol!! % И снова, без понятия. \def\@wtryfc #1{% \global\setbox\@outputbox\vbox{% \unvbox\@outputbox \vskip\@fpsep \def\FS@junk{\setbox\z@\lastbox}% \FS@checkswitch#1% \box\@ne}} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Среда для работы с контентом, похожим на цифры % \newsavebox{\@ContentCollectorBox} \newsavebox{\@CaptionCollectorBox} \newenvironment{@CollectContentAndCaption}[1][\linewidth]{% % Эта среда собирает свое содержимое в ящике \@ContentCollectorBox, % ширина поля задается необязательным аргументом №1 (по умолчанию % \ширина линии) % Кроме того, он переопределяет макрос \caption и сохраняет его содержимое в % макросы \@CurrCaptionLong и \@CurrCaptionShort. % % Коробка и два макроса доступны по всему миру. \begingroup \begin{lrbox}{0\null\global\setbox\@ContentCollectorBox}% \begin{minipage}{#1}% \renewcommand\caption[2][]{% \gdef\@CurrCaptionLong{##2}% \ifx\\##1\\ \gdef\@CurrCaptionShort{##2}% \еще \gdef\@CurrCaptionShort{##1}% \фи }% }{% \end{minipage}% \end{lrbox}% \endgroup } % % Среда, обеспечивающая плавающую фигуру, выступающую за пределы поля. % \newenvironment{widefigure}[1][]{% % Сохраните необязательный аргумент. Если он указан, добавьте квадратные скобки вокруг. \ifx\\#1\\ \def\@rgOne{}% \еще \def\@rgOne{[#1]}% \фи % % Запустить среду, определенную выше \begin{@CollectContentAndCaption}[\dimexpr\textwidth+\marginparsep+\marginparwidth]% }{% % Конец окружающей среды \end{@CollectContentAndCaption}% \begin{lrbox}{\@CaptionCollectorBox}% \begin{minipage}{\marginparwidth}% \RaggedRight \@CurrCaptionLong \end{мини-страница} \end{lrbox} % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Это часть решения (и создает новые проблемы) % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Здесь я глобально задаю две длины, которые содержат вертикальный размер % подпись и содержание \setlength{\captionlength}{\ht\@CaptionCollectorBox}% \addtolength{\captionlength}{\dp\@CaptionCollectorBox}% \global\captionlength=\captionlength\relax \setlength{\contentlength}{\ht\@ContentCollectorBox}% \addtolength{\contentlength}{\dp\@ContentCollectorBox}% \global\contentlength=\contentlength\relax % Запустить среду figure с необязательным аргументом из % часть \begin{widefigure}. Я использую \figure здесь, чтобы избежать % \expandafter-orgy для расширения аргумента \expandafter\figure\@rgOne % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Это тоже новое. Здесь встречаются некоторые недозаполненные ящики, которые все еще нужно % проверено. % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % \@floatswitch — это команда из магии, я просто добавил @ \@floatswitch{% \ifodd\c@page% \rlap{\usebox{\@ContentCollectorBox}}% \vbox в 0pt{% \rlap{% \hspace*{\dimexpr\textwidth+\marginparsep}% \usebox{\@CaptionCollectorBox}% }% \vss } \еще \rlap{\hspace*{\dimexpr-\marginparwidth-\marginparsep}\usebox{\@ContentCollectorBox}}% \vbox в 0pt{% \llap{% \usebox{\@CaptionCollectorBox}% \hspace*{\marginparsep}% }% \vss }% \фи }{% % % Примерно то же самое, что и выше, но для нижнего расположения. % \ifodd\c@page% \vbox в 0pt{% \vss \rlap{\hspace*{\dimexpr\textwidth+\marginparsep}% \usebox{\@CaptionCollectorBox}% }% \vspace*{\baselineskip}% }% \rlap{\usebox{\@ContentCollectorBox}}% \еще \vbox в 0pt{% \vss \llap{% \usebox{\@CaptionCollectorBox}% \hspace*{\marginparsep}% }% \vspace*{\baselineskip}% }% \rlap{\hspace*{\dimexpr-\marginparwidth-\marginparsep}\usebox{\@ContentCollectorBox}}% \фи }{% % Мне все еще нужно что-то для размещения на странице % \@wide{\wd\@ContentCollectorBox}{\usebox{\@ContentCollectorBox}}% }{% % Я не хочу, чтобы цифры были размещены [здесь]. В любом случае, мне нужно решение % это тоже. % \@wide{\wd\@ContentCollectorBox}{\usebox{\@ContentCollectorBox}}% }% \endfigure } % % Это волшебство было опубликовано Хайко Обердиком % http://tex.stackexchange.com/questions/69517/send-and-stack-marginpar-to-the-top-or-the-bottom-of-the-page \providecommand*{\c@zabspage}{\c@abspage} % Я определяю четыре новые длины, которые используются для того, чтобы оставить место вверху и % нижняя часть tbmpar-узла \newlength\tbmparTopSepLength \newlength\tbmparBottomSepLength % и два, которые сохраняют вертикальный размер полей, содержащих заголовок % и содержание поплавка \newlength\captionlength \newlength\contentlength % Две команды, которые развернуты в верхней и нижней части поля, содержащие % содержимое поля \newcommand*{\tbmparTopSep}{% \vspace*{\tbmparTopSepLength}% } \newcommand*{\tbmparBottomSep}{% \vspace*{\tbmparBottomSepLength}% } % Макрос для сброса длин \newcommand\restoreSeps{% \global\tbmparTopSepLength0pt\relax%\@zp \global\tbmparBottomSepLength0pt\relax } \restoreСепс % Следующие строки снова код Хейко % * Пользовательские макросы для настройки % % \tbmparItemSep вставляется между примечаниями на полях % \tbmparMiddleSep вставляется между верхними и нижними примечаниями. \newcommand*{\tbmparItemSep}{% \vspace{1ex минус .5ex}% \хруле \vspace{1ex минус .5ex}% } \newcommand*{\tbmparMiddleSep}{% \vspace*{0pt плюс 1fil}% } % * Отладочные сообщения % \newcommand*{\tbmparDebug}[1]{% \typeout{[tbmpar] #1}% } % * Управление метками для запоминания абсолютного номера страницы % % \tbmpar@PageByLabel сохраняет и загружает абсолютный номер страницы из % метка и определяет \tbmpar@page с абсолютным номером страницы или % ноль, если метка еще не доступна. \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@страница{% \zref@extractdefault{tbmpar\the\c@tbmpar@item}{abspage}{0}% }% \zref@refused{tbmpar\the\c@tbmpar@item}% \tbmparDebug{Элемент \the\c@tbmpar@item\пространство на странице \tbmpar@page}% } % * Управление регистром ящиков \newcount\c@tbmpar@box \c@tbmpar@box\z@ \let\tbmpar@boxfreelist\@empty % Получите новый бесплатный ящик регистра либо из бесплатного списка, либо, % если свободный список пуст, выделить новый регистр ящика. \newcommand*{\tbmpar@NextBox}[1]{% \@следующий#1\tbmpar@boxfreelist{% \tbmparDebug{Повторно используемый ящик: #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{Новый ящик: #1}% }% } % Поместить свободный ящик в список свободных. \новаякоманда*{\tbmpar@FreeBox}[1]{% \begingroup \let\@elt\relax \xdef\tbmpar@boxfreelist{% \tbmpar@boxfreelist \@elt#1% }% \tbmparDebug{Свободный ящик: #1}% \endgroup } \newsavebox{\tbmpar@box} % Каждый marginpar помещается в поле, которое инициализируется как % parbox/министраница. \newcommand*{\tbmparBoxSetup}{} \newcommand{\tbmpar@VBox}[1]{% \vbox{% \color@begingroup \hsize\ширина_маржинального_параметра \edef\tbmpar@restore@ifminipage{% \if@minipage \noexpand\@minipagetrue \еще \noexpand\@minipagefalse \фи }% \@parboxrestore \@marginparreset \tbmparBoxSetup #1% \tbmpar@restore@ifminipage \color@endgroup }% } % Макрос \tbmpar@marginpar ищет страницу, на которой находится заметка на полях % принадлежит, сохраняет заметку в ящике и добавляет ящик к % регистра сбора заметок страницы. % Каждой странице назначается ящик-сборщик регистров, которые собирают % верхние ноты и регистр, собирающий нижние ноты. % Имя регистра ящика — \tbmpar@box. \newcommand{\tbmpar@marginpar}[4]{% \ifhmode \@bsphack \фи \tbmpar@PageByLabel \ifnum\tbmpar@страница>\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{Использовать поле: \tbmpar@currbox}% \expandafter\let\expandafter\tbmpar@currbox \csname tbmpar@#1box\tbmpar@page\endcsname \global\setbox\tbmpar@currbox\tbmpar@VBox{% \unvbox#3% \пар \begingroup \tbmparItemSep \endgroup \unvbox#2% }% }% \фи \ifhmode \@esphack \фи } \newcommand*{\topmarginpar}{% \tbmpar@marginpar{top}\tbmpar@currbox\tbmpar@box } \newcommand*{\botmarginpar}{% \tbmpar@marginpar{bot}\tbmpar@box\tbmpar@currbox } % Во время отгрузки мы ищем регистры коллектора коробок этого % страницы и установите эти поля в поле marginpar с учетом % \topskip и \maxdepth. \def\@marginparxpos{0pt}\def\@marginparypos{0pt}% \AtBeginShipout{% \AtBeginShipoutUpperLeft{% % % Я добавил следующее определение соответствующих длин, чтобы иметь возможность % для различения четных и нечетных страниц. % \ifodd\c@page% \def\@marginparxpos{\dimexpr 1in+\oddsidemargin+\textwidth+\marginparsep\relax}% \def\@marginparypos{-\dimexpr 1in+\topmargin+\headheight+\headsep+\texttheight\relax}% \еще \def\@marginparxpos{\dimexpr 1in+\evensidemargin-\marginparsep-\marginparwidth\relax}% \def\@marginparypos{-\dimexpr 1in+\topmargin+\headheight+\headsep+\texttheight\relax}% \фи \помещать(% \@marginparxpos,\@marginparypos% ){% \begingroup \global\let\tbmpar@inuse=N% \setbox\tbmpar@box=\tbmpar@VBox{% \штраф-\@M % % Здесь я вставляю пробел вверху % \tbmparTopSep \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% }% % % Здесь я вставляю пробел внизу % \tbmparBottomSep }% \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 \фи \endgroup }% }% % В конце длины сбрасываются. \restoreСепс } \makeatother \begin{документ} \lipsum[1] \topmarginpar{Более длинная верхняя нота! Более длинная верхняя нота! Более длинная верхняя нота! Более длинная верхняя нота! } \begin{widefigure}[т] \color{green}\rule{\linewidth}{2cm} \caption{Более длинная подпись вверху! Более длинная подпись вверху! Более длинная подпись вверху! Более длинная подпись вверху!} \end{widefigure} \botmarginpar{Нижнее поле находится внизу.} \lipsum[2-5] % Теперь мы на четной странице \botmarginpar{Более длинная нижняя нота! Более длинная нижняя нота! Более длинная нижняя нота!} \begin{widefigure}[б] \color{green}\rule{\linewidth}{2cm} \caption{Нижнее размещение с более длинной подписью. Нижнее размещение с более длинной подписью. Нижнее размещение с более длинной подписью. Нижнее размещение с более длинной подписью. Нижнее размещение с более длинной подписью. } \end{widefigure} \lipsum[1-3] \begin{widefigure}[т] \color{red}\rule{\linewidth}{1см} \caption{Верхняя подпись, определенная первой.} \end{widefigure} \topmarginpar{Верхняя нота со слишком большим расстоянием.} \botmarginpar{Нижняя нота с тем же расстоянием (в данном случае ок).} \begin{widefigure}[б] \color{red}\rule{\linewidth}{2cm} \caption{Нижняя подпись, определенная второй. Эта длиннее верхней. один. Также содержание длиннее.} \end{widefigure} \lipsum \end{документ}