这是上一个问题的延续:如何使用tikzpicture
和防止自定义形状与页眉和页脚发生冲突fancyhdr
?
我想要的是
请考虑这个 MWE(取自薛定谔猫答案的第二个代码):
\documentclass{article}
\usepackage{lipsum}
\def\maxwidth{16cm}
\def\maxheight{20cm}
\usepackage[a4paper,margin=.25in,top=0.2cm,bottom=1.4cm,footskip=0.25in]{geometry}
% From https://tex.stackexchange.com/a/529773/152550
\newif\ifStart
\Startfalse
\newif\ifImage
\Imagefalse
\newif\ifCalloutRight
\CalloutRightfalse
\usepackage[skins,breakable]{tcolorbox}
\newsavebox\OurNiceSandBox
\tcbset{start/.code={\Starttrue},callout right/.code={\CalloutRighttrue},
shrink/.code={\tcbset{whatsapp cont/.style={hbox}}},
whatsapp cont/.style={width=\maxwidth},
whatsapp/.style={empty,breakable,
left=2mm,right=2mm,top=2mm,bottom=2mm,fontupper=\sffamily,
whatsapp cont,after skip=1ex},
whatsap skin/.style={% code for unbroken boxes:
frame code={%
\ifStart
\ifCalloutRight
\path[fill=#1]([xshift=-3mm]frame.north east) -- ++ (9mm,0)
-- ([yshift=-6mm]frame.north east);
\else
\path[fill=#1]([xshift=3mm]frame.north west) -- ++ (-9mm,0)
-- ([yshift=-6mm]frame.north west);
\fi
\fi},
interior code={
\path[fill=#1,rounded corners=3mm]
(frame.south west) rectangle (frame.north east);
},
% code for the first part of a break sequence:
skin first is subskin of={emptyfirst}{%
frame code={%
\ifStart
\ifCalloutRight
\path[fill=#1]([xshift=-3mm]frame.north east) -- ++ (9mm,0)
-- ([yshift=-6mm]frame.north east);
\else
\path[fill=#1]([xshift=3mm]frame.north west) -- ++ (-9mm,0)
-- ([yshift=-6mm]frame.north west);
\fi
\fi},
interior code={
\path[fill=#1]
(frame.south west) [rounded corners=3mm] |-
(frame.north) -| (frame.east) [rounded corners=0mm] |- cycle;
},
},
% code for the middle part of a break sequence:
skin middle is subskin of={emptymiddle}{%
frame code={%
},
interior code={
\path[fill=#1]
(frame.south west) rectangle (frame.north east);
},
},
% code for the last part of a break sequence:
skin last is subskin of={emptylast}{%
frame code={%
},
interior code={
\path[fill=#1]
(frame.north west) [rounded corners=3mm] |-
(frame.south) -| (frame.east) [rounded corners=0mm] |- cycle;
},
}}}
\newtcolorbox{Mebox}[1][]{#1,whatsapp,flush left,whatsap skin=white}
\newcommand{\Me}[2][]{\begin{lrbox}{\OurNiceSandBox}
#2
\end{lrbox}%
\ifdim\wd\OurNiceSandBox<\maxwidth
\begin{Mebox}[shrink,#1]
#2
\end{Mebox}
\else
\begin{Mebox}[#1]
#2
\end{Mebox}
\fi}
\newtcolorbox{Youbox}[1][]{flush right,right skip=15mm,#1,whatsapp,callout right,
whatsap skin=green!60!black}
\newcommand{\You}[2][]{\begin{lrbox}{\OurNiceSandBox}
#2
\end{lrbox}%
\ifdim\wd\OurNiceSandBox<\maxwidth
\begin{Youbox}[shrink,#1]
#2
\end{Youbox}
\else
\begin{Youbox}[#1]
#2
\end{Youbox}
\fi}
\newtcolorbox{Exercisebox}[1][]{#1,whatsapp,coltext=white,center,whatsap skin=blue}
\newcommand{\Exercise}[2][]{\begin{lrbox}{\OurNiceSandBox}
#2
\end{lrbox}%
\ifdim\wd\OurNiceSandBox<\maxwidth
\begin{Exercisebox}[shrink,#1]
#2
\end{Exercisebox}
\else
\begin{Exercisebox}[#1]
#2
\end{Exercisebox}
\fi}
\usepackage[explicit]{titlesec}
\usepackage{eso-pic}
\AddToShipoutPictureBG{\includegraphics[width=\paperwidth,height=\paperheight]{example-image}}
%%%%%%%%%%%%%%%%%%%%
\begin{document}
\Exercise{Hello}
\Me[start]{This is working}
\Me{\includegraphics[scale=0.75]{example-image}}
\You[start]{\includegraphics{example-image-duck}}
\end{document}
我想要\Me
并\You
命令添加的可能性:
- 列表(
enumerate
和itemize
,和变体)。 - 示例代码(
listings
)。 - 数学模式和表格(
array
和tabular
)。
以下输出是使用非自动代码生成的老薛定谔猫的答案。对于该答案,我唯一无法做的事情就是添加listings
代码(它让我困惑Paragraph ended before \lst@next was complete
):
的代码\Me
看起来应该是这样的:
\Me[start]{This is working\\ Lists:
\begin{itemize}
\item Item 1.
\item Item 2.
\end{itemize}
Sample code (it does not work so I edited the output):
\begin{lstlisting}
var i=0
\end{lstlisting}
Table:
\begin{center}\begin{tabular}{|c|c|}
\hline1&2\\\hline
3&4\\\hline
\end{tabular}\end{center}}
旧答案的优缺点
优点:
- 我们可以添加列表和表格。
- 我们可以在新行中添加文本而不会出现问题。
缺点:
- 我们无法添加
listings
。 - 它用
tikzpicture
。 - 它不处理图像框。
我做了什么
我查看了以下链接:
但以上方法都无法帮助我解决我所描述的问题。
答案1
处理逐字材料
逐字材料(lstlisting
其中一种)对于 TeX 和 LaTeX 来说非常微妙。逐字读取某些内容时,所有通常对 TeX 来说是特殊的字符都会暂时不再特殊(空格、行尾、反斜杠、花括号、百分号、 、#
等&
)。这是通过在逐字材料读取时为这些字符分配类别代码(如 12(其他))来实现的(这是通过宏\@makeother
和\dospecials
LaTeX 内核来实现的)。
当实现类似设置时,这些大规模类别代码更改的时间非常敏感verbatim
(即从“正常模式”到“逐字模式”的转换有点难以实现)。为了理解你提出的语法的问题,需要意识到 TeX 在从输入流读取字符时会根据内部表动态地为字符分配类别代码,该内部表在 TeX 运行期间执行分配时可能会发生变化\catcode
。这个内存表有很多“行”,这些行表示:“在当前状态下,如果在输入流中遇到字符代码 xx,它将被分配类别代码 yy”。这是在标记化(TeX 的眼睛),即在输入文件(流)处理的早期阶段。根据类别代码表,将类别代码附加到刚刚读取的字符上时,结果是一个字符标记。此类标记的两个属性均已冻结:字符代码和类别代码。可以单独检查它们,但一旦标记形成,就无法真正更改它。
现在,重要的一点是:一旦宏被扩展,它抓住了它的论点,并每个参数中的所有内容都会被立即标记化。如前所述,一旦标记化,类别代码就不会再改变(像和\scantokens
这样的 e-TeX 原语\detokenize
可以做非常有趣的事情,但也有副作用;您不能使用它们对已经标记化的内容执行真正的逐字处理)。
\Me
因此,例如,采用一个参数并像下面这样调用的宏\Me{abc \verb|\bla yay \a| def}
无法正确处理其参数中的逐字材料,因为当控制序列标记\Me
展开时,该参数将被标记如下:
a
,b
并且c
catcode 为 11(字母);一个空格标记(字符代码 32,类别代码 10);
控制
\verb
序列标记;|
类别代码为 12 (其他);控制
\bla
序列标记;y
,,a
类别y
代码为 11;一个太空令牌(是的,只有一个!);
控制
\a
序列标记;|
类别代码为 12 (其他);一个太空令牌;
d
,e
并且f
catcode 为 11(字母)。
(这是在正常类别代码制度下)。此时,\verb
无法再正常工作,因为\a
(如\bla
)已被标记为单个控制序列标记,而如果\verb
使用正确,它会以这样一种方式设置 catcode 表,即会形成两个 catcode 12 的标记,以便打印 a\
和 a a
。3 个连续空格也存在问题,它们已成为单个空格标记。另一个问题是,在输入中, 之后有一个空格,\bla
而 之后没有\a
,但由于控制序列的标记方式,这种差异在标记过程中消失了(在 的标记化过程中已跳过空格\bla
)。
由于这些原因,人们普遍认为verbatim
材料不能在宏或环境的参数中使用。lstlisting
本质上就像verbatim
,这也适用于它。
解决此问题的一个可靠方法是将敏感材料保存在盒子寄存器中。然后,使用该\usebox
命令,可以在需要的地方输出盒装材料(注意:如果盒子多次重复使用,则此方法非常有效:例如,用 Ti 绘制的象形图钾Z,保存在一个盒子寄存器中并在同一个文档中重复使用数千次)。
如何做到这一点lstlisting
在第二个例子中显示沃纳的回答(lrbox
是 LaTeX 内核定义的环境):
% Reserve a box register and assign it a name. This can be done
% in the preamble or in the document body.
\newsavebox{\myBox}
...
% Store material in the box register (here, verbatim material)
\begin{lrbox}{\myBox}
\begin{lstlisting}
This is read in verbatim mode.
\end{lstlisting}%
\end{lrbox}
...
% Print the box
\usebox{\myBox}
正如您在评论中所问的那样,可以定义一个宏(\saveListing
此处调用)以便为“存储阶段”节省一些击键:
\newcommand*{\saveListing}[1]{\begin{lrbox}{#1}\begin{lstlisting}}
使用此宏,可以逐字读取列表,并将其以这种方式存储在盒子寄存器中\myBox
:
\saveListing{\myBox}
#! /usr/bin/env python3
print(r"\LaTeX is {}!".format("awesome"))
\end{lstlisting}%
\end{lrbox}
话虽如此,我自己不会使用这样的宏,因为:
有了它,在框中保存材料的地方可以看到
\end{lstlisting}
和,但看不到它们的对应部分。这看起来有点奇怪,并且混淆了我文本编辑器(可能还有其他编辑器)中的语法突出显示。\end{lrbox}
\begin
这只是一个复制和粘贴或使用文本编辑器的 LaTeX 特定功能插入适当的
\begin{...} ... \end{...}
对的问题 - 这应该不是问题。
代码更改
我对代码做了一些更改:
重新缩进许多部分;
\hbox
修复了因\You
使用而导致的过满问题right skip
;重构了
\You
、\Me
和\Exercise
;现在,它们只是内部宏周围的微小包装器,其中包含了、和 所\@OurBox
共有的所有逻辑;\Me
\You
\Exercise
将我们所有的 PGF 密钥移至,
/tcb/WhatsApp/
以避免与tcolorbox
密钥或类似于此的应用程序定义的密钥发生潜在冲突(这样,即使有一天tcolorbox
有一个密钥,它也不会与我们的密钥冲突)。/tcb/fixed width
/tcb/WhatsApp/fixed width
\You
,和\Me
\Exercise
而不是一个),以及一个强制参数(即要排版的“内容”)。它们的语法是 \命令[选择1][选择2][内容}。第一个可选参数选择1是我们自己的键(最值得注意的是
start
、shrink
和varwidth
)fixed width
。它在命名空间 中执行/tcb/WhatsApp
。第二个可选参数选择2用于
tcolorbox
键;它在命名空间中执行/tcb
。
我添加了一个
varwidth
键/tcb/WhatsApp
(这取代了shrink, varwidth upper
此答案先前版本中使用的组合)。varwidth
在 的第一个可选参数中使用\You
,\Me
或者\Exercise
当内容参数包含垂直材料(例如段落或列表),并且您需要一个与内容的自然宽度完全相同的框。请注意,使用此选项,自动不会发生换行除非你明确使用
minipage
或类似内容的参数\You
,\Me
或\Exercise
。如果这是个问题,请fixed width
按照下面的说明使用。我
fixed width
在 中添加了一个键/tcb/WhatsApp
。在 的第一个可选参数中使用它\You
,\Me
或者\Exercise
当内容参数包含垂直材料(段落、列表等),并且您希望它以已知宽度排版。然后,您的框将表现得像minipage
。该选项可以以三种方式使用:fixed width
或fixed width=true
使盒子具有宽度\maxwidth
;fixed width=〈some width〉
使用规定的宽度;fixed width=false
禁用所有这些。
\You
如果在、\Me
或的第一个参数中\Exercise
,您不使用shrink
、varwidth
和fixed width
(或仅使用fixed width=false
),那么与@Schrödinger'scat 的原始代码使用,即:
首先对材料进行排版,
lrbox
以测量其自然宽度;如果这个自然宽度严格小于
\maxwidth
,它将以 LR 模式排版(无段落等),并且生成的框将紧密贴合材料(这是通过 实现的/tcb/WhatsApp/shrink
);否则,材料将以
minipage
宽度为 的形式排版\maxwidth
。
例子
下面的例子说明了上面提到的大多数选项。
\documentclass{article}
\usepackage{geometry}
\geometry{a4paper, margin=.25in, top=0.2cm, bottom=1.4cm, footskip=0.25in}
\usepackage{listings}
\usepackage[breakable, skins, xparse]{tcolorbox}
\usepackage{varwidth}
\usepackage{etoolbox}
\usepackage{xparse}
\usepackage{lipsum}
\newcommand*{\maxwidth}{16cm}
% From https://tex.stackexchange.com/a/529773/152550
\makeatletter
\newsavebox{\OurNiceSandBox}
\newif\ifStart
\Startfalse
\newif\ifImage
\Imagefalse
\newif\ifCalloutRight
\CalloutRightfalse
\tcbset{
WhatsApp/.is family,
WhatsApp/.cd, % don't take the risk of overwriting tcolorbox keys
start/.code={\Starttrue},
callout right/.code={\CalloutRighttrue},
whatsapp cont/.code={\pgfqkeys{/tcb}{width=\maxwidth}},
shrink/.style={
/tcb/WhatsApp/whatsapp cont/.code={\pgfqkeys{/tcb}{hbox}}},
varwidth/.style={
/tcb/WhatsApp/whatsapp cont/.code={\pgfqkeys{/tcb}{hbox, varwidth upper}}},
fixed width/.code={%
\ifstrequal{#1}{true}
{\tcbset{WhatsApp/whatsapp cont/.code={\pgfqkeys{/tcb}{width=\maxwidth}}}}
{\ifstrequal{#1}{false}
{}
{\tcbset{WhatsApp/whatsapp cont/.code={\pgfqkeys{/tcb}{width={#1}}}}}%
}%
},
fixed width/.default=\maxwidth,
whatsapp/.code={%
\pgfqkeys{/tcb}{empty, breakable, left=2mm, right=2mm, top=2mm, bottom=2mm,
fontupper=\sffamily, after skip=1ex}%
\pgfqkeys{/tcb/WhatsApp}{whatsapp cont}%
},
whatsapp skin/.code={%
\pgfqkeys{/tcb}{
% Code for unbroken boxes
frame code={
\ifStart
\ifCalloutRight
\path[fill=#1]([xshift=-3mm]frame.north east) -- ++ (9mm,0)
-- ([yshift=-6mm]frame.north east);
\else
\path[fill=#1]([xshift=3mm]frame.north west) -- ++ (-9mm,0)
-- ([yshift=-6mm]frame.north west);
\fi
\fi
},
interior code={
\path[fill=#1,rounded corners=3mm]
(frame.south west) rectangle (frame.north east);
},
% Code for the first part of a break sequence
skin first is subskin of={emptyfirst}{%
frame code={
\ifStart
\ifCalloutRight
\path[fill=#1]([xshift=-3mm]frame.north east) -- ++ (9mm,0)
-- ([yshift=-6mm]frame.north east);
\else
\path[fill=#1]([xshift=3mm]frame.north west) -- ++ (-9mm,0)
-- ([yshift=-6mm]frame.north west);
\fi
\fi
},
interior code={
\path[fill=#1]
(frame.south west) [rounded corners=3mm] |-
(frame.north) -| (frame.east) [rounded corners=0mm] |- cycle;
},
},
% Code for the middle part of a break sequence
skin middle is subskin of={emptymiddle}{
frame code={
},
interior code={
\path[fill=#1]
(frame.south west) rectangle (frame.north east);
},
},
% Code for the last part of a break sequence
skin last is subskin of={emptylast}{
frame code={
},
interior code={
\path[fill=#1]
(frame.north west) [rounded corners=3mm] |-
(frame.south) -| (frame.east) [rounded corners=0mm] |- cycle;
},
},
}%
},
}
\NewTColorBox{Mebox}{O{} O{}}
{
WhatsApp/.cd, #1, whatsapp, whatsapp skin=white, /tcb/.cd, flush left, #2,
}
% I (frougon) removed the 'right skip=15mm' in 'Youbox' because there is
% nothing symmetric in Mebox and it was causing overfull \hbox warnings.
\NewTColorBox{Youbox}{O{} O{}}
{
WhatsApp/.cd, #1, whatsapp, callout right, whatsapp skin=green!40!gray,
/tcb/.cd, flush right, #2,
}
\NewTColorBox{Exercisebox}{O{} O{}}
{
WhatsApp/.cd, #1, whatsapp, whatsapp skin=blue,
/tcb/.cd, coltext=white, center, #2,
}
\newif\ifOurFixedWidth
\renewcommand*{\OurFixedWidthtrue}{\global\let\ifOurFixedWidth=\iftrue}
\renewcommand*{\OurFixedWidthfalse}{\global\let\ifOurFixedWidth=\iffalse}
% Internal macro that factors out common code for \You, \Me and \Exercise.
% #1: box name
% #2: PGF keys run in /tcb/WhatsApp
% #3: PGF keys run in /tcb
% #4: box contents
\NewDocumentCommand{\@OurBox}{ m m m +m }{%
\begingroup
% Check if #2 contains a call to 'fixed width' that is not
% 'fixed width=false'. The code in #2 had better not had side effects
% once the following \endgroup has been executed, otherwise a different
% approach would be needed (e.g., separate macros as we had in a previous
% revision, but this is not as nice to use).
\OurFixedWidthfalse
\tcbset{WhatsApp/.cd,
fixed width/.code={%
\ifstrequal{##1}{false}{}{\OurFixedWidthtrue}},
#2}%
\endgroup
\ifOurFixedWidth
\begin{#1box}[#2][#3]
#4%
\end{#1box}%
\else
\begin{lrbox}{\OurNiceSandBox}
#4%
\end{lrbox}%
\ifdim \wd\OurNiceSandBox<\maxwidth \relax
\begin{#1box}[shrink, #2][#3]
#4%
\end{#1box}%
\else
\begin{#1box}[#2][#3]
#4%
\end{#1box}%
\fi
\fi
}
\NewDocumentCommand{\Me}{ O{} O{} +m }{\@OurBox{Me}{#1}{#2}{#3}}
\NewDocumentCommand{\You}{ O{} O{} +m }{\@OurBox{You}{#1}{#2}{#3}}
\NewDocumentCommand{\Exercise}{ O{} O{} +m }{\@OurBox{Exercise}{#1}{#2}{#3}}
\makeatother
\usepackage{eso-pic}
\AddToShipoutPictureBG{%
\includegraphics[width=\paperwidth,height=\paperheight]{example-image}}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\newsavebox{\myBox} % for storing listings, or anything else
% Offers a small shortcut, but I wouldn't use it, as it doesn't improve
% readability in my opinion and can easily ruin syntax highlighting in the
% text editor.
\newcommand*{\saveListing}[1]{\begin{lrbox}{#1}\begin{lstlisting}}
\begin{document}
% Store one listing in \myBox:
\begin{lrbox}{\myBox}
\begin{lstlisting}
#! /usr/bin/env python3
print(r"\LaTeX is {}!".format("awesome"))
\end{lstlisting}%
\end{lrbox}
\Exercise{Hello}
% One interword space added on either side
\Exercise{\mbox{}\ Hello\ \mbox{}}
\Me[start, fixed width=10cm]{%
This is a list environment typeset in width $10\,$cm:
\begin{itemize}
\item \lipsum[1][1-3]
\item \lipsum[2][1-3]
\item \lipsum[3][1-3]
\end{itemize}
Now, a listing we've previously saved in a box register:\par\nobreak\medskip
\usebox{\myBox}% output the boxed material (can be done several times)
}
\You[start, fixed width]{%
This is a list environment typeset in width \texttt{\string\maxwidth}:
\begin{enumerate}
\item \lipsum[4][1-3]
\item \lipsum[5][1-3]
\item \lipsum[6][1-3]
\end{enumerate}%
}
% Other assignment to \myBox
\begin{lrbox}{\myBox}
\begin{lstlisting}
#! /bin/sh
echo "Oh, TeX is nice. :-)"
\end{lstlisting}%
\end{lrbox}
\Me[start, varwidth]{%
This box is no wider than the natural width of its contents\\
(it has been set with option \texttt{varwidth}).
\bigskip
Some code:\par\nobreak\medskip
\usebox{\myBox}% output the boxed material (can be done several times)
}
\Me{\includegraphics[scale=0.5]{example-image-duck}}
\You[start]{%
Reuse \texttt{\string\myBox} with its new contents: \usebox{\myBox}%
}
\Exercise{How many boxes are there on this page?}
\end{document}