why unix | RBL service | netrs | please | ripcalc | linescroll
hosted services

hosted services

LaTeX documents do not crash on legal trials like MS Word documents when they're merged together.

The main reason I like LaTeX is that it can be given variables at build time, rather than when the document is being written, similar to system environment variables.

makefile

One of the things I've found from making LaTeX documents is that most of the work can be placed in a single Makefile which simplifies the process greatly.

The Makefile below makes the process simpler, for me anyway.

EVINCE=/usr/bin/evince
LATEX=/usr/bin/latex
PDFLATEX=/usr/bin/latex
DVIPDF=/usr/bin/dvipdf
DETEX=/usr/bin/detex
BIBTEX=/usr/bin/bibtex
SED=/bin/sed
SCP=/usr/bin/scp

ifndef $(OUT)
OUT=$(DOC)
endif

# this just attempts to build all the *.tex files
all:
    for i in *.tex ; do \
        echo Making $$i ; \
        make DOC=$$( echo $$i | $(SED) -e 's/\.tex$$//' ) $$i build ; \
    done ;

clean:
    rm *.aux *.dvi *.log *.out *.pdf *.blg *.bbl

build:
    $(PDFLATEX) -jobname $(OUT) -output-format=pdf "$(DEF)\input $(DOC)"
    if [ -f $(DOC).bib ] ; then $(BIBTEX) $(DOC) ; fi ;
    -$(PDFLATEX) -jobname $(OUT) -output-format=pdf "$(DEF)\input $(DOC)"
    #-$(DETEX) "$(DEF)\input $(DOC)" > $(OUT).txt
    -$(PDFLATEX) -jobname $(OUT) -output-format=pdf "$(DEF)\input $(DOC)"

    @$(EVINCE) $(OUT).pdf

For example to make all documents in the current directory run

make

Should you prefer to make just one document run

make DOC=project-details build

variables

This is perhaps the most useful thing that you can do at build time without having to parse the document from something like a template.

This is really handy if you want to output two variations on your document:

$ latex '\def\key{value} \input document'

This can be used inside your Makefile, should you want to do something such as a public or private version of your document.

Although LaTeX isn't aware of environment variables as such, for example, if you wanted to pull in the name of the current USER, this is perhaps the closest method of doing that which I am aware of.

forms

One thing which perplexed me for a little while was the mystery behind doing forms within LaTeX documents. It's all solved now but the journey was interesting and kept me quite for a little while.

It's far easier to solve this using Acrobat as I found to my dismay it's not possible with evince.

If this feature of Acrobat PDF catches on then I'm sure it will transport to other viewers but at least for the moment it's available in the Acrobat viewer for Linux.

okular

After looking around, okular does support PDF forms already but they don't support the sending of data (from what I can tell with two minutes of clicking around).

creating a form

Like mentioned above, the most confusing thing for me was realising that I was not using a formed capable viewer. Thankfully Adobe has released Acrobat Reader for Linux.

setting up your environment

LaTeX basics include using the pdftex package:

\usepackage[pdftex]{hyperref}

Once you're using pdftex it might be worth using \usepackage{thumbpdf}, but that's not essential right now.

The next stage is to setup your form where you want it in your document (you will also need a file to receive the form on a web server):

