% % This is file `chemobabel.sty', % for generating chemical structural formulas % using Open Babel and Inkscape. % % Copyright 2014-2022 Acetaminophen (Hironobu YAMASHITA) % Email : h.y.acetaminophen[a t]gmail.com % GitHub : https://github.com/aminophen % Blog : http://acetaminophen.hatenablog.com/ % Twitter : @aminophen % % This work is based on a lot of resources published online. % - Noel O'Boyle http://baoilleach.blogspot.jp/ % - Jakob Lykke Andersen http://imada.sdu.dk/~jlandersen/ % - TeX Forum http://oku.edu.mie-u.ac.jp/tex/ % \NeedsTeXFormat{LaTeX2e} \ProvidesPackage{chemobabel} [2022/10/22 v0.9l Chemical structures with Open Babel] \def\chemob@bel@error{\PackageError{chemobabel}} \def\chemob@bel@warn{\PackageWarningNoLine{chemobabel}} %% Start of package main code %% Thanks: http://imada.sdu.dk/~jlandersen/latex/graphvizObabel.sty \RequirePackage{verbatim} \RequirePackage{graphicx} %% ----- Check graphics/x driver ----- % prefer .pdf but fallback to .eps if necessary % --- currently known drivers all support .eps \@ifundefined{Gin@rule@.pdf}{\@ifundefined{Gin@rule@.eps}{% \chemob@bel@warn{Unsupported situation? cannot happen}}{}% % EPS: dvips.def \def\chemob@belimgExt@default{eps}% }{% % PDF>EPS: dvipdfmx.def, dvisvgm.def, luatex.def, pdftex.def, xetex.def \def\chemob@belimgExt@default{pdf}% } %% ----- Check status ----- \chardef\chemob@bel@status\z@ %% ----- External command execution ----- \ifx\directlua\@undefined \def\chemob@bel@exec#1{\immediate\write18{#1}} \ifx\pdfshellescape\@undefined \ifx\shellescape\@undefined \chardef\chemob@bel@shellescape \@ne % fallback no warning \else % xelatex, hilatex \chardef\chemob@bel@shellescape \shellescape \fi \else % (pdf)latex, platex, uplatex \chardef\chemob@bel@shellescape \pdfshellescape \fi \else \def\chemob@bel@exec#1{\directlua{os.execute[[#1]]}} \chardef\chemob@bel@shellescape =\directlua{tex.sprint(status.shell_escape)} \fi %% ----- Ensure -shell-escape when processing ChemFigFile.tex \edef\chemob@bel@jobname{\jobname} {\escapechar=-1 \xdef\chemob@bel@outname{\string\ChemFigFile}} \ifx\chemob@bel@jobname\chemob@bel@outname \ifnum\chemob@bel@shellescape=\@ne\else \chemob@bel@error{% Processing \chemob@bel@outname.tex needs -shell-escape.\MessageBreak Run 'pdflatex -shell-escape \chemob@bel@outname.tex'}\@ehc \fi \fi %% ----- Make a directory for images ----- \newcommand\chemobabelimgdir{chemobabelimgdir} \chemob@bel@exec{mkdir \chemobabelimgdir} %% ----- Definition of external commands ----- \def\smilesob@bel@obabelcmd#1#2{obabel -:"#1" -O \smilesob@belGetName.svg #2} \def\chemob@bel@obabelcmd#1#2{obabel "#1" -O \chemob@belGetName.svg #2} % % inkscape 0.92 or earlier \def\chemob@bel@inkscapeoldcmd#1#2{inkscape -f #1.svg --export-#2=#1.#2} % inkscape major version 1.0 \def\chemob@bel@inkscapecmd#1#2{inkscape #1.svg --export-type=#2} % % librsvg \def\chemob@bel@librsvgcmd#1#2{rsvg-convert -f #2 -o #1.#2 #1.svg} % % crop PDF \def\chemob@bel@pdfcropcmd#1{pdfcrop #1.pdf}% -> #1-crop.pdf % crop EPS \def\chemob@bel@epscropcmd#1{ps2eps -f #1.eps}% -> #1.eps.eps %% ----- Common part of \smilesobabel and \chemobabel ----- \def\chemob@bel@common@maybeimg#1#2#3{% \def\chemob@belImgName{#2\chemob@bel@maybecrop.\chemob@belimgExt}% \IfFileExists{\chemob@belImgName}{% the image exists: include it #1{\chemob@belImgName}% }{% the image was not created - show a warning and a hint \chemob@bel@warn{Processing of #3\space failed}% \fbox{\begin{minipage}{0.9\textwidth} Warning in chemobabel: #3\space was not processed. \ifnum\chemob@bel@shellescape=\@ne Please make sure that \texttt{obabel}, \ifx\chemob@bel@svgtoimgcmd\chemob@bel@librsvgcmd \texttt{rsvg-convert} \else \texttt{inkscape} \fi \ifx\chemob@bel@cropcmd\chemob@bel@pdfcropcmd and \texttt{pdfcrop} \else\ifx\chemob@bel@cropcmd\chemob@bel@epscropcmd and \texttt{ps2eps} \fi\fi are installed.\\ \else Remember to run \texttt{latex} (\texttt{pdflatex}, \texttt{platex}, etc.) with the \texttt{-shell-escape} option.\\ \fi \IfFileExists{#2.log}{% obabel log (might be empty): \verbatiminput{#2.log}% }{% No log file.% }% \end{minipage}}% \global\chardef\chemob@bel@status\@ne }% } %% ----- Common part of \smilesobabel and \chemobabel end ----- %% ----- Definition of \smilesobabel ----- \newcounter{smilesob@belCounter} \setcounter{smilesob@belCounter}{1} \newcommand\smilesob@belPrefix{\chemobabelimgdir/smilesobabelimg} \newcommand\smilesob@belGetName{\smilesob@belPrefix\arabic{smilesob@belCounter}} % 1: (optional) options for includegraphics % 2: SMILES notation % 3: options for obabel \newcommand\smilesobabel{\@ifstar{\smilesobabel@i}{\smilesobabel@ii}} \newcommand\smilesobabel@i[1][scale=1]{% \def\smilesobabel@next{\includegraphics[clip,#1]}% \begingroup \let\do\@makeother \dospecials \catcode`\{=1 \catcode`\}=2 \@smilesobabel@i } \newcommand\smilesobabel@ii[1][scale=1]{% \def\smilesobabel@next{\includegraphics[clip,#1]}% \begingroup \let\do\@makeother \dospecials \catcode`\{=1 \catcode`\}=2 \@smilesobabel@ii } \def\@smilesobabel@i#1{% \endgroup \@smilesobabel@main{#1}{}% } \def\@smilesobabel@ii#1#2{% \endgroup \@smilesobabel@main{#1}{#2}% } \def\@smilesobabel@main#1#2{% \chemob@bel@exec{% \smilesob@bel@obabelcmd{#1}{#2} 2>\smilesob@belGetName.log && \chemob@bel@svgtoimgcmd{\smilesob@belGetName}{\chemob@belimgExt} 2>>\smilesob@belGetName.log || rm -f \smilesob@belGetName.\chemob@belimgExt}% \chemob@bel@exec{\chemob@bel@cropcmd{\smilesob@belGetName}}% \chemob@bel@common@maybeimg{\smilesobabel@next}{\smilesob@belGetName}{SMILES string}% \addtocounter{smilesob@belCounter}{1}% select next name } %% ----- Definition of \smilesobabel end ----- %% ----- Definition of \chemobabel ----- \newcounter{chemob@belCounter} \setcounter{chemob@belCounter}{1} \newcommand\chemob@belPrefix{\chemobabelimgdir/chemobabelimg} \newcommand\chemob@belGetName{\chemob@belPrefix\arabic{chemob@belCounter}} % 1: (optional) options for includegraphics % 2: original files (.mol, .cdx, etc.) % 3: options for obabel \newcommand\chemobabel{\@ifstar{\chemobabel@i}{\chemobabel@ii}} \newcommand\chemobabel@i[1][scale=1]{% \def\chemobabel@next{\includegraphics[clip,#1]}% \begingroup \let\do\@makeother \dospecials \catcode`\{=1 \catcode`\}=2 \@chemobabel@i } \newcommand\chemobabel@ii[1][scale=1]{% \def\chemobabel@next{\includegraphics[clip,#1]}% \begingroup \let\do\@makeother \dospecials \catcode`\{=1 \catcode`\}=2 \@chemobabel@ii } \def\@chemobabel@i#1{% \endgroup \@chemobabel@main{#1}{}% } \def\@chemobabel@ii#1#2{% \endgroup \@chemobabel@main{#1}{#2}% } \def\@chemobabel@main#1#2{% \IfFileExists{"#1"}{% % the file exists: start processing % (``#1'' can contain spaces, so ``"'' required) \chemob@bel@exec{% \chemob@bel@obabelcmd{#1}{#2} 2>\chemob@belGetName.log && \chemob@bel@svgtoimgcmd{\chemob@belGetName}{\chemob@belimgExt} 2>>\chemob@belGetName.log || rm -f \chemob@belGetName.\chemob@belimgExt}% \chemob@bel@exec{\chemob@bel@cropcmd{\chemob@belGetName}}% \chemob@bel@common@maybeimg{\chemobabel@next}{\chemob@belGetName}{file ``#1''}% }{% % the file does not exist: show a std error \chemob@bel@error{File ``#1'' not found}\@ehc \fbox{\begin{minipage}{0.9\textwidth} Error in chemobabel: the file ``#1'' does not exist. \end{minipage}}% }% \addtocounter{chemob@belCounter}{1}% select next name } %% ----- Definition of \chemobabel end ----- %% ----- Check if no chemical structures ----- \def\chemob@bel@ifnochem{% \let\chemob@bel@nochem\relax \ifnum\value{smilesob@belCounter}=\@ne \ifnum\value{chemob@belCounter}=\@ne \let\chemob@bel@nochem\@empty \fi\fi \ifx\chemob@bel@nochem\@empty \expandafter\@firstoftwo \else \expandafter\@secondoftwo \fi } %% ----- Declaration of options ----- % load macros for extracting \chemobabel and \smilesobabel commands \let\chemob@bel@extract\relax \DeclareOption{extract}{\let\chemob@bel@extract\@empty} % define the image extension \DeclareOption{pdf}{% \def\chemob@belimgExt{pdf}% \let\chemob@bel@cropcmd\chemob@bel@pdfcropcmd \def\chemob@bel@maybecrop{-crop}} \DeclareOption{eps}{% \def\chemob@belimgExt{eps}% \let\chemob@bel@cropcmd\chemob@bel@epscropcmd \def\chemob@bel@maybecrop{.eps}} % determine image conversion command \DeclareOption{inkscape-old}% {\let\chemob@bel@svgtoimgcmd\chemob@bel@inkscapeoldcmd} \DeclareOption{inkscape}% {\let\chemob@bel@svgtoimgcmd\chemob@bel@inkscapecmd} \DeclareOption{librsvg}% {\let\chemob@bel@svgtoimgcmd\chemob@bel@librsvgcmd} % crop image or not \let\chemob@bel@nocrop\relax \DeclareOption{nocrop}{\let\chemob@bel@nocrop\@empty} % default settings \ExecuteOptions{\chemob@belimgExt@default,inkscape} % handle user settings - arbitrary order allowed \ProcessOptions*\relax \ifx\chemob@bel@nocrop\relax \else \let\chemob@bel@cropcmd\@gobble \let\chemob@bel@maybecrop\@empty \fi %% ----- Declaration of options end ----- %% ----- Warn if something went wrong ----- % e.g.) \chemob@bel@common@maybeimg is not an image % --- when current processing failed % AND previous shell-escape run not found % e.g.) \chemob@bel@common@maybeimg is definitely a wrong image % --- when current processing failed % BUT previous shell-escape run with different total number found % --- note that re-run after removing [extract] should avoid the warning % --- as it will include all the correct images from previous shell-escape run \def\chemob@bel@record@imgnum#1{% \immediate\write#1{\gdef\string \smilesob@bel@imgNum{\number\value{smilesob@belCounter}}}% \immediate\write#1{\gdef\string \chemob@bel@imgNum{\number\value{chemob@belCounter}}}% } \def\chemob@bel@preserve@imgnum#1{% \immediate\write#1{\gdef\string \smilesob@bel@imgNum{\smilesob@bel@imgNum}}% \immediate\write#1{\gdef\string \chemob@bel@imgNum{\chemob@bel@imgNum}}% } \def\chemob@bel@atenddocument{% \ifnum\chemob@bel@status>\z@ % at least one is not an image \chemob@bel@warn{% Some processing failed.\MessageBreak Please rerun}% \else % all images \ifnum\chemob@bel@shellescape=\@ne % current run is shell-escape % record number of images of the current run \if@filesw \chemob@bel@record@imgnum{\@mainaux}% \fi % when processing ChemFigFile.tex, also write to the original .aux \ifx\chemob@bel@jobname\chemob@bel@outname \IfFileExists{\chemobabelfile.tex}{% \if@filesw \newwrite\chemob@bel@origaux \immediate\openout\chemob@bel@origaux=\chemobabelfile.aux\relax \chemob@bel@record@imgnum{\chemob@bel@origaux}% \immediate\closeout\chemob@bel@origaux \fi }{}% \fi \else % compare number of images with the previous shell-escape run \ifx\smilesob@bel@imgNum\@undefined\def\smilesob@bel@imgNum{0}\fi \ifnum\value{smilesob@belCounter}=\smilesob@bel@imgNum\relax\else \chemob@bel@warn{% Number of \noexpand\smilesobabel changed.\MessageBreak Please rerun}% \fi \ifx\chemob@bel@imgNum\@undefined\def\chemob@bel@imgNum{0}\fi \ifnum\value{chemob@belCounter}=\chemob@bel@imgNum\relax\else \chemob@bel@warn{% Number of \noexpand\chemobabel changed.\MessageBreak Please rerun}% \fi % record number of images of the previous shell-escape run \if@filesw \chemob@bel@preserve@imgnum{\@mainaux}% \fi \fi \fi } \AtEndDocument{\chemob@bel@atenddocument} %% ----- Warn if something went wrong end ----- %% ----- Exit this package now, if [extract] is not specified ----- \ifx\chemob@bel@extract\relax \endinput \fi %% Extracting all codes of Open Babel figures %% Thanks: http://oku.edu.mie-u.ac.jp/tex/mod/forum/discuss.php?d=1411 %% ----- Safety check before opening output file ----- \ifx\chemob@bel@jobname\chemob@bel@outname \chemob@bel@warn{% Wow! Your file name is `\chemob@bel@outname'!\MessageBreak Option [extract] ignored to avoid overwriting} \expandafter\endinput \fi \chemob@bel@warn{% You are using [extract] option.\MessageBreak No chemical structures will be printed} %% ----- Accumulate user-given options to be passed ----- % the image extension (non-empty) \edef\chemob@bel@options{\chemob@belimgExt} % image conversion command (except default=inkscape) \ifx\chemob@bel@svgtoimgcmd\chemob@bel@inkscapeoldcmd \edef\chemob@bel@options{\chemob@bel@options,inkscape-old} \else\ifx\chemob@bel@svgtoimgcmd\chemob@bel@librsvgcmd \edef\chemob@bel@options{\chemob@bel@options,librsvg} \fi\fi % crop image or not \ifx\chemob@bel@nocrop\relax \else \edef\chemob@bel@options{\chemob@bel@options,nocrop} \fi %% ----- Define intermediate output file and load packages ----- \newwrite\chemob@bel@outfile \immediate\openout\chemob@bel@outfile=\chemob@bel@outname.tex\relax \immediate\write\chemob@bel@outfile{\string\def\string\chemobabelfile{\chemob@bel@jobname}} \immediate\write\chemob@bel@outfile{\string\documentclass{article}} \immediate\write\chemob@bel@outfile{\string\usepackage[\chemob@bel@options]{chemobabel}} %% ----- Read and write ----- \immediate\write\chemob@bel@outfile{\string\begin{document}} \def\chemob@bel@atenddocument{% \immediate\write\chemob@bel@outfile{\string\end{document}}% \immediate\closeout\chemob@bel@outfile \chemob@bel@ifnochem{}{% \chemob@bel@warn{% Some chemical structures are extracted.\MessageBreak Run 'pdflatex -shell-escape ChemFigFile.tex'}}% } % \smilesobabel*[#1]{#2} => normalized to \smilesobabel[#1]{#2}{#3} \renewcommand\smilesobabel@i{% \begingroup \let\do\@makeother \dospecials \catcode`\{=1 \catcode`\}=2 \@smilesobabel@i } \renewcommand\smilesobabel@ii{% \begingroup \let\do\@makeother \dospecials \catcode`\{=1 \catcode`\}=2 \@smilesobabel@ii } \renewcommand\@smilesobabel@i[2][scale=1]{% \endgroup \@smilesobabel@main{#1}{#2}{}% } \renewcommand\@smilesobabel@ii[3][scale=1]{% \endgroup \@smilesobabel@main{#1}{#2}{#3}% } \def\@smilesobabel@main#1#2#3{% \immediate\write\chemob@bel@outfile{% \string\smilesobabel[#1]{#2}{#3}% \string\newpage}% [\smilesob@belGetName.\chemob@belimgExt]% \addtocounter{smilesob@belCounter}{1}} % \chemobabel*[#1]{#2} => normalized to \chemobabel[#1]{#2}{#3} \renewcommand\chemobabel@i{% \begingroup \let\do\@makeother \dospecials \catcode`\{=1 \catcode`\}=2 \@chemobabel@i } \renewcommand\chemobabel@ii{% \begingroup \let\do\@makeother \dospecials \catcode`\{=1 \catcode`\}=2 \@chemobabel@ii } \renewcommand\@chemobabel@i[2][scale=1]{% \endgroup \@chemobabel@main{#1}{#2}{}% } \renewcommand\@chemobabel@ii[3][scale=1]{% \endgroup \@chemobabel@main{#1}{#2}{#3}% } \def\@chemobabel@main#1#2#3{% \immediate\write\chemob@bel@outfile{% \string\chemobabel[#1]{#2}{#3}% \string\newpage}% [\chemob@belGetName.\chemob@belimgExt]% \addtocounter{chemob@belCounter}{1}} %% ----- Read and write end ----- \endinput %% %% End of file `chemobabel.sty'.