我想要的是

我想要的是

这是上一个问题的延续:如何使用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}

MWE 输出

我想要\Me\You命令添加的可能性:

  • 列表(enumerateitemize,和变体)。
  • 示例代码(listings)。
  • 数学模式和表格(arraytabular)。

以下输出是使用非自动代码生成的老薛定谔猫的答案。对于该答案,我唯一无法做的事情就是添加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\dospecialsLaTeX 内核来实现的)。

当实现类似设置时,这些大规模类别代码更改的时间非常敏感verbatim(即从“正常模式”到“逐字模式”的转换有点难以实现)。为了理解你提出的语法的问题,需要意识到 TeX 在从输入流读取字符时会根据内部表动态地为字符分配类别代码,该内部表在 TeX 运行期间执行分配时可能会发生变化\catcode。这个内存表有很多“行”,这些行表示:“在当前状态下,如果在输入流中遇到字符代码 xx,它将被分配类别代码 yy”。这是在标记化(TeX 的眼睛),即在输入文件(流)处理的早期阶段。根据类别代码表,将类别代码附加到刚刚读取的字符上时,结果是一个字符标记。此类标记的两个属性均已冻结:字符代码和类别代码。可以单独检查它们,但一旦标记形成,就无法真正更改它。

现在,重要的一点是:一旦宏被扩展,它抓住了它的论点,并每个参数中的所有内容都会被立即标记化。如前所述,一旦标记化,类别代码就不会再改变(像和\scantokens这样的 e-TeX 原语\detokenize可以做非常有趣的事情,但也有副作用;您不能使用它们对已经标记化的内容执行真正的逐字处理)。

\Me因此,例如,采用一个参数并像下面这样调用的宏\Me{abc \verb|\bla yay \a| def}无法正确处理其参数中的逐字材料,因为当控制序列标记\Me展开时,该参数将被标记如下:

  • ab并且ccatcode 为 11(字母);

  • 一个空格标记(字符代码 32,类别代码 10);

  • 控制\verb序列标记;

  • |类别代码为 12 (其他);

  • 控制\bla序列标记;

  • y,,a类别y代码为 11;

  • 一个太空令牌(是的,只有一个!);

  • 控制\a序列标记;

  • |类别代码为 12 (其他);

  • 一个太空令牌;

  • de并且fcatcode 为 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是我们自己的键(最值得注意的是startshrinkvarwidthfixed 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 widthfixed width=true使盒子具有宽度\maxwidth

    • fixed width=〈some width〉使用规定的宽度;

    • fixed width=false禁用所有这些。

\You如果在、\Me或的第一个参数中\Exercise,您不使用shrinkvarwidthfixed 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}

输出

相关内容