\begin{Form}[method=post, encoding=html, action=http://www.s5h.net/cgi-bin/form_submit.pl]
\begin{tabular}{ l r }
Your name & \TextField[name=name,width=10cm]{} \\
& \\
Your email & \TextField[name=email,width=10cm]{} \\
& \\
& \\
\Submit{Send} & \Reset{Clear}
\end{tabular}
\end{Form}

The above TextField should look familiar to HTML versed people, the name is the post value's key. The text in the Submit's value will appear as the button text.

This is all you need, once you build your document the data will be sent to the action URL, in this case I'm using a perl script to email that data to myself, the output of the CGI needs to tell the reader what to do. I simply tell it to reload another PDF URL.

the form recipient

Like I mentioned above, this is handled using CGI (could simply be improved to use fcgid) in this case as it was simple:

#!/usr/bin/perl

print "Content-type: application/vnd.adobe.xfdf\r\n";
print "\r\n";

open( F, "|/usr/lib/sendmail -fnospam\@s5h.net nospam\@s5h.net" ) or exit;

print F "To: <nospam\@s5h.net>\n";
print F "Subject: Post from\n";
print F "\n";

foreach( sort keys %ENV ) {
        print F "$_ = ", $ENV{$_}, "\n";
}

my @input = <STDIN>;
chomp( @input );

print F "Input data\n\n";

foreach( @input ) {
        my @ent = split( "&", $_ );
        foreach( @ent ) {
                my ($k,$v) = split( "=", $_ );
                print F $k, "=", $v, "\n";
        }
}

close(F);

my $a = <<EOT;
<?xml version="1.0" encoding="UTF-8"?>
<xfdf xmlns="http://ns.adobe.com/xfdf/" xml:space="preserve">
<f href="http://www.s5h.net/code/foo/public.pdf"/>
<ids original="7A0631678ED475F0898815F0A818CFA1" modified="BEF7724317B311718E8675B677EF9B4E"/>
<fields>
<field name="name"> <value></value> </field>
<field name="email"> <value></value> </field>
</fields>
</xfdf>
EOT

print $a;

It's very important to ensure you set the content type here to application/vnd.adobe.xfdf.

conditions

Another really useful feature of LaTeX sits in a package called ifthen.

Presume you have two, or more variations on your document, but you wish to do something different depending on the contents of a variable (passed in through a \def perhaps).

In this case you might want the output of a command to be inserted in your document:

\ifthenelse{\equal{\food}{Egg}} {\recipeForm}{}

So the command recipeForm could be:

\newcommand{\recipeForm} {
    ...
    \TextField[name=food,width=10cm]{}
    \TextField[name=ingredients,width=10cm]{}
    ...
}

If you want your condition to do something when a variable is defined, and nothing when it's not, similiar to, "if not equal to null" then this is probably the closest way:

\ifthenelse{\equal{\food}{}} { } { \food{} }

Which is saying in pseudo-code:

if food equals null 
then
  break
else
  print food
end

In my opinion, this is a really handy feature, especially when your document needs to take different forms. It's quite possible to do the same by having two documents which are templates and bring in different files depending on their configuration but this to me is a much more consistent way to do the task.

Other things can be done here, which could be more interesting, such as using \input to include other documents based on variables, but be careful not to split the document into unmanageable sections which become unwieldy.

collaboration

However you decide to store your documents in revision control (subversion) I'd like to suggest that when editing you follow some basic rules:

editing

Paragraphs and sentences should be on one line by themselves. The reason being that when a co-editor compares your edit against their commit at merge time, it makes life easier when the diff is restricted to just one line and not several.

Try to set your editor to not wrap at 80 cols. If you're using vim the following might be of use:

set linebreak

This then ensures that your words are still readable and not spanning lines.

It goes without saying that your edits should have sensible and meaningful comments when committing them to version control. When making your commit ensure that you're changing large potions of the document through doing something such as re-indenting the whole document.

centralise common parts

It's well worth centralising common parts of the document, headers, footers, bibliographies.

\input{../common/common_header.tex}

for example.

kindle formats

I am now a proud owner of an Amazon Kindle. When writing documents it's nice to review them on the Kindle later, or even just convert PDF documents to the kindle.

There are two ways I've found to go about this:

  • produce a document that's in the correct dimensions for the Kindle
  • convert the document through one or more stages to a .mobi document

The former is perhaps the easiest.

creating a pdf in correct dimensions for kindle

This is easiest, although the Kindle cannot resize the font, however. Firstly change the paper size:

\usepackage[paperwidth=9cm, paperheight=12cm, top=1cm, left=1cm, right=1cm, bottom=1.5cm, includefoot]{geometry}

You may wrap this inside an ifthenelse block, so that you can pass in a \def to trigger this.

Within the Makefile above you can add this stage:

ifndef $(FORMAT)
PAPER=a4paper,top=2cm,bottom=2cm,left=2cm,right=2cm
endif

ifeq ($(FORMAT),kindle)
PAPER=paperwidth=9cm, paperheight=12cm, top=1cm, left=1cm, right=1cm, \
bottom=1.5cm, includefoot
endif

DEF=\def\paper{$(PAPER)}

The first if block sets the default paper size, the next is a condition for the kindle only. We then add the paper settings to a define.

Within the document you attach to this with this hook:

\ifthenelse{\equal{\paper}{}} {
    \usepackage[top=2cm,bottom=2cm,left=2cm,right=2cm]{geometry}
} {
    \usepackage[\paper]{geometry}
}

converting the pdf to mobi

This is easiest if you prefer not to alter the document. It's fairly simple again to add this to the Makefile:

At the top:

EBOOKCONVERT=/usr/bin/ebook-convert

Within the build: section:

ifeq ($(FORMAT),kindle)
    -$(EBOOKCONVERT) $(DOC).pdf $(DOC).mobi --output-profile $(FORMAT)
else 
    -$(EBOOKCONVERT) $(DOC).pdf $(DOC).mobi
endif

We use the FORMAT variable so that should we output for other ebook formats its relatively simple to change.

(pandoc is also available.)

external resources

Latex font catalogue