diff options
Diffstat (limited to 'Master/texmf-dist')
-rw-r--r-- | Master/texmf-dist/doc/latex/csvmerge/README | 19 | ||||
-rw-r--r-- | Master/texmf-dist/doc/latex/csvmerge/csvmerge.pdf | bin | 0 -> 218699 bytes | |||
-rw-r--r-- | Master/texmf-dist/source/latex/csvmerge/csvmerge.dtx | 1198 | ||||
-rw-r--r-- | Master/texmf-dist/source/latex/csvmerge/csvmerge.ins | 39 | ||||
-rw-r--r-- | Master/texmf-dist/tex/latex/csvmerge/csvmerge.sty | 300 |
5 files changed, 1556 insertions, 0 deletions
diff --git a/Master/texmf-dist/doc/latex/csvmerge/README b/Master/texmf-dist/doc/latex/csvmerge/README new file mode 100644 index 00000000000..a269609b49e --- /dev/null +++ b/Master/texmf-dist/doc/latex/csvmerge/README @@ -0,0 +1,19 @@ +---------------------------------------------------------------- + The csvmerge package + ==================== + + This package is designed to take a bit of TeX code such as + a letter or a line on a table and produce multiple copies + of it with parts of it filled in from a csv spreadsheet file. + It is useful for mail merges and making tables. It requires + minimal configuration of the csv file and is designed for easy + merging with the TeX code. + + Run `csvmerge.ins' through LaTeX to produce csvmerge.sty. + Run `csvmerge.dtx' through LaTeX to produce the documentation. + +E-mail: taylor.2@nd.edu +Released under the LaTeX Project Public License v1.3c or later +See http://www.latex-project.org/lppl.txt +---------------------------------------------------------------- + diff --git a/Master/texmf-dist/doc/latex/csvmerge/csvmerge.pdf b/Master/texmf-dist/doc/latex/csvmerge/csvmerge.pdf Binary files differnew file mode 100644 index 00000000000..c82cf61be91 --- /dev/null +++ b/Master/texmf-dist/doc/latex/csvmerge/csvmerge.pdf diff --git a/Master/texmf-dist/source/latex/csvmerge/csvmerge.dtx b/Master/texmf-dist/source/latex/csvmerge/csvmerge.dtx new file mode 100644 index 00000000000..995565378ef --- /dev/null +++ b/Master/texmf-dist/source/latex/csvmerge/csvmerge.dtx @@ -0,0 +1,1198 @@ +% \iffalse meta-comment +% +% Copyright (C) 2019 by Laurence R Taylor +% ----------------------------------- +% +% This file may be distributed and/or modified under the +% conditions of the LaTeX Project Public License, either version 1.3 +% of this license or (at your option) any later version. +% The latest version of this license is in: +% +% http://www.latex-project.org/lppl.txt +% +% and version 1.3 or later is part of all distributions of LaTeX +% version 2005/12/01 or later. +% +% \fi +% +% \iffalse +% +%<*driver> +\documentclass{ltxdoc} +\usepackage{csvmerge} +\usepackage{hyperref} +\hypersetup{ + colorlinks=true, + linkcolor=blue, + filecolor=magenta, + urlcolor=cyan, +} +\DisableCrossrefs +\CodelineNumbered +\RecordChanges +\CheckSum{525} +\begin{document} +\DocInput{csvmerge.dtx} +\end{document} +%</driver> +% \fi +% +% +% \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 \~} +% +% +% +% \changes{v1.0}{?2019?/?07?/?17?}{Initial version} +% \title{csvmerge} +% \author{Laurence R Taylor} +% \date{August 8, 2019} +% \maketitle +% +% \begin{abstract} +% The package |csvmerge.sty| is designed to take a bit of \TeX\ code such as a letter or a line +% in a table and produce +% multiple copies of it with parts of each copy filled in from a data file. +% It is invoked in the usual manner from a master-file, that is a standard +% \LaTeX\ file with +% +% \null\noindent\hskip 1in|\documentclass{whatever}|\\ +% \null\hskip 1in|\usepackage{csvmerge}|\\ +% \null\hskip 1.5in$\vdots$ +% +% The data source is a standard csv file. +% The bit of \TeX\ code resides in a blueprint file. +% The |\usepackage{csvmerge}| can go anywhere in the preamble. +% \end{abstract} +% \tableofcontents +% \def\Hyperlink#1{\hyperlink{#1Description}{\tt \mm #1}} +% \def\Hypertarget#1#2{\hypertarget{#1Description}{#2}} +% \xdef\mm{$\backslash\hskip-5pt\backslash$} +% \section{Introduction} +% +% The |csvmerge.sty| merges \TeX\ code with a data file. +% \begin{itemize} +% \item +% The \TeX\ code comes from a blueprint file, which looks like +% a standard \TeX\ file except it contains no code which should +% go into the preamble of a normal \TeX\ file. +% The blueprint file should also not contain a |\begin{document}| +% nor an |\end{document}|. This stuff goes in the master-file. +% \item The data file is a standard csv file. +% \end{itemize} +% One difference between |csvmerge.sty| and other packages which +% merge |csv| data into a \TeX\ file is that the user does not need to do +% much with the |csv|-file. As long as the first row contains reasonable +% entries describing the contents of its column you are good to go. +% Extra columns are no problem; subsequent rows can be too short +% or too long; characters which are special to \TeX\ such as |$| and |&| +% can appear in the data; \dots\ +% \noindent More details concerning the |csv|-file can be found in subsection 2.1. +% +% The |tex|-file consists of standard \TeX\ code. Each row of the |csv|-file is processed +% and you have access to the values of all the entries on that row as well as the row +% number. You also can tell if the entry in a particular column is empty. +% \noindent More details concerning the |tex|-file can be found in subsection 2.2. +% +% The main macro, |\mergeFields|, supports two modes. +% In one mode, \emph{stream mode}, the \TeX\ code from the |tex|-file +% is added to the input stream at the location from which |\mergeFields| is invoked. +% In the second mode, \emph{storage mode}, the code is written to a +% temporary file. You can then use the standard \LaTeX\ |\input| to read the +% temporary file back into the master-file. +% +% Good models for the two modes are the following. Most mail-merges can be +% done in streaming mode. Because \TeX\ is doing rather complicated things behind +% the scenes when it is setting a table or a matrix, you are usually better off using +% storage mode with your blueprint file containing the code for one line of your table/matrix. +% After the data is generated you can read it back into your table. +% See \hyperlink{subsubsection.B.3.1}{here} for a typical example. +% +% \newpage +% \section{Usage} +% +% \DescribeMacro{\mergeFields} +% The main macro is |\mergeFields| which takes one argument. +% The format is +%\begin{verbatim} +% \mergeFields{ +% tex={<tex-file>} +% csv = {<csv-file>} +% tmp = {<tmp-file>} +% } +%\end{verbatim} +% The |tmp| entry is not required. If there is none, |mergeFields| operates in +% \emph{streaming} mode, otherwise it acts in \emph{storage} mode. +% The |<prefix-file>| entry should be a path to the corresponding sort of file. +% If the file name has spaces you will need to quote it: life is easier if there are no +% spaces in file names. +% The format |prefix = {<prefix-file>}| is quite forgiving: all leading and trailing spaces +% are stripped out so |prefix = { <prefix-file> }| works. +% The three lines can be in any order and do not even need to be on separate lines. +% The extensions of the three files are irrelevant: the |tex|-file contains \TeX\ code +% but it can have an extension of |.txt| (or anything else or none at all) if you prefer. +% All that matters is that +% when \TeX\ reads the file the contents have the proper content. +% If you have a simple project you may be able to complete it just by looking at +% the examples in \hyperlink{appendix.B}{appendix B}. +% +% \subsection{The data file} +% The data file is a |csv| spreadsheet file. +% By default the field delimiter is the comma and the +% text delimiter is the double-quotes character. +% This is default behavior for most spreadsheets when +% asked to produce a |cvs| file. +% If there are no {\tt "}'s in your |csv|-file there should be no problems. +% In some cases you may need a different set of delimiters +% and this is supported. +% See \Hyperlink{setDelimitersTabQuote}\ below for more details. +% +% The entries in the first row should consist solely of \emph{alphanumeric characters, +% punctuation and spaces}. +% Each entry is a \emph{label} for that column. +% Labels need not be unique although they usually are except sometimes there are +% several columns with blank labels. +% +% A \emph{field-name} is extracted from a label by deleting all spaces and making all +% characters lowercase: e.g. a column labeled |First Name| has a field-name +% |firstname|. Commas are also deleted so a column label of |Last, First Name| +% becomes a field-name of |lastfirstname|. +% Distinct labels can have the same field-name although again +% this seems unlikely except for blank labels. +% +% Associated to each field-name is a \emph{field-name-macro}. +% This macro is private: its name is found by prepending |@LRT@@| to the field-name. +% When |\mergeFields| is invoked, the data file is read one row at a time. +% All rows after the first are data. +% When a row is read, each entry is peeled off and placed in the field-name-macro +% whose field-name is the one associated to the label for that column +% with |@LRT@| prepended. +% For example, if row one of some column contains |First Name|, the field-name is |firstname| and +% the field-name-macro is |\@LRT@@firstname|. When a row after the first is read, +% |\@LRT@@firstname| will expand to the entry in the |First Name| column on that row. +% +% When |\mergeFields| has read a row and set the field-name-macros the |tex|-file is +% read and the \TeX\ code evaluated. +% In streaming mode the result is added to the input stream at +% the current location whereas in storage mode the result is written to the |tmp|-file. +% +% \DescribeMacro{End of csv-file}\Hypertarget{End of csv-file} +% This process continues until the last line in the |csv|-file is processed OR until an entirely +% blank line is encountered. +% \vskip10pt +% The request for only alphanumeric, punctuation and blanks for entries in row one is +% not actually enforced but the macro |caselower| from |stringstrings| is used to +% produce lowercase letters and may do weird things to characters not on the requested list. +% Without studying the |stringstrings| manual carefully % +% you may not be able to identify the field-names if you insist on weird characters. +% +% It is quite possible that several columns can have the same field-name, and hence +% the same field-name-macro. In that case the macro will expand to the contents of the +% column with that field-name which is furthest to the right. +% +% Because of the prepended |@LRT@@| you can not use these field-name-macros directly +% in your |tex|-file. +% The solution is to use \Hyperlink{Field}{}|{field-name}| which will expand correctly. +% See the examples in appendix B. +% \subsubsection{Entry issues} +% Except for issues discussed here, you should be able to use any +% standard |csv|-file. +% +% The data file is read with the usual \TeX\ issues turned off: +% |#|, |$|, |&|, |@|, |%| +% , |^|, |~|, and \verb!|! +% can all be in your data file. +% \begin{itemize} +% \item +% In streaming mode, only |#|, |$|, |&|, |@|, |%|, |^|, |~| print correctly. +% The characters \$, \#, \%, @ and \& often occur in data and they print correctly. +% \TeX\ code will not be rendered correctly so for example, |\$| in the data file produces +% {$``$\tt\$} when \TeX ed. +% As long as you are not too creative in your entries you should get the results you want. +% \item +% In storage mode the characters above end up printing correctly in the |tmp|-file +% BUT when you |\input| the |tmp|-file back into your master-file these characters +% will resume their usual \TeX\ meaning and wreak havoc with your code. +% If you absolutely must have them, writing |\&|, |\$|, |\#|, +% etc. in the |csv|-file may save you. +% \end{itemize} +% +% \DescribeMacro{Delimiters}\null\hskip-\parindent +% There is one more issue. For most csv files, the field separator is the comma. +% When a field has a comma in it, the entire field is delimited by double quotes ({\tt"}). +% When the field has a double quote it is converted to a pair of double quotes ({\tt""}) and so on. +% If you have both commas and quotes in a field the best case scenario is that +% you will get |""|'s where you were hoping for |"|'s. This probably is not what you want. +% Even worse, a |",| in a data entry will end the entry prematurely at this point +% and things deteriorate from there. +% +% There is a mechanism to deal with this. Most spreadsheets can save csv files with other +% field (and text) delimiters. The second most common version of csv has a tab as the +% field delimiter and quotes for fields which have a tab in a field. +% +% These two cases are supported out of the box. By default you get the comma-quotes version. +% If you add |\setDelimitersTabQuote| before calling |\mergeFields| you will get +% the tab-quotes version. +% If you have a second call to |\mergeFields| and the data file is comma-quotes, +% adding |\setDelimitersCommaQuote| before |\mergeFields| will restore comma-quotes. +% See \hyperlink{setDelimitersCommaQuoteDescription}{setDelimters\dots} +% for a more thorough discussion. +% +% +% \subsection{The blueprint file} +% +% The blueprint file is a normal \TeX\ file with macros defined as above from the |csv|-file. +% It should not contain a \verb!\begin{document}! or an \verb!\end{document}! +% nor anything that needs to be expanded in the preamble. +% Such things belong in the master file. +% +% For mail-merges, the |tex|-file should end with a \verb!\newpage! +% to guarantee that each copy of the message appears on a +% different page. +% If you need custom sizes or headers or you have page numbers that you want to +% reset for each copy, you should do this in the |tex|-file. +% +% To use the data from the |csv|-file you need to write |\Field{some-field-name}| wherever +% you want the data in your |tex|-file. +% Some field-names may be rather long so if you are going to use them a lot in your +% |tex|-file, feel free to write something like |\def\sfn{\Field{some-field-name}}| at the +% top of your |tex|-file and then use |\sfn| thereafter. +% +% One additional issue occurs in storage mode. You may very well want \TeX\ code in your +% |tex|-file. When |mergeFields| reads a line of data, you want the field-name-macros to +% expand to their current values but you often do not want your \TeX\ code to expand. +% It is good practice stick a |\noexpand| in front of each \TeX\ macro. +% Each line is expanded once when the line is written to the |tmp| file, so the |\noexpand| +% disappears and you are left with just your \TeX\ macros. +% +% If you do define macros like |\sfn| as above, you will need +% to write\\ |\noexpand\def\noexpand\sfn{\Field{some-field-name}}|. +% \StopEventually{\PrintIndex} +% +% \section{List of public macros} +% There are the 6 public macros: links are to descriptions of the |@LRT@|-versions which +% actually do the work. +% +% \null\hskip-10pt \begin{tabular}{lll} +% \Hyperlink{mergeFields}% +% &\Hyperlink{Field}% +% &\hbox to 0pt{\Hyperlink{ifFieldEmpty}\hss}% +% \\ +% \Hyperlink{setDelimitersCommaQuote}&\Hyperlink{setDelimitersTabQuote}% +% &\Hyperlink{makeMePublic}\\ +% \end{tabular} +% \begin{itemize} +% \item |\mergeFields{tex={tex-file}csv={csv-file}}| or +% \\|\mergeFields{tex={tex-file}csv={csv-file}{tmp={tmp-file}}|\\ +% sets up to do the merge. +% \hyperlink{section.2}{Section 2} is devoted to a detailed discussion. +% \item |\Field{field-name}| expands to the current value of the field-name-macro. +% \item |\ifFieldEmpty{field-name to test}{do if empty}{do if not}| +% \item The two |\setDelimiters| set the delimiters to match those used in the |csv| file: +% comma-quotes is the default. +% \item |\makeMePublic| must be immediately followed by |\@LRT@| or you will get a +% |Use of \makeMePublic doesn't match its definition.| error. Then you can write +% |\makeMePublic \@LRT@foo\becomes\Foo| to make the public macro |\Foo| that +% you just define equal to the private macro |\@LRT@foo|. You may not write +% |\makeMePublic{\@LRT@foo}\becomes\Foo| or other variants: the first non-white-space +% character \TeX\ sees after |\makeMePublic| needs to be the start of |\@LRT@|. +% +% As a useful example, +% |\makeMePublic\@LRT@Row\becomes\Row| means you can get your hands on +% the row number using |\Row|. +% \end{itemize} +% +% \section{Implementation} +% +% \begin{macro}{Header} +% First we have the standard package header stuff. +% The line numbers in the typeset \jobname.dtx file match the line numbers +% in the \jobname.sty file +% generated by \TeX ing the \jobname.ins file. +% \setcounter{CodelineNo}{15} +% \begin{macrocode} +\NeedsTeXFormat{LaTeX2e}[2005/12/01] +\ProvidesPackage{csvmerge} + [2019/07/17 v1.0 merges TeX code with csv data] + +% \end{macrocode} +% \end{macro} +% \begin{macro}{Required Packages} +% These required packages are part of the standard \href{https://www.tug.org/texlive/}{{\tt TeX Live}} +%and \href{https://miktex.org}{{\tt MiKTEX}} distributions. +% \begin{macrocode} +\RequirePackage{stringstrings} +\RequirePackage{trimspaces} +\RequirePackage{etoolbox} + +% \end{macrocode} +% \end{macro} +% \begin{macro}{Options} +% |csvmerge| supports an option which is rarely needed. All the private macros +% (except \hyperlink{exceptionDescription}{one}) in +% this package have |@LRT@| or |@lrt@| somewhere in their name so are very +% unlikely to produce name conflicts. In the very unlikely chance that one or more of +% the six public macros has a name conflict, +% this can be alleviated by putting |\usepackage[foo]{csvmerge}| +% in your master file. Here |foo| can be any string of alphabetical characters. +% Then the names of all six public macros are invoked with a leading |foo|: +% |\mergeFields| no longer exists but becomes |\foomergeFields|. The evident change happens +% with the other five macros. +% +% The next two lines of code merely save the option for use +% \hyperlink{optionsDescription}{later} at the end. +% \begin{macrocode} +\DeclareOption*{\xdef\@LRT@CO{\CurrentOption}} +\ProcessOptions\relax + +% \end{macrocode} +% \end{macro} +% \begin{macrocode} +\makeatletter + +% \end{macrocode} +% \begin{macro}{\mergeFields} +% \Hypertarget{mergeFields} +% |\@LRT@mergeFields| actually initiates the work. +% It initializes some macros for later use. +% Then it collects the |prefix={<prefix-file>}| arguments using |\@LRT@getFilePaths| +% and checks to be sure it has a |csv|-file +% and a |tex|-file. If not it bails. +% Then it checks if it has a |tmp| file and sets the file reading subroutine, +% |\@LRT@Input| to |\@LRT@streaming| is there is no |tmp| file and to +% |\@LRT@storage| if there is. If there is a |tmp| file, it is opened. +% Then |\@LRT@mergeData| is called which actually does the merging. +% Finally, if there is a |tmp| file, it is closed. +% \begin{macrocode} +\newcommand{\@LRT@mergeFields}[1]{% +\gdef\@LRT@csv{}% +\gdef\@LRT@tex{}% +\gdef\@LRT@tmp{}% +\setcounter{\@LRT@macroPrefix Row}{2}\relax% +\gdef\@LRT@csvBlankLine{}% +\@LRT@getFilePaths#1=@% +\if@LRT@empty{\@LRT@csv}{\@LRT@missingArgumentMessage{csv}}{}% +\if@LRT@empty{\@LRT@tex}{\@LRT@missingArgumentMessage{tex}}{}% +\if@LRT@empty{\@LRT@tmp}% +{\global\let\@LRT@Input\@LRT@streaming}% +{\global\let\@LRT@Input\@LRT@storage}% +\if@LRT@empty{\@LRT@tmp}{}{% +\immediate\openout\@lrt@tmpFile=\@LRT@tmp\relax}% +\@LRT@mergeData% +\if@LRT@empty{\@LRT@tmp}{}{\immediate\closeout\@lrt@tmpFile\relax}% +} + +% \end{macrocode} +% \end{macro} +% \Hypertarget{macroPrefix}{} +% \begin{macro}{\@LRT@macroPrefix} +% \begin{macrocode} +\gdef\@LRT@macroPrefix{@LRT@@} + +% \end{macrocode} +% \end{macro} +% \Hypertarget{Field}{} +% \begin{macro}{\Field} +% |\@LRT@Field| is just a wrapper for a |\csname ... \endcsname|. +% Notice it adds the |\@LRT@macroPrefix| prefix to the argument so +% the input is always the field-name and the output is always the value of +% the corresponding field-name-macro. +% Because of the third |@| there is no danger that these macros defined from +% entries in the |csv|-file will conflict with other macros defined in this package +% since none of them have a |@@| in their names. +% \begin{macrocode} +\def\@LRT@Field#1{% +\csname\@LRT@macroPrefix#1\endcsname% +} + +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ifFieldEmpty} +% \Hypertarget{ifFieldEmpty}{} +% |\ifFieldEmpty| takes three arguments. +% The first argument is a field-name and if the content of the corresponding +% macro is empty then the second argument is executed: otherwise the +% third argument is executed. +% +% \begin{macrocode} +\long\gdef\@LRT@ifFieldEmpty#1#2#3{\ifbool{\@LRT@macroPrefix#1}{#2}{#3}} + +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\setDelimitersCommaQuote} +%\end{macro} +% \begin{macro}{\setDelimitersTabQuote} +% \Hypertarget{setDelimitersCommaQuote}{} +% \Hypertarget{setDelimitersTabQuote}{} +% These two |setDelimiters| are similar. +% Recall how delimiters work. A row in a |csv|-file with comma delimiters +% looks like |foo1,foo2,foo3,...| so the delimiter is the comma and if +% |\@LRT@getFieldDelimited| is put in front of this string it will peal off +% |foo1|. Since |^^I| is \TeX\ for the tab, +% a row in a csv file with tab delimiters +% looks like |foo1^^Ifoo2^^Ifoo3^^I...| and so the first two lines of +% |\@LRT@setDelimitersCommaQuote| and |\@LRT@setDelimitersTabQuote| +% look the same after switching |,| and |^^I|. +% +% There is a wrinkle in the tab case. The tab normally looks to \TeX\ like white space and +% so by the time the line reaches |\@LRT@getFieldDelimited| the line looks like +% |foo1foo2foo3...| and there is no way to break off |foo1|. The |\catcode`\^^I12\relax| +% insures |\@LRT@getFieldDelimited| sees the line as |foo1^^Ifoo2^^Ifoo3^^I...|.. +% This means that the catcode of |^^I| must also be 12 when each line is read and the line +% and the macro |\gdef\@LRT@catcodes{|\\|\catcode`\^^I12\relax}| insures this. +% In the comma delimited version no catcode heroics are needed so |\@LRT@catcodes| +% is empty. +% +% If some field contains one or more |\@LRT@fieldDelimiterBack|'s then the |csv|-file will use +% the text-delimiter at the front and back. When quote is the text delimiter and |foo2| contains +% a comma but |foo1| and |foo3| do not, the comma delimited string looks like +% |foo1,"foo2",foo3,...| and the tab delimited string looks like +% |foo1^^I"foo2"^^Ifoo3^^I...|. +% +% The text delimiters have a front one, the double-quotes in both versions, and the back one which is +% the front text delimiter followed by the back field delimiter. +% +% The macro |\@LRT@blankFieldItem| is used to construct what a blank line looks like. +% In the comma delimited case, you need a string of commas of the same length as +% the number of entries on line one of the |csv|-file. In the tab delimited case, the blank line +% turns out to be a blank macro. +% +% The |\@LRT@catcodes| macro is a collection of catcodes that need to be changed: +% nothing for the comma delimited case and |^^I| for the tab delimited case. +% +% See \hyperlink{newDelimiterAppendix}{Appendix A} for more discussion on writing your own delimiters. +% \begin{macrocode} +{{% +\gdef\@LRT@setDelimitersCommaQuote{ +\gdef\@LRT@fieldDelimiterBack{,} +\gdef\@LRT@getFieldDelimited##1,{\@LRT@getFirstArg{##1}} +\gdef\@LRT@textDelimiterFront{"} +\gdef\@LRT@getTextDelimited"##1",{\@LRT@getFirstArg{##1}} +\gdef\@LRT@blankFieldItem{,} +\gdef\@LRT@catcodes{} +}% +}} + +{{\catcode`\^^I12\relax% +\gdef\@LRT@setDelimitersTabQuote{ +\gdef\@LRT@fieldDelimiterBack{^^I} +\gdef\@LRT@getFieldDelimited##1^^I{\@LRT@getFirstArg{##1}} +\gdef\@LRT@textDelimiterFront{"} +\gdef\@LRT@getTextDelimited"##1"^^I{\@LRT@getFirstArg{##1}} +\gdef\@LRT@blankFieldItem{} +\gdef\@LRT@catcodes{\catcode`\^^I12\relax} +}% +}} + +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@LRT@makeMePublic} +% \hypertarget{exceptionDescription}{} +% \hypertarget{makeMePublicDescription}{} +% The next line of code defines the only macro which does not have |@LRT@| +% in its name. It seems sufficiently weird that there should be no name conflicts. +% Notice that |\makeatletter| is still in effect so |@| has catcode 11. +% +% Then we switch to |\makeatother| so |@| has catcode 12. +% The next line expands to |\@LRT@makeMePublic| with the initial +% |\@LRT@| having its |@|'s of catcode 11 so it is a private macro. +% In the |\@LRT@| that you see, the |@|'s have catcode 12 so it is really +% the macro |\@| with extra stuff. Since it should never be expanded +% it doesn't really matter. Then we drop back into |\makeatletter| to define +% more private macros. +% +% The net result is that you can write \\ +% \null\hskip1in |\makeMePublic \@LRT@foo\becomes\newMacro| \\in your master-file and +% the private macro |\@LRT@foo| becomes the same as |\newMacro| and you can +% use |\newMacro| in your |tex|-file. +% \begin{macrocode} +\expandafter\gdef\csname1238LRTLRTsvbneLRT\endcsname{@LRT@} +\makeatother +\expandafter\gdef\csname\csname1238LRTLRTsvbneLRT\endcsname +makeMePublic\endcsname\@LRT@#1\becomes#2{\global\letcs{#2}{@LRT@#1}}% +\makeatletter + +% \end{macrocode} +% \end{macro} +% \begin{macro}{private macros} +% \end{macro} +% \begin{macro}{\@LRT@Row} +% \Hypertarget{Row}{} +% The macro |\@LRT@Row| expands to the number of the row of the |csv|-file currently +% being read. To get the |\xdef| to expand correctly we needed to define (and did) +% |\@LRT@macroPrefix| first. Recall that the first line of actual data is row 2. +% \begin{macrocode} +\xdef\@LRT@Row{\noexpand\number\noexpand\value{\@LRT@macroPrefix Row}} + +% \end{macrocode} +% \end{macro} +% \begin{macro}{\if@LRT@empty}\Hypertarget{if@LRT@empty}{} +% Takes three arguments: if |#1| expands to empty do the code in |#2|; +% otherwise do the code in |#3|. +% \begin{macrocode} +\newcommand{\if@LRT@empty}[3]{\ifthenelse{\equal{#1}{}}{#2}{#3}} + +% \end{macrocode} +% \end{macro} +% \begin{macro}{\@LRT@fieldName} +% Takes an alphanumeric string, uses |\caselower| from the |stringstrings| package +% to convert the string to all lower case; then it strips the spaces from the answer +% using |\convertchar|, also from the |stringstrings| package. Next it strips commas from +% the answer. +% The final |\@LRT@TheString| makes the answer global. +% \begin{macrocode} +\gdef\@LRT@fieldName#1{% +\caselower[q]{#1}% +\convertchar[q]{\thestring}{ }{}% +\convertchar[q]{\thestring}{,}{}% +\xdef\@LRT@TheString{\thestring}% +} + +% \end{macrocode} +% \end{macro} +% \begin{macro}{\@LRT@getFilePaths} +% |\@LRT@getFilePaths#1=#2@| is a recursive macro which works through the +% |prefix={<prefix-file>}| argument list. +% The first argument, |#1|, picks up one |prefix={<prefix-file>}| and |#2| contains the rest of the arguments. +% If |#1| is not empty it is passed to |\@LRT@extractKeyValue| which collects the +% prefix and file path. +% \begin{macrocode} +\gdef\@LRT@getFilePaths#1=#2@{% +\xdef\@LRT@hashKey{\trim@spaces{#1}}% +\if@LRT@empty{\@LRT@hashKey}{}{ +\def\@LRT@test{tex}\ifthenelse{\equal{\@LRT@hashKey}{\@LRT@test}}{}% +{\def\@LRT@test{csv}\ifthenelse{\equal{\@LRT@hashKey}{\@LRT@test}}{}% +{\def\@LRT@test{tmp}\ifthenelse{\equal{\@LRT@hashKey}{\@LRT@test}}{}% +{\@LRT@unknownArgumentMessage{#1}}}}% +\@LRT@extractKeyValue#2@% +\expandafter\@LRT@getFilePaths\@LRT@doWeStillHaveStuffToDo @}} + +% \end{macrocode} +% \end{macro} +% \begin{macro}{\@LRT@extractKeyValue} +% |\@LRT@hashKey| contains the prefix and |\@LRT@hashValue| contains the file path. +% If the prefix is not one of |tex|, |csv| or |tmp| you will be warned. +% \begin{macrocode} +\def\@LRT@extractKeyValue#1#2@{% +\edef\@LRT@hashValue{\trim@spaces{#1}}% +\xdef\@LRT@doWeStillHaveStuffToDo{\trim@spaces{#2}}% +\expandafter\xdef\csname @LRT@\@LRT@hashKey\endcsname{\@LRT@hashValue}% +} + +% \end{macrocode} +% \end{macro} +% \begin{macro}{\@LRT@SecondifFieldEmpty} +% |\@LRT@SecondifFieldEmpty| checks for an empty macro directly. +% The previously defined \Hyperlink{ifFieldEmpty} uses a different method so both are needed at +% various points in the code. +% \begin{macrocode} +\newcommand{\@LRT@SecondifFieldEmpty}[3]{% +\edef\@LRT@fieldValue{#1}% +\ifx\@LRT@fieldValue\@empty\relax{#2}\else{#3}\fi\relax} + +% \end{macrocode} +% \end{macro} +% \begin{macro}{\@LRT@getCommaFieldDelimited} +% The macros |\@LRT@getCommaFieldDelimited|and +% |\@LRT@getFieldDelimited| are the same when the field delimiter is the comma. +% The list of field-names is always comma delimited and so +% we need a |\@LRT@getCommaFieldDelimited| to iterate through it. +% \begin{macrocode} +\gdef\@LRT@getCommaFieldDelimited#1,{\@LRT@getFirstArg{#1}} + +% \end{macrocode} +% \end{macro} +% \begin{macro}{New counter} The counter containing the row number. +% \begin{macrocode} +\newcounter{\@LRT@macroPrefix Row} + +% \end{macrocode} +% \end{macro} +% \begin{macro}{New read/write's} +% \begin{macrocode} +\newread\@lrt@csvFile% +\newread\@lrt@texFile% +\newwrite\@lrt@tmpFile% + +% \end{macrocode} +% \end{macro} +% \begin{macro}{Messages} +% \begin{macrocode} +\gdef\@LRT@justProcessedMessage{% +\immediate\typeout{}% +\immediate\typeout{Just processed row % +\@LRT@Row\space of the data file.}% +\immediate\typeout{}% +\stepcounter{\@LRT@macroPrefix Row}\relax% +} + +\gdef\@LRT@failedToOpenMessage#1{% +\immediate\typeout{}% +\immediate\typeout{File #1 failed to open.}% +\immediate\typeout{}\fi% +\end{document}% +} + +\gdef\@LRT@NoMoreDataMessage{% +\immediate\typeout{No more data.}% +\immediate\typeout{}% +} + +\gdef\@LRT@missingArgumentMessage#1{ +\immediate\typeout{}% +\immediate\typeout{I was never given a path to the #1 file.}% +\immediate\typeout{}% +\end{document}% +} + +\gdef\@LRT@unknownArgumentMessage#1{ +\immediate\typeout{}% +\immediate\typeout{I do not recognize an argument of type #1.}% +\immediate\typeout{}% +} + +% \end{macrocode} +% \end{macro} +% \begin{macro}{Inputs} +% When |mergeFields| starts up, it knows immediately whether it is in streaming or in storage +% mode. It sets |\@LRT@Input| to |\@LRT@streaming| or |\@LRT@storage|. +% Each time a new line of data in the |csv|-file is read the +% the macros are updated to reflect that data on the current line. +% |\@LRT@streaming| uses the \LaTeX\ command |\input| to add the |tex|-file to the +% stream. +% |\@LRT@storage| works through the data in the |tex|-file. +% First it opens the |tex| file; then it +% reads the |tex|-file one line at a time; expands that line; and +% writes the answer to a the |tmp|-file. When the |eof| for the |tex|-file is reached, +% the |tex|-file is closed. +% +% |\@LRT@catcodeMagic| is used in two places to set the catcodes of certain characters +% to values that won't cause trouble. +% +% |\@LRT@texNotAtEOF{#1}| is used to tighten up the code looping over the lines in +% the |tex|-file in |\@LRT@storage|. +% \begin{macrocode} +\gdef\@LRT@streaming{\input{\@LRT@tex}} + +\gdef\@LRT@catcodeMagic{% +\catcode`\^^M9\relax% +\catcode`\#12\relax +\catcode`\$12\relax +\catcode`\&12\relax +\catcode`\^12\relax +\catcode`\_12\relax +\catcode`\%12\relax +} + +\gdef\@LRT@texNotAtEOF#1{% +\ifeof\@lrt@texFile{\global\booltrue{@LRT@texEOF}}\else{#1}\fi% +} + +\gdef\@LRT@storage{{% +\@LRT@catcodeMagic% +\openin\@lrt@texFile=\@LRT@tex\relax% +\@LRT@texNotAtEOF{\global\boolfalse{@LRT@texEOF}}% +\unlessboolexpr{bool{@LRT@texEOF}}{% +\read\@lrt@texFile to \@LRT@preItem% +\@LRT@texNotAtEOF{% +\immediate\write\@lrt@tmpFile{\@LRT@preItem}\relax}% +}{}% +\immediate\closein\@lrt@texFile% +}} + +% \end{macrocode} +% \end{macro} +% \begin{macro}{\@LRT@dataLineIterator} +% This macro looks to see if the next character is |\@LRT@textDelimiterFront|. +% If it is the entry is collected using |\@LRT@getTextDelimited| and if not we collect it +% using |\@LRT@getFieldDelimited|. +% \begin{macrocode} +\gdef\@LRT@dataLineIterator{% +\expandafter\@ifnextchar\@LRT@textDelimiterFront% +{\@LRT@getTextDelimited}{\@LRT@getFieldDelimited}} + +% \end{macrocode} +% \end{macro} +% \begin{macro}{\@LRT@getFirstArg} +% This is the |\@LRT@getFirstArg| from the |\@LRT@getFieldDelimited| and \\ +% |\@LRT@getTextDelimited| macros. It collects the first field and then +% uses |\@LRT@getSecondArg| to collect the rest of the line from the |csv|-file. +% \begin{macrocode} +\gdef\@LRT@getFirstArg#1{\gdef\@LRT@firstArg{#1}\@LRT@getSecondArg} +\gdef\@LRT@getSecondArg#1@{\gdef\@LRT@restArg{#1}} + +% \end{macrocode} +% \end{macro} +% \begin{macro}{\@LRT@getLOFirstArg} +% These next two macros do the same thing with the first line of the |csv|-file. +% The difference is that these two macros put their answers in a different pair of +% macros than the previous two so we can iterate along both the first line (field-names) +% and the current line of the |csv|-file. +% \begin{macrocode} +\gdef\@LRT@getLOFirstArg#1{\gdef\@LRT@ffirstArg{#1}\@LRT@getLOSecondArg} +\gdef\@LRT@getLOSecondArg#1@{\gdef\@LRT@frestArg{#1}} + +% \end{macrocode} +% \end{macro} +% \begin{macro}{\@LRT@getLineOne} +% This is the version of |\@LRT@getFirstArg| which is used on the first line of the |csv|-file. +% \begin{macrocode} +\gdef\@LRT@fieldNameIterator#1,{\@LRT@getLOFirstArg{#1}} + +% \end{macrocode} +% \end{macro} +% \begin{macro}{\@LRT@setFirstLineAndBlankLine} +% This macro collects the first line of the |csv|-file and packages it for later use: +% |\@lrt@firstline| is the first line when we are done. +% We also construct |\@LRT@csvBlankLine| which is used to check if +% a line of the |csv|-file is blank. +% \begin{macrocode} +\providebool{@LRT@iterator} +\gdef\@LRT@setFirstLineAndBlankLine#1{% +\gdef\@lrt@firstline{}% +\gdef\@LRT@csvBlankLine{} +\edef\@LRT@restArg{#1\@LRT@fieldDelimiterBack} +\booltrue{@LRT@iterator}% +\whileboolexpr{bool{@LRT@iterator}}{% +\expandafter\@LRT@dataLineIterator\@LRT@restArg @% +\@LRT@fieldName{\@LRT@firstArg}% +\xdef\@lrt@firstline{\@lrt@firstline\@LRT@TheString,}% +\if@LRT@empty{\@LRT@restArg}% +{\boolfalse{@LRT@iterator}}{\booltrue{@LRT@iterator}}% +\ifboolexpr{bool{@LRT@iterator}}% +{\xdef\@LRT@csvBlankLine{\@LRT@csvBlankLine\@LRT@blankFieldItem}}{}% +}{}% +} + +% \end{macrocode} +% \end{macro} +% \begin{macro}{\@LRT@getAndProcessNextLine} +% This is the recursive macro which processes each line of the |csv|-file +% until there are no more. +% The code in the first |{{| \dots |}}| pair +% reads a line from the |csv|-file: the last line here converts +% the answer from local to global. +% The code in the second |{{| \dots |}}| pair checks +% if the line is \hyperlink{End of csv-fileDescription}{blank} or not. +% |\@LRT@exitGetAndProcessNextLine| exits the recursion and +% |\@LRT@repeatGetAndProcessNextLine| reloops. +% \begin{macrocode} +\gdef\@LRT@getAndProcessNextLine{% +\unlessboolexpr{bool{@LRT@csvEOF}}{% +{{% +\@LRT@catcodeMagic% +\@LRT@catcodes +\read\@lrt@csvFile to\@LRT@x@nextLine% +\ifeof\@lrt@csvFile\relax{\global\booltrue{@LRT@csvEOF}}\else% +{\global\boolfalse{@LRT@csvEOF}}\fi% +\xdef\@LRT@nextLine{\@LRT@x@nextLine}% +}}% +{{% +\if@LRT@empty{\@LRT@nextLine}% +{\global\let\@LRT@next\@LRT@exitGetAndProcessNextLine\relax}% +{\ifx\@LRT@csvBlankLine\@LRT@nextLine\relax% +{\global\let\@LRT@next\@LRT@exitGetAndProcessNextLine\relax}\else% +{\global\let\@LRT@next\@LRT@repeatGetAndProcessNextLine\relax}\fi}% +\@LRT@next% +}}% +}{}% +} + +% \end{macrocode} +% \end{macro} +% \begin{macro}{\@LRT@exitGetAndProcessNextLine} +% This just exits. +% \begin{macrocode} +\gdef\@LRT@exitGetAndProcessNextLine{% +\closein\@lrt@csvFile\relax\global\booltrue{@LRT@csvEOF}} + +% \end{macrocode} +% \end{macro} +% \begin{macro}{\@LRT@repeatGetAndProcessNextLine} +% This sets up to process the line we just read. +% \begin{macrocode} +\gdef\@LRT@repeatGetAndProcessNextLine{% +\@LRT@processNextLine{\@LRT@nextLine\@LRT@fieldDelimiterBack}} + +% \end{macrocode} +% \end{macro} +% \begin{macro}{\@LRT@setMacrosForOneEntry} +% This bit of code sets the macro and the if-empty boolean for a single item: +% \#1 is the field-name and \#2 is the entry in that (row,column). +% \begin{macrocode} +\gdef\@LRT@setMacrosForOneEntry#1#2{% +\expandafter\xdef\csname\@LRT@macroPrefix#1\endcsname{#2}% +\@LRT@SecondifFieldEmpty{#2}% +{\global\booltrue{\@LRT@macroPrefix#1}}% +{\global\boolfalse{\@LRT@macroPrefix#1}}% +} + +% \end{macrocode} +% \end{macro} +% \begin{macro}{\@LRT@processNextLine} +% This code actually processes the line from the |csv|-file. +% The first three lines are setup. +% The two |\edef|'s do the following: |\@LRT@restArg{#1}| has the entire current row; and +% |\@LRT@frestArg| has a comma separated list of field-names. +% Setting |\booltrue{@LRT@iterator}| means the iteration will start. +% The two |\expandafter| lines do the iteration: the current values are in |\ffirstArg| for +% the field-name and |\firstArg| for the entry in the current row. +% The line |\@LRT@setMacrosForOneEntry| sets the contents of the field-name-macro. +% Finally the +% |\if@LRT@empty| line checks if we are done. +% +% The |\edef| between the two |\expandafter|'s adds a blank entry to the back of +% |\@LRT@restArg| so if the row is too short in the |csv|-file it will still scan correctly. +% +% The |\@LRT@Input| outputs the result either to the stream or to the |tmp|-file +% and |\@LRT@justProcessedMessage| writes to the terminal which row was just processed. +% If something suddenly goes wrong you know which row you were working on +% when things messed up. +% \begin{macrocode} +\gdef\@LRT@processNextLine#1{% +\edef\@LRT@restArg{#1}% +\edef\@LRT@frestArg{\@lrt@firstline}% +\booltrue{@LRT@iterator}% +\whileboolexpr{bool{@LRT@iterator}}{% +\expandafter\@LRT@fieldNameIterator\@LRT@frestArg @% +\edef\@LRT@restArgA{\@LRT@restArg\@LRT@fieldDelimiterBack} +\expandafter\@LRT@dataLineIterator\@LRT@restArgA @% +\@LRT@setMacrosForOneEntry{\@LRT@ffirstArg}{\@LRT@firstArg}% +\if@LRT@empty{\@LRT@frestArg}% +{\boolfalse{@LRT@iterator}}{\booltrue{@LRT@iterator}}% +}{}% +\@LRT@Input\relax% +\@LRT@justProcessedMessage% +} + +% \end{macrocode} +% \end{macro} +% \begin{macro}{\@LRT@mergeData} +% This macro opens the |csv|-file, reads the first row. +% The code between the |{{ ... }}| makes a comma separated list of field-names +% in the order in which they occur in the |csv|-file. It also creates a macro which +% expands to what a blank line in the |csv|-file will look like. It also defines one +% boolean for each field-name. +% This requires an iteration over |\@lrtfirstline|. +% +% Because \TeX\ |\newif|'s are not global, we need to define the ones we need as soon +% as possible, which is right here. +% +% Then |\@LRT@mergeData| calls |\@LRT@getAndProcessNextLine| which +% reads and processes the subsequent lines of the |csv| file. +% +% Finally it puts out a no-more-data message to let you know |\mergeFields| is finished. +% \begin{macrocode} +\gdef\@LRT@mergeData{% +\providebool{@LRT@texEOF}% +\immediate\openin\@lrt@csvFile=\@LRT@csv\relax% +\providebool{@LRT@csvEOF}% +\boolfalse{@LRT@csvEOF}% +\ifeof\@lrt@csvFile\@LRT@failedToOpenMessage{\@LRT@csv}\fi% +{{\@LRT@catcodes +\read\@lrt@csvFile to\@lrt@firstlineX% +\@LRT@setFirstLineAndBlankLine{\@lrt@firstlineX} +}}% +\booltrue{@LRT@iterator} +\edef\@LRT@restArg{\@lrt@firstline}% +\whileboolexpr{bool{@LRT@iterator}}{% +\expandafter\@LRT@getCommaFieldDelimited\@LRT@restArg @% +\providebool{\@LRT@macroPrefix\@LRT@firstArg}% +\if@LRT@empty{\@LRT@restArg}% +{\boolfalse{@LRT@iterator}}{\booltrue{@LRT@iterator}}% +}{}% +\@LRT@getAndProcessNextLine% +\@LRT@NoMoreDataMessage% +} + +% \end{macrocode} +% \end{macro} +% \Hypertarget{options}{} +% \begin{macro}{Options code} +% Here is the code which +% examines the option string and maps the public macro names to the +% private codes which actually do the work. +% \begin{macrocode} +\gdef\@LRT@weirdOption#1{\@ifundefined{@LRT@CO}% +{\global\csletcs{#1}{@LRT@#1}}{{\global\csletcs{\@LRT@CO #1}{@LRT@#1}}}% +} + +\@LRT@weirdOption{mergeFields}% +\@LRT@weirdOption{Field}% +\@LRT@weirdOption{ifFieldEmpty}% +\@LRT@weirdOption{setDelimitersCommaQuote}% +\@LRT@weirdOption{setDelimitersTabQuote}% +\@LRT@weirdOption{makeMePublic}% + +% \end{macrocode} +% \end{macro} +% \begin{macro}{Set the default delimiters} +% Here \Hypertarget{defaults}{is} +% where the default delimiters are set. +% \begin{macrocode} +\@LRT@setDelimitersCommaQuote + +% \end{macrocode} +% \end{macro} +% \begin{macro}{Finish up} +% \begin{macrocode} +\makeatother +\endinput +% \end{macrocode} +% \end{macro} +% \Finale +% \newpage +% \appendix +% \section{Writing new delimiter macros}\hypertarget{newDelimiterAppendix}{} +% If the field delimiter is not something \TeX\ sees as white space, life is fairly easy. +% In your master-file, copy and paste one of the code examples below. +% Change to suit your needs. +% +% \noindent*********************************************************************** +% +% \begin{verbatim} +% {{% +% \makeatletter +% \gdef\setMyDelimiters{% +% \gdef\@LRT@fieldDelimiterBack{,} +% \gdef\@LRT@getFieldDelimited##1,{\@LRT@getFirstArg{##1}} +% \gdef\@LRT@textDelimiterFront{"} +% \gdef\@LRT@textDelimiterBack{",} +% \gdef\@LRT@getTextDelimited"##1",{\@LRT@getFirstArg{##1}} +% \gdef\@LRT@blankFieldItem{,} +% \gdef\@LRT@catcodes{} +% }% +% \makeatother +% }} +% \end{verbatim} +% +% \noindent*********************************************************************** +% +% Replace all occurrences of {\tt ,} by your field delimiter. Replace all occurrences of {\tt "} +% with your text deliminator +% and you should be ready to go. +% Just add |\setMyDelimiters| in your master-file before using |\mergeFields|. +% +% If the field delimiter is a white space character such as a tab, then copy and paste +% the next example +% +% \noindent*********************************************************************** +% +% \begin{verbatim} +% \makeatletter +% {{\makeatletter +% \catcode`\^^I12\relax% +% \gdef\@LRT@setDelimitersTabQuote{ +% \gdef\@LRT@fieldDelimiterBack{^^I} +% \gdef\@LRT@getFieldDelimited##1^^I{\@LRT@getFirstArg{##1}} +% \gdef\@LRT@textDelimiterFront{"} +% \gdef\@LRT@textDelimiterBack{"^^I} +% \gdef\@LRT@getTextDelimited"##1"^^I{\@LRT@getFirstArg{##1}} +% \gdef\@LRT@blankFieldItem{} +% \gdef\@LRT@catcodes{\catcode`\^^I12\relax} +% }% +% }} +% \makeatother +% }} +% \end{verbatim} +% +% \noindent*********************************************************************** +% +% Use Chapter 8 of the \TeX\ book to find how to write your delimiter and replace +% all occurrences of |\^^I| with the code for your delimiter. +% Then replace all occurrences of {\tt "} with your text deliminator +% and hope for the best. +% Try leaving |\@LRT@blankFieldItem| blank and hope for the best. +% Again add |\setMyDelimiters| in your master-file before using |\mergeFields|. +% +% \section{Some examples} +% \subsection{\TeX\ code in a table} +% A typical line in a tabular environment looks like |item1 & item2& item3\\|. +% Suppose you have 4 columns in your |csv|-file with entries |Last Name|, +% |First name|, |Numerator| and |Denominator| and you want |item1| to be +% |Last Name|, |item2| to be |First name| and |item3| to be |\frac{Numerator}{Denominator}|. +% Here is the needed code in your |tex|-file: +% +% \begin{verbatim} +% \Field{lastname}&\Field{firstname}&% +% $\noexpand\frac{\Field{numerator}}{\Field{denominator}}$\noexpand\\ +% \end{verbatim} +% You might have expected to see a |\noexpand| in front of the \$'s but remember \$ is one of +% those characters which goes through unscathed to the |tmp|-file. +% It certainly doesn't hurt to write |\noexpand$|. When you read the |tmp|-file back in +% you do want |$| to resume its meaning of making math mode and it does. +% +% The |\noexpand| in front of the |\frac| and the |\\| are needed. +% You don't want to try to expand either one +% until the |tmp|-file is read back in. +% +% Assuming that the file |merge.tex| contains the code above and that the file |data.csv| contains your data, +% the following code typesets the table. +% \begin{verbatim} +% \documentclass[12pt]{article} +% \RequirePackage{csvmerge} +% \begin{document} +% \mergeFields{ +% tmp={example.tmp} +% tex ={merge.tex} +% csv = {data.csv} +% } +% \begin{tabular}{lll} +% \input{example.tmp} +% \end{tabular} +% \end{document} +% \end{verbatim} +% The |csv|-file needs to contain the four field-names that appear in the |tex|-file, +% but it is fine if it contains additional columns. +% +% \subsection{\TeX\ code in a mail-merge} +% Here is a snippet of code to do a header in a mail-merge business letter. +% The field-names from the |csv|-file should be obvious. The interesting point +% is the usage of |\ifFieldEmpty|. +% \begin{verbatim} +% \Field{firstname} \ifFieldEmpty{middleinitial}{} +% {\Field{middleinitial}.~}\Field{lastname}\par +% \ifFieldEmpty{streetline1}{}{\Field{streetline1}\par} +% \ifFieldEmpty{streetline2}{}{\Field{streetline2}\par} +% \ifFieldEmpty{streetline3}{}{\Field{streetline3}\par} +% \Field{city}, \Field{stateprovince} \Field{postalcode} +% \end{verbatim} +% +% The |middleinitial| column is a middle initial with no period. Some recipients will have no middle initial +% but you don't want just a floating period. Also you don't want an additional space. +% Knuth also suggests that names should be \TeX ed with |First M.~Last| [The \TeX\ book, Ch. 6]. +% The |\ifFieldEmpty| with |streetline1| is probably redundant but often |streetline2| will be empty. +% If it is not, we want a new line after it, but if it is empty we don't want a blank new line. +% +% In storage mode you need a |\noexpand| before the |~|, as well as before the |\par|'s. +% \subsection{Some master-files} +% \subsubsection{A table} +% We are doing a table so we want storage mode. +% We also want a horizontal line after each row of the table. +% The master-file: +% \begin{verbatim} +% \documentclass[12pt]{amsart} +% \RequirePackage{csvmerge} +% \begin{document} +% \mergeFields{ +% tmp= { ./table.tmp} +% csv = {./data.csv} +% tex ={./table.tex } +% } +% \begin{tabular}{|l|l|l|l|l|} +% \hline% +% \input{./table.tmp} +% \end{tabular} +% \end{document} +% \end{verbatim} +% +% The |tex|-file |table.tmp|: +% \begin{verbatim} +% % one table line +% \Field{firstname} &\Field{id} & \Field{city} &\Field{lastname}& +% \Field{postalcode} \noexpand\\ +% \noexpand\hline +% \end{verbatim} +% +% \subsubsection{A mail-merge} +% We are doing a mail-merge so we can use streaming mode. +% Since the |tex|-file name has spaces, we have quoted it. +% The master-file: +% \begin{verbatim} +% \documentclass[12pt]{letter} +% \RequirePackage{./csvmerge} +% \begin{document} +% \mergeFields{ tex = {"./Second letter.template" } +% csv ={ ./Invites.csv}} +% \end{document} +% \end{verbatim} +% +% The |tex|-file |Second letter.template|: +% +% \begin{verbatim} +% %First page of letter +% \setcounter{page}{1} +% +% \Field{firstname} \ifFieldEmpty{middleinitial}{}% +% {\Field{middleinitial}.~}\Field{lastname}\par +% \ifFieldEmpty{streetline1}{}{\Field{streetline1} \par} +% \ifFieldEmpty{streetline2}{}{\Field{streetline2}\par} +% \ifFieldEmpty{streetline3}{}{\Field{streetline3}\par} +% \Field{city}, \Field{stateprovince} \Field{postalcode} +% +% \vskip.25in +% Dear \Field{preferredfirstname},\par +% \vskip10pt +% How come no RSVP? +% \vskip10pt +% Bilbo Baggins +% \newpage +% \end{verbatim} +% +% Here is an equivalent version using storage mode. +% The master-file: +% \begin{verbatim} +% \documentclass[12pt]{letter} +% \RequirePackage{./csvmerge} +% \begin{document} +% \mergeFields{ tex = {"./Second letter.template" } +% csv ={ ./Invites.csv}tmp={letters}} +% \input{letters} +% \end{document} +% \end{verbatim} +% +% We must adjust the |tex|-file |Second letter.template|: +% +% \begin{verbatim} +% %First page of letter +% \noexpand\setcounter{page}{1} +% +% \Field{firstname} \ifFieldEmpty{middleinitial}{}% +% {\Field{middleinitial}.\noexpand~}\Field{lastname}\noexpand\par +% \ifFieldEmpty{streetline1}{}{\Field{streetline1} \noexpand\par} +% \ifFieldEmpty{streetline2}{}{\Field{streetline2}\noexpand\par} +% \ifFieldEmpty{streetline3}{}{\Field{streetline3}\noexpand\par} +% \Field{city}, \Field{stateprovince} \Field{postalcode} +% +% \noexpand\vskip.25in +% Dear \Field{preferredfirstname},\par +% \noexpand\vskip10pt +% How come no RSVP? +% \noexpand\vskip10pt +% Bilbo Baggins +% \noexpand\newpage +% \end{verbatim} +% +% Usually using storage mode when you don't need it is wasteful since you have to create and +% save a |tmp|-file. It can come in handy if your letter code has lots of field macros and +% you can't get one |tex|-file to not have overfull/underfull boxes and the like after the +% various expansions. The |tmp|-file contains all the letters and you can fine tune each one and +% then just do +% \begin{verbatim} +% \documentclass[12pt]{letter} +% \begin{document} +% \input{letters} +% \end{document} +% \end{verbatim} + + + + + + diff --git a/Master/texmf-dist/source/latex/csvmerge/csvmerge.ins b/Master/texmf-dist/source/latex/csvmerge/csvmerge.ins new file mode 100644 index 00000000000..bf1d6b7de09 --- /dev/null +++ b/Master/texmf-dist/source/latex/csvmerge/csvmerge.ins @@ -0,0 +1,39 @@ +%% http://texdoc.net/texmf-dist/doc/latex/dtxtut/dtxtut.pdf +%% Copyright (C) 2019 by Laurence R Taylor +%% +%% This file may be distributed and/or modified under the +%% conditions of the LaTeX Project Public License, either +%% version 1.3 of this license or (at your option) any later +%% version. The latest version of this license is in: +%% +%% http://www.latex-project.org/lppl.txt +%% +%% and version 1.3 or later is part of all distributions of +%% LaTeX version 2005/12/01 or later. +%% +\input docstrip.tex +\keepsilent +\usedir{} +\preamble +This is a generated file. Copyright (C) 2019 by Laurence R Taylor +This file may be distributed and/or modified under the +conditions of the LaTeX Project Public License, either +version 1.3 of this license or (at your option) any later +version. The latest version of this license is in: + http://www.latex-project.org/lppl.txt +and version 1.3 or later is part of all distributions of +LaTeX version 2005/12/01 or later. +\endpreamble +\askforoverwritefalse +\generate{\file{csvmerge.sty}{\from{csvmerge.dtx}{package}}} +\Msg{*********************************************************} +\Msg{*} +\Msg{* To finish the installation you have to move the} +\Msg{* following file into a directory searched by TeX:} +\Msg{*} +\Msg{* \space\space csvmerge.sty} +\Msg{*} +\Msg{* To produce the documentation run the file csvmerge.dtx} \Msg{* through LaTeX.} +\Msg{*} +\Msg{*********************************************************} +\endbatchfile
\ No newline at end of file diff --git a/Master/texmf-dist/tex/latex/csvmerge/csvmerge.sty b/Master/texmf-dist/tex/latex/csvmerge/csvmerge.sty new file mode 100644 index 00000000000..06d97971930 --- /dev/null +++ b/Master/texmf-dist/tex/latex/csvmerge/csvmerge.sty @@ -0,0 +1,300 @@ +%% +%% This is file `csvmerge.sty', +%% generated with the docstrip utility. +%% +%% The original source files were: +%% +%% csvmerge.dtx (with options: `package') +%% This is a generated file. Copyright (C) 2019 by Laurence R Taylor +%% This file may be distributed and/or modified under the +%% conditions of the LaTeX Project Public License, either +%% version 1.3 of this license or (at your option) any later +%% version. The latest version of this license is in: +%% http://www.latex-project.org/lppl.txt +%% and version 1.3 or later is part of all distributions of +%% LaTeX version 2005/12/01 or later. +\NeedsTeXFormat{LaTeX2e}[2005/12/01] +\ProvidesPackage{csvmerge} + [2019/07/17 v1.0 merges TeX code with csv data] + +\RequirePackage{stringstrings} +\RequirePackage{trimspaces} +\RequirePackage{etoolbox} + +\DeclareOption*{\xdef\@LRT@CO{\CurrentOption}} +\ProcessOptions\relax + +\makeatletter + +\newcommand{\@LRT@mergeFields}[1]{% +\gdef\@LRT@csv{}% +\gdef\@LRT@tex{}% +\gdef\@LRT@tmp{}% +\setcounter{\@LRT@macroPrefix Row}{2}\relax% +\gdef\@LRT@csvBlankLine{}% +\@LRT@getFilePaths#1=@% +\if@LRT@empty{\@LRT@csv}{\@LRT@missingArgumentMessage{csv}}{}% +\if@LRT@empty{\@LRT@tex}{\@LRT@missingArgumentMessage{tex}}{}% +\if@LRT@empty{\@LRT@tmp}% +{\global\let\@LRT@Input\@LRT@streaming}% +{\global\let\@LRT@Input\@LRT@storage}% +\if@LRT@empty{\@LRT@tmp}{}{% +\immediate\openout\@lrt@tmpFile=\@LRT@tmp\relax}% +\@LRT@mergeData% +\if@LRT@empty{\@LRT@tmp}{}{\immediate\closeout\@lrt@tmpFile\relax}% +} + +\gdef\@LRT@macroPrefix{@LRT@@} + +\def\@LRT@Field#1{% +\csname\@LRT@macroPrefix#1\endcsname% +} + +\long\gdef\@LRT@ifFieldEmpty#1#2#3{\ifbool{\@LRT@macroPrefix#1}{#2}{#3}} + +{{% +\gdef\@LRT@setDelimitersCommaQuote{ +\gdef\@LRT@fieldDelimiterBack{,} +\gdef\@LRT@getFieldDelimited##1,{\@LRT@getFirstArg{##1}} +\gdef\@LRT@textDelimiterFront{"} +\gdef\@LRT@getTextDelimited"##1",{\@LRT@getFirstArg{##1}} +\gdef\@LRT@blankFieldItem{,} +\gdef\@LRT@catcodes{} +}% +}} + +{{\catcode`\^^I12\relax% +\gdef\@LRT@setDelimitersTabQuote{ +\gdef\@LRT@fieldDelimiterBack{^^I} +\gdef\@LRT@getFieldDelimited##1^^I{\@LRT@getFirstArg{##1}} +\gdef\@LRT@textDelimiterFront{"} +\gdef\@LRT@getTextDelimited"##1"^^I{\@LRT@getFirstArg{##1}} +\gdef\@LRT@blankFieldItem{} +\gdef\@LRT@catcodes{\catcode`\^^I12\relax} +}% +}} + +\expandafter\gdef\csname1238LRTLRTsvbneLRT\endcsname{@LRT@} +\makeatother +\expandafter\gdef\csname\csname1238LRTLRTsvbneLRT\endcsname +makeMePublic\endcsname\@LRT@#1\becomes#2{\global\letcs{#2}{@LRT@#1}}% +\makeatletter + +\xdef\@LRT@Row{\noexpand\number\noexpand\value{\@LRT@macroPrefix Row}} + +\newcommand{\if@LRT@empty}[3]{\ifthenelse{\equal{#1}{}}{#2}{#3}} + +\gdef\@LRT@fieldName#1{% +\caselower[q]{#1}% +\convertchar[q]{\thestring}{ }{}% +\convertchar[q]{\thestring}{,}{}% +\xdef\@LRT@TheString{\thestring}% +} + +\gdef\@LRT@getFilePaths#1=#2@{% +\xdef\@LRT@hashKey{\trim@spaces{#1}}% +\if@LRT@empty{\@LRT@hashKey}{}{ +\def\@LRT@test{tex}\ifthenelse{\equal{\@LRT@hashKey}{\@LRT@test}}{}% +{\def\@LRT@test{csv}\ifthenelse{\equal{\@LRT@hashKey}{\@LRT@test}}{}% +{\def\@LRT@test{tmp}\ifthenelse{\equal{\@LRT@hashKey}{\@LRT@test}}{}% +{\@LRT@unknownArgumentMessage{#1}}}}% +\@LRT@extractKeyValue#2@% +\expandafter\@LRT@getFilePaths\@LRT@doWeStillHaveStuffToDo @}} + +\def\@LRT@extractKeyValue#1#2@{% +\edef\@LRT@hashValue{\trim@spaces{#1}}% +\xdef\@LRT@doWeStillHaveStuffToDo{\trim@spaces{#2}}% +\expandafter\xdef\csname @LRT@\@LRT@hashKey\endcsname{\@LRT@hashValue}% +} + +\newcommand{\@LRT@SecondifFieldEmpty}[3]{% +\edef\@LRT@fieldValue{#1}% +\ifx\@LRT@fieldValue\@empty\relax{#2}\else{#3}\fi\relax} + +\gdef\@LRT@getCommaFieldDelimited#1,{\@LRT@getFirstArg{#1}} + +\newcounter{\@LRT@macroPrefix Row} + +\newread\@lrt@csvFile% +\newread\@lrt@texFile% +\newwrite\@lrt@tmpFile% + +\gdef\@LRT@justProcessedMessage{% +\immediate\typeout{}% +\immediate\typeout{Just processed row % +\@LRT@Row\space of the data file.}% +\immediate\typeout{}% +\stepcounter{\@LRT@macroPrefix Row}\relax% +} + +\gdef\@LRT@failedToOpenMessage#1{% +\immediate\typeout{}% +\immediate\typeout{File #1 failed to open.}% +\immediate\typeout{}\fi% +\end{document}% +} + +\gdef\@LRT@NoMoreDataMessage{% +\immediate\typeout{No more data.}% +\immediate\typeout{}% +} + +\gdef\@LRT@missingArgumentMessage#1{ +\immediate\typeout{}% +\immediate\typeout{I was never given a path to the #1 file.}% +\immediate\typeout{}% +\end{document}% +} + +\gdef\@LRT@unknownArgumentMessage#1{ +\immediate\typeout{}% +\immediate\typeout{I do not recognize an argument of type #1.}% +\immediate\typeout{}% +} + +\gdef\@LRT@streaming{\input{\@LRT@tex}} + +\gdef\@LRT@catcodeMagic{% +\catcode`\^^M9\relax% +\catcode`\#12\relax +\catcode`\$12\relax +\catcode`\&12\relax +\catcode`\^12\relax +\catcode`\_12\relax +\catcode`\%12\relax +} + +\gdef\@LRT@texNotAtEOF#1{% +\ifeof\@lrt@texFile{\global\booltrue{@LRT@texEOF}}\else{#1}\fi% +} + +\gdef\@LRT@storage{{% +\@LRT@catcodeMagic% +\openin\@lrt@texFile=\@LRT@tex\relax% +\@LRT@texNotAtEOF{\global\boolfalse{@LRT@texEOF}}% +\unlessboolexpr{bool{@LRT@texEOF}}{% +\read\@lrt@texFile to \@LRT@preItem% +\@LRT@texNotAtEOF{% +\immediate\write\@lrt@tmpFile{\@LRT@preItem}\relax}% +}{}% +\immediate\closein\@lrt@texFile% +}} + +\gdef\@LRT@dataLineIterator{% +\expandafter\@ifnextchar\@LRT@textDelimiterFront% +{\@LRT@getTextDelimited}{\@LRT@getFieldDelimited}} + +\gdef\@LRT@getFirstArg#1{\gdef\@LRT@firstArg{#1}\@LRT@getSecondArg} +\gdef\@LRT@getSecondArg#1@{\gdef\@LRT@restArg{#1}} + +\gdef\@LRT@getLOFirstArg#1{\gdef\@LRT@ffirstArg{#1}\@LRT@getLOSecondArg} +\gdef\@LRT@getLOSecondArg#1@{\gdef\@LRT@frestArg{#1}} + +\gdef\@LRT@fieldNameIterator#1,{\@LRT@getLOFirstArg{#1}} + +\providebool{@LRT@iterator} +\gdef\@LRT@setFirstLineAndBlankLine#1{% +\gdef\@lrt@firstline{}% +\gdef\@LRT@csvBlankLine{} +\edef\@LRT@restArg{#1\@LRT@fieldDelimiterBack} +\booltrue{@LRT@iterator}% +\whileboolexpr{bool{@LRT@iterator}}{% +\expandafter\@LRT@dataLineIterator\@LRT@restArg @% +\@LRT@fieldName{\@LRT@firstArg}% +\xdef\@lrt@firstline{\@lrt@firstline\@LRT@TheString,}% +\if@LRT@empty{\@LRT@restArg}% +{\boolfalse{@LRT@iterator}}{\booltrue{@LRT@iterator}}% +\ifboolexpr{bool{@LRT@iterator}}% +{\xdef\@LRT@csvBlankLine{\@LRT@csvBlankLine\@LRT@blankFieldItem}}{}% +}{}% +} + +\gdef\@LRT@getAndProcessNextLine{% +\unlessboolexpr{bool{@LRT@csvEOF}}{% +{{% +\@LRT@catcodeMagic% +\@LRT@catcodes +\read\@lrt@csvFile to\@LRT@x@nextLine% +\ifeof\@lrt@csvFile\relax{\global\booltrue{@LRT@csvEOF}}\else% +{\global\boolfalse{@LRT@csvEOF}}\fi% +\xdef\@LRT@nextLine{\@LRT@x@nextLine}% +}}% +{{% +\if@LRT@empty{\@LRT@nextLine}% +{\global\let\@LRT@next\@LRT@exitGetAndProcessNextLine\relax}% +{\ifx\@LRT@csvBlankLine\@LRT@nextLine\relax% +{\global\let\@LRT@next\@LRT@exitGetAndProcessNextLine\relax}\else% +{\global\let\@LRT@next\@LRT@repeatGetAndProcessNextLine\relax}\fi}% +\@LRT@next% +}}% +}{}% +} + +\gdef\@LRT@exitGetAndProcessNextLine{% +\closein\@lrt@csvFile\relax\global\booltrue{@LRT@csvEOF}} + +\gdef\@LRT@repeatGetAndProcessNextLine{% +\@LRT@processNextLine{\@LRT@nextLine\@LRT@fieldDelimiterBack}} + +\gdef\@LRT@setMacrosForOneEntry#1#2{% +\expandafter\xdef\csname\@LRT@macroPrefix#1\endcsname{#2}% +\@LRT@SecondifFieldEmpty{#2}% +{\global\booltrue{\@LRT@macroPrefix#1}}% +{\global\boolfalse{\@LRT@macroPrefix#1}}% +} + +\gdef\@LRT@processNextLine#1{% +\edef\@LRT@restArg{#1}% +\edef\@LRT@frestArg{\@lrt@firstline}% +\booltrue{@LRT@iterator}% +\whileboolexpr{bool{@LRT@iterator}}{% +\expandafter\@LRT@fieldNameIterator\@LRT@frestArg @% +\edef\@LRT@restArgA{\@LRT@restArg\@LRT@fieldDelimiterBack} +\expandafter\@LRT@dataLineIterator\@LRT@restArgA @% +\@LRT@setMacrosForOneEntry{\@LRT@ffirstArg}{\@LRT@firstArg}% +\if@LRT@empty{\@LRT@frestArg}% +{\boolfalse{@LRT@iterator}}{\booltrue{@LRT@iterator}}% +}{}% +\@LRT@Input\relax% +\@LRT@justProcessedMessage% +} + +\gdef\@LRT@mergeData{% +\providebool{@LRT@texEOF}% +\immediate\openin\@lrt@csvFile=\@LRT@csv\relax% +\providebool{@LRT@csvEOF}% +\boolfalse{@LRT@csvEOF}% +\ifeof\@lrt@csvFile\@LRT@failedToOpenMessage{\@LRT@csv}\fi% +{{\@LRT@catcodes +\read\@lrt@csvFile to\@lrt@firstlineX% +\@LRT@setFirstLineAndBlankLine{\@lrt@firstlineX} +}}% +\booltrue{@LRT@iterator} +\edef\@LRT@restArg{\@lrt@firstline}% +\whileboolexpr{bool{@LRT@iterator}}{% +\expandafter\@LRT@getCommaFieldDelimited\@LRT@restArg @% +\providebool{\@LRT@macroPrefix\@LRT@firstArg}% +\if@LRT@empty{\@LRT@restArg}% +{\boolfalse{@LRT@iterator}}{\booltrue{@LRT@iterator}}% +}{}% +\@LRT@getAndProcessNextLine% +\@LRT@NoMoreDataMessage% +} + +\gdef\@LRT@weirdOption#1{\@ifundefined{@LRT@CO}% +{\global\csletcs{#1}{@LRT@#1}}{{\global\csletcs{\@LRT@CO #1}{@LRT@#1}}}% +} + +\@LRT@weirdOption{mergeFields}% +\@LRT@weirdOption{Field}% +\@LRT@weirdOption{ifFieldEmpty}% +\@LRT@weirdOption{setDelimitersCommaQuote}% +\@LRT@weirdOption{setDelimitersTabQuote}% +\@LRT@weirdOption{makeMePublic}% + +\@LRT@setDelimitersCommaQuote + +\makeatother +\endinput +%% +%% End of file `csvmerge.sty'. |