% \iffalse meta-comment % (The MIT License) % % Copyright (c) 2021-2024 Yegor Bugayenko % % Permission is hereby granted, free of charge, to any person obtaining a copy % of this software and associated documentation files (the 'Software'), to deal % in the Software without restriction, including without limitation the rights % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell % copies of the Software, and to permit persons to whom the Software is % furnished to do so, subject to the following conditions: % % The above copyright notice and this permission notice shall be included in all % copies or substantial portions of the Software. % % THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE % SOFTWARE. % \fi % \CheckSum{0} % % \CharacterTable % {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z % Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z % Digits \0\1\2\3\4\5\6\7\8\9 % Exclamation \! Double quote \" Hash (number) \# % Dollar \$ Percent \% Ampersand \& % Acute accent \' Left paren \( Right paren \) % Asterisk \* Plus \+ Comma \, % Minus \- Point \. Solidus \/ % Colon \: Semicolon \; Less than \< % Equals \= Greater than \> Question mark \? % Commercial at \@ Left bracket \[ Backslash \\ % Right bracket \] Circumflex \^ Underscore \_ % Grave accent \` Left brace \{ Vertical bar \| % Right brace \} Tilde \~} % \GetFileInfo{docshots.dtx} % \DoNotIndex{\endgroup,\begingroup,\let,\else,\s,\n,\r,\\,\1,\fi} % \iffalse %<*driver> \ProvidesFile{docshots.dtx} % %\NeedsTeXFormat{LaTeX2e} %\ProvidesPackage{docshots} %<*package> [2024-02-02 0.4.3 TeX Samples Next to Their PDF Snapshots in DTX] % %<*driver> \documentclass{ltxdoc} \usepackage[tt=false, type1=true]{libertine} \usepackage{microtype} \AddToHook{env/verbatim/begin}{\microtypesetup{protrusion=false}} \usepackage[dtx]{docshots} \usepackage{href-ul} \usepackage{xcolor} \PageIndex \EnableCrossrefs \CodelineIndex \RecordChanges \begin{document} \DocInput{docshots.dtx} \PrintChanges \PrintIndex \end{document} % % \fi % \title{|docshots|: \LaTeX{} Package that Renders \\ \TeX{} Samples Next to Their \\ PDF Snapshots\thanks{The sources are in GitHub at \href{https://github.com/yegor256/docshots}{yegor256/docshots}}} % \author{Yegor Bugayenko \\ \texttt{yegor256@gmail.com}} % \date{\filedate, \fileversion} % % \maketitle % % \textbf{\color{red}NB!} % This package doesn't work on Windows! % Also, you must run \TeX{} processor with |--shell-escape| option. % Also, you must have % |pdlaftex|, % \href{https://www.perl.org}{Perl}, % \href{https://www.ghostscript.com}{Ghostscript}, % and \href{https://ctan.org/pkg/pdfcrop}{pdfcrop} % installed. % \section{Introduction} % % When you want to demonstrate to the readers of your documentation % how to use certain \TeX{} commands, the best way would be % to show exactly how the entire document will be rendered in PDF, % using a subprocess that would render it (via |pdflatex|, for example). % To \href{https://tex.stackexchange.com/questions/661027}{my best} % knowledge, there were no packages that would allow % you do exactly this. That's why I created this simple package. % For example, this code: %\iffalse% %<*verb>% %\fi% \begin{verbatim} \begin{docshot} \documentclass{article} \usepackage{xcolor} \pagestyle{empty} \begin{document} Hello, {\color{orange}\LaTeX}! \end{document} \end{docshot} \end{verbatim} %\iffalse % %\fi % is rendered as such: % \begin{docshot} % \documentclass{article} % \usepackage{xcolor} % \pagestyle{empty} % \begin{document} % Hello, {\color{orange}\LaTeX}! % \end{document} % \end{docshot} % Here is a more complex example: % \begin{docshot} % \documentclass{article} % \usepackage{tikz} % \pagestyle{empty} % \begin{document} % \begin{tikzpicture} % \node [circle,draw] (v0) {$v_0$}; % \node [circle,draw=orange,thick, % below right of=v0] (v1) {$v_1$}; % \draw [->] (v0) -- (v1); % \end{tikzpicture} % \end{document} % \end{docshot} % The picture you see on the left side is rendered by a subprocess % executing |pdflatex| with the |.tex| content taken from the source file. % After a successful processing of \TeX{} sources, we use % \href{https://ctan.org/pkg/pdfcrop}{pdfcrop} to trim the document. % We execute |pdflatex| with |-interaction=batchmode| and |-halt-on-error| options. % This means that % \TeX{} processing will stop at the first error. Check your \TeX{} log % to understand what went wrong. % When you render a text instead of a single picture, it's recommended to use % \href{https://ctan.org/pkg/geometry}{geometry} package to change the size % of the page: % \begin{docshot} % \documentclass{article} % \usepackage[paperwidth=2in, % paperheight=2.3in]{geometry} % \begin{document} % ``There is no sadder thing than % a young pessimist, except an old % pessimist'' --- \emph{Mark Twain} % \end{document} % \end{docshot} % You can also use |\pagestyle{empty}| as was done in previous docshots. % \section{Package Options} % \DescribeMacro{pdflatex} % The default command line tool for turning |.tex| into % |.pdf| is |pdflatex|. However, you can change that by using |pdflatex| package option, % for example: %\iffalse %<*verb> %\fi \begin{verbatim} \documentclass{article} \usepackage[pdflatex=/usr/local/bin/pdflatex]{docshot} \begin{document} \begin{docshot} Hello, world! \end{docshot} \end{document} \end{verbatim} %\iffalse % %\fi % \DescribeMacro{gs} % The default location of Ghostscript is just |gs|. % You can change that by using |gs| package option, % for example: %\iffalse %<*verb> %\fi \begin{verbatim} \usepackage[gs=/usr/bin/ghostscript]{docshot} \end{verbatim} %\iffalse % %\fi % \DescribeMacro{pdfcrop} % The default location of |pdfcrop| is just |pdfcrop|. % You can change that by using |pdfcrop| package option, % for example: %\iffalse %<*verb> %\fi \begin{verbatim} \usepackage[pdfcrop=/bin/pdfcrop]{docshot} \end{verbatim} %\iffalse % %\fi % \DescribeMacro{margin} % When we crop the PDF rendered, we leave a margin around the content. The % default value may be changed by the package option |margin|: %\iffalse %<*verb> %\fi \begin{verbatim} \usepackage[margin=10]{docshot} \end{verbatim} %\iffalse % %\fi % \DescribeMacro{nocrop} % You may disable cropping with the help of |nocrop| option: %\iffalse %<*verb> %\fi \begin{verbatim} \usepackage[nocrop]{docshot} \end{verbatim} %\iffalse % %\fi % \DescribeMacro{hspace} % The horizontal distance between the image and its verbatim \TeX{} source % may be configured via |hspace| package option: %\iffalse %<*verb> %\fi \begin{verbatim} \usepackage[hspace=1em]{docshot} \end{verbatim} %\iffalse % %\fi % \DescribeMacro{left} % \DescribeMacro{right} % The default width of the image may be changed by |left| option, while % the width of the verbatim \TeX{} source may be modified by |right| option: %\iffalse %<*verb> %\fi \begin{verbatim} \usepackage[left=2in,right=.5\linewidth]{docshot} \end{verbatim} %\iffalse % %\fi % \DescribeMacro{dtx} % If you use this package inside |.dtx| documentation, add |dtx| package option. Thanks % to this option all comment symbols will be removed from line starts: %\iffalse %<*verb> %\fi \begin{verbatim} \usepackage[dtx]{docshot} \end{verbatim} %\iffalse % %\fi % \DescribeMacro{tmpdir} % The default location of temp files is |_docshots|. You can change this using |tmpdir| option: %\iffalse %<*verb> %\fi \begin{verbatim} \usepackage[tmpdir=/tmp/foo]{docshot} \end{verbatim} %\iffalse % %\fi % \DescribeMacro{runs} % By default, we run |pdflatex| just once for each docshot. You can change this number using the package % option |runs|. This may be helpful if you need Bib\TeX{} processing, for example: %\iffalse %<*verb> %\fi \begin{verbatim} \usepackage[runs=3]{docshot} \end{verbatim} %\iffalse % %\fi % \DescribeMacro{small} % \DescribeMacro{tiny} % You don't have too much freedom in formatting of verbatim texts, but you can make % their font a bit smaller using |small| package option. You can also make it very % small using |tiny| option: %\iffalse %<*verb> %\fi \begin{verbatim} \usepackage[small]{docshot} \end{verbatim} %\iffalse % %\fi % \DescribeMacro{log} % With |log| option you can make us print all possible logs to the main \TeX{} log. % By default, we don't do this and you won't see the output of |pdflatex| compilation, for example. % Just use it like this: %\iffalse %<*verb> %\fi \begin{verbatim} \usepackage[log]{docshot} \end{verbatim} %\iffalse % %\fi % \DescribeMacro{inputminted} % By default, we render the verbatim text using |\VerbatimInput| command. You % can change that and make us use |\inputminted| % from \href{https://ctan.org/pkg/minted}{minted} package instead, for example: %\iffalse %<*verb> %\fi \begin{verbatim} \usepackage{minted} \setminted[java]{frame=lines,framesep=2mm} \usepackage[inputminted=java]{docshot} \end{verbatim} %\iffalse % %\fi % \DescribeMacro{lstinputlisting} % By default, we render the verbatim text using |\VerbatimInput| command. You % can change that and make us use |\lstinputlisting| from % \href{https://ctan.org/pkg/listings}{listings} package instead, for example: %\iffalse %<*verb> %\fi \begin{verbatim} \usepackage{listings} \lstset{basicstyle=\small} \usepackage[lstinputlisting]{docshot} \end{verbatim} %\iffalse % %\fi % \section{Fine-tuning Options} % \DescribeMacro{\docshotOptions} % By default, we render the verbatim text using |\VerbatimInput| command with % no options. You can add your options using |\docshotOptions| command: %\iffalse %<*verb> %\fi \begin{verbatim} \docshotOptions{firstline=4} \begin{docshot} ... \end{docshot} \end{verbatim} %\iffalse % %\fi % The options will be cleaned up by the first render of a docshot. % \section{Prerequisites} % \DescribeMacro{\docshotPrerequisite} % \changes{0.0.3}{2022/10/14}{The command is added to enable copying of supplementary files into docshots snippets processing directory.} % If you need some files to be present next to the |.tex| snippet while % it's rendered by |pdflatex|, you can use |\docshotPrerequisite| with % a single mandatory argument. The argument is the name of a file you need % to be copied from current directory to the temporary directory, where % all snippets are rendered. The command can be used either in the body % of the document or in the preamble --- it doesn't matter, as long as % it shows up before the docshot that needs the prerequisite. For example: %\iffalse %<*verb> %\fi \begin{verbatim} \documentclass{article} \usepackage{docshot} \docshotPrerequisite{duck.jpg} \begin{document} \begin{docshot} \documentclass{article} \usepackage{graphicx} \pagestyle{empty} \begin{document} This is my favorite picture of a duck: \includegraphics[width=2in]{duck.jpg} \end{document} \end{docshot} \end{document} \end{verbatim} %\iffalse % %\fi % \DescribeMacro{\docshotAfter} % If you need something to happen after each |pdflatex| run of a docshot, you may use % |\docshotAfter| command right before |docshot| environment. For example, you have a bibliography file that % you want to be visible for all snippets and you want all of them to % run \href{https://ctan.org/pkg/biber}{biber} in order to process citations: %\iffalse %<*verb> %\fi \begin{verbatim} \documentclass{article} \usepackage{docshot} \docshotPrerequisite{main.bib} \begin{document} \docshotAfter{biber $2} \begin{docshot} \documentclass{acmart} \usepackage[natbib=true]{biblatex} \addbibresource{main.bib} \pagestyle{empty} \begin{document} You must read the book of \citet{knuth1984}. \printbibliography \end{document} \end{docshot} \end{document} \end{verbatim} %\iffalse % %\fi % The script you specify in the first argument of |\docshotAfter| will get % three arguments when it runs: % \begin{description}\setlength\itemsep{0em} % \item[|\$1|] the cycle of |pdflatex| processing (1, 2, ...), % \item[|\$2|] the hash of the snippet, % \item[|\$3|] the name of the |.tex| file. % \end{description} % |$2| is basically equals to |$1| with an % attached |.tex| suffix. |\docshotAfter| applies only to the first |docshot| % environment that goes after it! You must specify |\docshotAfter| before % each |docshot| where you want such post-processing to happen. % \StopEventually{} % \section{Implementation} % \changes{0.0.1}{2022/10/09}{Initial version} % First, we include \href{https://ctan.org/pkg/iexec}{iexec} % in order to execute shell commands, such as |pdflatex| and |pdfcrop|: % \begin{macrocode} \RequirePackage{iexec} % \end{macrocode} % Then, we include \href{https://ctan.org/pkg/fancyvrb}{fancyvrb} % in order to render verbatim texts: % \begin{macrocode} \RequirePackage{fancyvrb} % \end{macrocode} % Then, we include \href{https://ctan.org/pkg/xcolor}{xcolor} % in order to make borders gray: % \begin{macrocode} \RequirePackage{xcolor} % \end{macrocode} % Then, we include \href{https://ctan.org/pkg/graphicx}{graphicx} % in order to be able to use |\includegraphics| command: % \begin{macrocode} \RequirePackage{graphicx} % \end{macrocode} % Then, we include \href{https://ctan.org/pkg/tikz}{tikz} % in order to be able to position elements: % \begin{macrocode} \RequirePackage{tikz} % \end{macrocode} % Then, we process package options with the help of \href{https://ctan.org/pkg/pgfopts}{pgfopts}: % \changes{0.0.4}{2022/10/18}{Package options "lstinputlisting" and "inputminted" introduced to enable printing of verbatim text either via listings or minted packages.} % \changes{0.0.5}{2022/10/24}{Package option "log" added, which enables detailed logging via exec. By default, there is no logging at all.} % \changes{0.2.0}{2022/10/26}{New package option "nocrop" added to allow disabling of "pdfcrop" execution.} % \begin{macrocode} \RequirePackage{pgfopts} \RequirePackage{ifluatex} \RequirePackage{ifxetex} \def\docshots@log{} \pgfkeys{ /docshots/.cd, dtx/.store in=\docshots@dtx, log/.code=\def\docshots@log{log}, nocrop/.code=\def\docshots@nocrop{}, lstinputlisting/.store in=\docshots@lstinputlisting, inputminted/.store in=\docshots@inputminted, tmpdir/.store in=\docshots@tmpdir, tmpdir/.default=_docshots\ifxetex-xe\else\ifluatex-lua\fi\fi, small/.store in=\docshots@small, tiny/.store in=\docshots@tiny, runs/.store in=\docshots@runs, runs/.default=1, pdflatex/.store in=\docshots@pdflatex, pdflatex/.default=pdflatex, gs/.store in=\docshots@gs, gs/.default=gs, pdfcrop/.store in=\docshots@pdfcrop, pdfcrop/.default=pdfcrop, margin/.store in=\docshots@margin, margin/.default=5, hspace/.store in=\docshots@hspace, hspace/.default=.05\linewidth, left/.store in=\docshots@left, left/.default=.3\linewidth, right/.store in=\docshots@right, right/.default=.55\linewidth, tmpdir,pdflatex,gs,pdfcrop,margin,hspace,left,right,runs } \ProcessPgfOptions{/docshots} % \end{macrocode} % Then, we print the version of |pdflatex| to \TeX{} log: % \begin{macrocode} \iexec[\docshots@log,quiet]{"\docshots@pdflatex" --version}% % \end{macrocode} % Then, we print the version of \href{https://ctan.org/pkg/pdfcrop}{pdfcrop} to \TeX{} log: % \begin{macrocode} \ifdefined\docshots@nocrop\else% \iexec[\docshots@log,quiet]{"\docshots@pdfcrop" --version}% \fi% % \end{macrocode} % Then, we print the version of |ghostscript| to \TeX{} log: % \begin{macrocode} \iexec[\docshots@log,quiet]{"\docshots@gs" --version}% % \end{macrocode} % Then, we make a directory where all temporary files will be kept: % \begin{macrocode} \iexec[null]{mkdir -p "\docshots@tmpdir/\jobname"}% % \end{macrocode} % \begin{macro}{\docshots@mdfive} % \changes{0.1.1}{2022/10/26}{New supplementary command added to calculate MD5 sum of a file.} % Then, we define a command for MD5 hash calculating of a file, with % the help of \href{https://ctan.org/pkg/pdftexcmds}{pdftexcmds}: % \begin{macrocode} \RequirePackage{pdftexcmds} \makeatletter \newcommand\docshots@mdfive[1]{\pdf@filemdfivesum{#1}} \makeatother % \end{macrocode} % \end{macro} % \begin{macro}{docshot} % Then, we define |docshot| environment: % \begin{macrocode} \makeatletter \newenvironment{docshot} {\VerbatimEnvironment\begin{VerbatimOut} {\docshots@tmpdir/\jobname/verbatim.tex}} {\end{VerbatimOut}% % \end{macrocode} % If we are in |dtx| mode, leading percent characters must be removed: % \changes{0.3.1}{2022/11/20}{A bug fixed, now handling of leading percentage symbols is done right.} % \begin{macrocode} \ifdefined\docshots@dtx% \iexec[null]{perl -i -0777pe "s/(\\n|^)\\x{25}\\s?/\\1/g" \docshots@tmpdir/\jobname/verbatim.tex}% \fi% % \end{macrocode} % We calculate MD5 hashsum of the file content: % \begin{macrocode} \def\hash{\docshots@mdfive{\docshots@tmpdir/\jobname/verbatim.tex}}% % \end{macrocode} % If the PDF with the required name already exists, we ignore this step. % Otherwise, we copy |verbatim.tex| into new file and run |pdflatex|: % \changes{0.1.0}{2022/10/26}{The output is saved to a hash-named file, for better uniqueness of temporary files.} % \changes{0.4.0}{2022/11/29}{Now, \TeX output has a log message with the number of the line in the source file, which we render.} % \begin{macrocode} \IfFileExists{\docshots@tmpdir/\jobname/\hash.pdf} {\message{docshots: Won't render, the PDF '\docshots@tmpdir/\jobname/\hash.pdf' already exists^^J}} {\iexec[\docshots@log,quiet]{cp "\docshots@tmpdir/\jobname/verbatim.tex" "\docshots@tmpdir/\jobname/\hash.tex"}% \message{docshots: rendering at line no. \the\inputlineno^^J}% \foreach \n in {1,...,\docshots@runs}{% \iexec[\docshots@log, stdout=\docshots@tmpdir/\jobname/\hash.stdout, exit=\docshots@tmpdir/\jobname/\hash.ret, quiet,trace]{cd "\docshots@tmpdir/\jobname" && "\docshots@pdflatex" -interaction=errorstopmode -halt-on-error -shell-escape \hash.tex}% \ifnum\n=1\else% \message{docshots: pdflatex run no.\n^^J}% \fi% \IfFileExists{\docshots@tmpdir/\jobname/after.sh} {\iexec[\docshots@log,quiet]{chmod a+x "\docshots@tmpdir/\jobname/after.sh"} \iexec[\docshots@log,quiet]{cd "\docshots@tmpdir/\jobname" && ./after.sh \n\space \hash\space \hash.tex}} {}}}% % \end{macrocode} % Here we delete |after.sh| which may exist here after the last % compilation of |pdflatex|: % \begin{macrocode} \iexec[\docshots@log,quiet]{rm -f "\docshots@tmpdir/\jobname/after.sh"} % \end{macrocode} % If a cropped version of the PDF with the required name already exists, we ignore this step. % Otherwise, we ask |pdfcrop| to crop the PDF: % \begin{macrocode} \IfFileExists{\docshots@tmpdir/\jobname/\hash.crop.pdf} {\message{docshots: Won't pdfcrop, the PDF '\docshots@tmpdir/\jobname/\hash.crop.pdf' already exists^^J}} {\ifdefined\docshots@nocrop \iexec[\docshots@log,quiet]{cp "\docshots@tmpdir/\jobname/\hash.pdf" "\docshots@tmpdir/\jobname/\hash.crop.pdf"}% \else% \iexec[\docshots@log,quiet]{"\docshots@pdfcrop" --margins 0 "\docshots@tmpdir/\jobname/\hash.pdf" "\docshots@tmpdir/\jobname/\hash.crop.pdf"}% \fi}% \message{docshots: the PDF is ready from line no. \the\inputlineno^^J}% % \end{macrocode} % Finally, we render the two-column content: % \begin{macrocode} \begingroup% \par% \tikz[baseline=(a.north)] \node (a) [draw=gray,inner sep=\docshots@margin, line width=.2pt] {\includegraphics[width=\docshots@left] {\docshots@tmpdir/\jobname/\hash.crop.pdf}};% \hspace{\docshots@hspace}% \begin{minipage}[t]{\docshots@right}% \vspace{0pt}% \ifdefined\docshots@lstinputlisting% \ifdefined\docshots@opts% \expandafter\lstset\expandafter{\docshots@opts}% \fi% \lstinputlisting{\docshots@tmpdir/\jobname/\hash.tex}% \else\ifdefined\docshots@inputminted% \expandafter\inputminted\expandafter[\docshots@opts] {\docshots@inputminted} {\docshots@tmpdir/\jobname/\hash.tex}% \else% \fvset{numbers=left,numbersep=3pt}% \fvset{frame=leftline,framerule=.2pt,rulecolor=\color{gray}}% \fvset{samepage=true}% \fvset{commandchars=none}% \fvset{baselinestretch=1}% \ifdefined\docshots@small% \fvset{fontsize=\small}% \fi% \ifdefined\docshots@tiny% \fvset{fontsize=\scriptsize}% \fi% \ifdefined\docshots@opts% \expandafter\fvset\expandafter{\docshots@opts}% \fi% \VerbatimInput{\docshots@tmpdir/\jobname/\hash.tex}% \fi\fi% \vspace{0pt}% \end{minipage}% \par% \endgroup% \docshotOptions{}% } \makeatother % \end{macrocode} % \end{macro} % \begin{macro}{\docshotPrerequisite} % Then, we define |\docshotPrerequisite| command: % \begin{macrocode} \makeatletter \newcommand\docshotPrerequisite[1]{ \iexec[\docshots@log,quiet]{cp #1 "\docshots@tmpdir/\jobname"}% \message{docshots: File '#1' copied to '\docshots@tmpdir/\jobname/#1'^^J}% } \makeatother % \end{macrocode} % \end{macro} % \begin{macro}{\docshotAfter} % Then, we define |\docshotAfter| command: % \begin{macrocode} \makeatletter \newcommand\docshotAfter[1]{ \iexec[\docshots@log,quiet]{/bin/echo -n '\detokenize{#1}' > "\docshots@tmpdir/\jobname/after.sh"}% \message{docshots: File '\docshots@tmpdir/\jobname/after.sh' created^^J}% } \makeatother % \end{macrocode} % \end{macro} % \begin{macro}{\docshotOptions} % Finally, we define |\docshotOptions| command: % \changes{0.3.0}{2022/11/10}{New command introduced to help specify custom options for verbatim environments.} % \begin{macrocode} \makeatletter \gdef\docshots@opts{} \newcommand\docshotOptions[1]{% \gdef\docshots@opts{#1}% } \makeatother % \end{macrocode} % \end{macro} % \begin{macrocode} \endinput % \end{macrocode} % \Finale %\clearpage %\PrintChanges %\clearpage %\PrintIndex