Thursday, December 3, 2009

How to generate a tart/clock shaped progress meter for latex-beamer

Update: Here is a rewrite of the previously published version. This new version is a bit more elegant and uses the pgfmath package for computations. So, to have a tart diagram inform your listeners about the progress made in your presentation, include the following TeX code into the preamble of your document. It will redefine the current frametitle template so that after the frame title the tart will appear. I've only tested this in the absence of any other templates, so it might not work straight out of the box for your setup. Feel free to modify this, though.

\usepackage{pgfmath}
\usepackage{tikz,ifthen}%


%% frametitle with tart-like progress meter
\setbeamertemplate{frametitle}{%
  \vskip-2pt%
  \begin{beamercolorbox}[rightskip=2cm,leftskip=1em,dp=1ex,wd=12.8cm]{frametitle}%
    \vskip2pt%
    \usebeamercolor{frametitle}%
    \begin{tikzpicture}[scale=1]% 
      \useasboundingbox (0,0) rectangle (0,0); %(-1,-1) rectangle (1,1);%
      \ifthenelse{\insertframenumber<\inserttotalframenumber}{%
        %% uncomplete tart          
        \pgfmathsetmacro{\aimangle}{90-(\insertframenumber*360/\inserttotalframenumber)}
        \fill [fill=frametitle.fg,thin] (11.8,.1) -- (11.8,0.4) arc
        (90:\aimangle:0.3) -- cycle;%
      }{
        %% the full tart
        \fill[fill=frametitle.fg,thin] (11.8,.1) circle (.3);%
      }%
    \end{tikzpicture}
    \insertframetitle%
    \vskip2pt%
  \end{beamercolorbox}% 
}

Documentation on the commands used here can be found in your local pgfuserguide.pdf and ifthen.pdf which should come with any recent TeX distribution.

Thursday, November 5, 2009

Generating local-url fields in BibDesk

Here's a script that I use to generate the local-url fields for my BibDesk databases necessary for my own scripts to be able to access linked files. The script is only a very minor modification of something I found a while ago on the BibDesk wiki. Also, it is still not perfect, as it would overwrite local-url fields very time it is run. I just don't have the time and motivation to fix that right now :-(

(*
Add Local-Url fields to all publications, containing a relative (if possible) path to the file encoded in the Bdsk-File-1 entry.
*)

tell application "BibDesk"
        activate
        set theDoc to get first document
        set theFile to (get file of theDoc)
        set theDocDir to my containerPath(get file of theDoc)
        tell theDoc
                set thePublications to displayed publications
                repeat with currentpub in thePublications
                        tell currentpub
                                set theID to get id
                                set the value of field "Local-Id" to theID
                                if ((count of (get linked files)) > 0) and ((get linked file 1) is not missing value) then
                                        set linkedFile to linked file 1
                                        tell linkedFile
                                                set thePath to get POSIX path
                                        end tell
                                        --set thePath to my relativePath(thePath, theDocDir)
                                        (* we might want to change the following line to a conditional, 
                                        to prevent that manually changed local-urls get overwritten when 
                                        this script is used again *)
                                        set the value of field "Local-Url" to thePath
                                end if
                        end tell
                end repeat
        end tell
end tell

on relativePath(fullPath, basePath)
        set theLength to length of basePath
        if (length of fullPath > theLength) and (text 1 thru theLength of fullPath) = basePath then
                return text theLength thru end of fullPath
        else
                return fullPath
        end if
end relativePath

on containerPath(theFile)
        set AppleScript's text item delimiters to "/"
        set theComponents to text items of (POSIX path of theFile)
        if last item of theComponents is "" and (count of theComponents) > 2 then
                set theComponents to items 1 thru -3 of theComponents
        else
                set theComponents to items 1 thru -2 of theComponents
        end if
        return (theComponents as text) & "/"
end containerPath

BibDesk and List of Publications

BibDesk is a fantastic tool to organize your library of scientific papers. It can also be used to organize your own publications. On my webpage I prefer to list my publications in a special order, namely organized by the categories journal papers and book chapters, conference papers, and everything else. Ideally, each record would also link to an attached PDF file and all I would have to do is keep my BibDesk database organized and my online list of publications would be kept up-to-date automatically.

Unfortunately, generating publication listings with attached PDF files organized by categories doesn't seem to be so straightforward in BibDesk's very own export mechanism. So here is what I came up with myself, a PERL script that I can call from within emacs to automatically include the list of publications where I need it.

#!/usr/bin/perl -w
# Copyright (C) 2009 by Bjoern Rueffer, Time-stamp: <2009-11-05 00:59:33 bjoern>

# This program TAKES A BIBDESK FILE (essentially a bibtex file) AND
# GENERATES A HTML PUBLICATION LIST FROM IT. This list is formatted
# using CSS tags and its intended use is to be insered into my
# homepage (a single html file as of the time of this writing). This
# programm can be called from within emacs and the actual insertion
# can be performed automatically, e.g., by using a small emacs lisp
# function embedded into the html-file. Something like this:
#       <!-- 
#       (let ((beg (progn (search-forward "startinsert")
#                         (forward-line 1)
#                         (point))))
#       (search-forward "/endinsert")
#       (beginning-of-line)
#       (delete-region beg (point))
#       (shell-command "~/path/to/this/file.pl" 1 "perl output to STDERR")
#       )   type C-x C-e after the closing brace to update publication listing
#       -->
#       <!--startinsert-->
#       <!--/endinsert-->
#
# Admittedly, this might not be the most elegant way to do it, but it
# is pretty effective.

# This program REQUIRES Text::BibTeX version >= 0.34 from
# http://starship.python.net/~gward/btOOL/ for reading the bibtex file
# and formatting author names etc. A very handy tool!

# All CONFIGURATION is currently hard-coded into this file; A bibtex
# file "$bibdeskfile" is read and the generated HTML output is pasted
# into STDOUT. File attachments (via the local-url-fields and assumed
# to be PDFs) will be copied to a directory
# "$attachmentdirabsolute". During the process, lists of authors will
# be condensed, by removing any author whose name matches /R.*ffer/ --
# you might want to adapt that for your purpuses.

# Use this software for whatever you want at your own risk, but don't
# blame me for anything. If you want to report any improvements you've
# made back to me, please do so to the email address to be found at
# http://bjoern.rueffer.info. I'd appreciate that!

use warnings; 
use strict;
use Text::BibTeX qw(:nameparts :joinmethods);
use Text::BibTeX::Name;
use Text::BibTeX::NameFormat;

### setup 
my $attachmentdirrelative = "attachments"; # relative directory name: where to link to for attached files
my $attachmentdirabsolute = "/path/to/Homepage/attachments"; # absolute directory name: where to put attached files
mkdir $attachmentdirabsolute unless (-d $attachmentdirabsolute);
print STDERR readpipe("rm -f $attachmentdirabsolute/*.pdf")."\n";

my $bibdeskfile = new Text::BibTeX::File "</path/to/Publications/mypublications.bib"; # which bibdesk file to load?

my $nameformat = new Text::BibTeX::NameFormat("fvlj", 1); # name formatting rules, see "man Text::BibTeX::Name"

sub texcleanedstring {          # does exactly what its name says, at least it takes care of everything that was needed in my case
  $_ = shift;
  s/\\verb\|(.+)\|/$1/g;         # remove \verb|...|
  s/\\em //g;                    # remove formatting
  s/\\ |~/ /g;                   # remove formatting
  s/\{\\"([auoAUO])\}/&$1uml;/g; # aou umlauts into HTML
  s/\{|\}//g;                    # remove curly braces 
  s/\$//g;                       # remove dollar signs
  s/--/&#8211;/g;                # correct en dashes
  return $_;
}

sub formatauthorstring { # reformat list of authors, put them into "(with x,y and z)"-form
  my @authors = split /\s+and\s+/, shift;
  my $authors=@authors;
  $authors--;
  my $f = "";
  $f .= "(with " unless $authors==0;
  foreach my $author (@authors) { # put authors into a x,y and z format
    my $name = new Text::BibTeX::Name($author); 
    next if ($author =~ /R.*ffer/i); # don't mention yourself
    $f .= $name->format($nameformat); # this takes care of the formatting, 
    if ($authors>2) {
      $f .= ", ";
      $authors--;
    } elsif ($authors==2) {
      $f .= " and ";
      $authors--;
    } 
  }
  $f .= ")" unless @authors==1;
#  print $f;
  return $f;
}

sub formateditorstring {        # similar to formatauthorstring, but different =-)
  my @authors = split /\s+and\s+/, shift;
  my $authors=@authors;
  my $f = "";
  foreach my $author (@authors) {
    my $name = new Text::BibTeX::Name($author);
    next if ($author =~ /R.*ffer/i);
    $f .= $name->format($nameformat);
    if ($authors>2) {
      $f .= ", ";
      $authors--;
    } elsif ($authors==2) {
      $f .= " and ";
      $authors--;
    } 
  }
  return $f;
}

my %jpapers = ();               # journal papers and book chapters
my %cpapers = ();               # conference papers
my %miscpapers = ();            # theses and reports

my $publicationcounter = 0;     # used for reversenumbering publications in html output

while (my $entry = new Text::BibTeX::Entry $bibdeskfile) # for each publication record in the bibtex file...
  {
    next unless $entry->parse_ok; # yeah, we might want to not consider everything, like @string{} and similar entries
    next unless $entry->type =~ /article|inproceedings|incollection|thesis|report/; # or @unpublished entries for that matter
    $publicationcounter += 1;
    $_= "";

    # some of my publication entries have a special key "publish-pdf"
    # to indicate whether an attached file should be made public or
    # not (this is a boolean field in BibDesk, very convenient to
    # handle). I'm assuming here that all attached files are of PDF
    # type.

    if ($entry->exists('publish-pdf')) { # create a copy of the attached (PDF!) file with a simplified filename
      if (($entry->exists('local-url')  && $entry->type !~ /thesis|report/) && 
          $entry->get('publish-pdf') =~ m/yes|true|1/i) {
        my $lurl = $entry->get('local-url'); # this field contains a link to (one of the) attached PDF files, see comment below
        $lurl =~ s/`/\\`/g;
        $_ .= "<div class=\"pdf\"><a href=\"$attachmentdirrelative/$publicationcounter.pdf\" class=\"pdf\"><img class=\"pdf\" src=\"Oficina-PDF-128x128.png\"></a></div>\n";
        print STDERR readpipe("cp \"$lurl\" $attachmentdirabsolute/$publicationcounter.pdf"); 
      }
    }  
    # to actually generate these "local-url" fields, I've used an
    # applescript within BibDesk... see other blog post on that

    # the next few if-clauses take care of the formatting of each
    # individual publication record, as it appears on my online
    # publication listing. Obviously journal and conference papers
    # have to be treated differently. To some degree we are doing
    # BibTeX's job here, but by doing it "manually", we can actually
    # insert css-tags so that we can change the looks later on.

    if ($entry->type =~ /thesis/) {
      $_ .= "<span class=\"pubtitle\">".$entry->get('title').".</span>\n  " if $entry->exists('title');
      $_ .= "<br />";
      if ($entry->type =~ /mastersthesis/) {
        $_ .= "Masters thesis, ";
      } elsif  ($entry->type =~ /phdthesis/) {
        $_ .= "PhD thesis, ";
      }
      $_ .= "<i>".$entry->get('school')."</i>, " if $entry->exists('school');
      $_ .= $entry->get('month').",\n  " if $entry->exists('month');
      $_ .= $entry->get('year')."\n" if $entry->exists('year');
    }

    if ($entry->type =~ /report/) {
      $_ .= "<span class=\"pubtitle\">".$entry->get('title').".</span>\n  " if $entry->exists('title');
      $_ .= "".formatauthorstring($entry->get('author'))." " if ($entry->exists('author'));
      $_ .= "<br />Technical report, ";
      $_ .= "<i>".$entry->get('institution')."</i>, " if $entry->exists('institution');
      $_ .= $entry->get('month').",\n  " if $entry->exists('month');
      $_ .= $entry->get('year')."\n" if $entry->exists('year');
    }


    if ($entry->type eq 'article') {
      #$_ .= formatauthorstring($entry->get('author')).": " if ($entry->exists('author'));   # this would be an altertnative way to list the authors; be consistent!
      $_ .= "<span class=\"pubtitle\">".$entry->get('title').".</span>\n  " if $entry->exists('title');
      $_ .= "".formatauthorstring($entry->get('author'))." " if ($entry->exists('author'));
      $_ .= "<br /><i>".$entry->get('journal')."</i>" if $entry->exists('journal');
      $_ .= " <b>".$entry->get('volume')."</b>" if $entry->exists('volume');
      $_ .= ", " unless $entry->exists('volume');
      $_ .= "(".$entry->get('number').")" if $entry->exists('number');
      $_ .= ":".$entry->get('pages').",\n  " if $entry->exists('pages');
      $_ .= $entry->get('year')."\n" if $entry->exists('year');
    }

    if ($entry->type eq 'incollection') {
      $_ .= "<span class=\"pubtitle\">".$entry->get('title').".</span>\n  " if $entry->exists('title');
      $_ .= "".formatauthorstring($entry->get('author'))." " if ($entry->exists('author'));
      $_ .= "<br />In: ";
      $_ .= "<i>".formateditorstring($entry->get('editor'))." (Eds.)</i>:\n  " if $entry->exists('editor');
      $_ .= "<i>".$entry->get('booktitle')."</i>,\n  " if $entry->exists('booktitle');
      $_ .= "pp. ".$entry->get('pages').",\n  " if $entry->exists('pages');
      $_ .= "".$entry->get('publisher').", " if $entry->exists('publisher');
      $_ .= "".$entry->get('address').", " if $entry->exists('address');
      $_ .= $entry->get('year')."\n" if $entry->exists('year');
    }

    if ($entry->type eq 'inproceedings') {
      $_ .= "<span class=\"pubtitle\">".$entry->get('title').".</span>\n  " if $entry->exists('title');
      $_ .= "".formatauthorstring($entry->get('author'))." " if ($entry->exists('author'));
      $_ .= "<br />In: <i>".$entry->get('booktitle')."</i>,\n  " if $entry->exists('booktitle');
      $_ .= $entry->get('address').",\n  " if $entry->exists('address');
      $_ .= "pp. ". $entry->get('pages').",\n  " if $entry->exists('pages');
      $_ .= $entry->get('month').",\n  " if $entry->exists('month');
      $_ .= $entry->get('year')."\n" if $entry->exists('year');
    }

    if ($entry->exists('note')) { # the note field contains "to appear", "submitted June 2009" etc. it might also contain a DOI as text
      my $bibnote = $entry->get('note');
      $bibnote =~ s/(http:\/\/[-a-zA-Z\/.:0-9]+)/<a href="$1">[external resource]<\/a>/g; # activate hyperlinks
      $bibnote =~ s/DOI:([\w\d.\/-]+)/\n<span class=\"pubDOI\">DOI:<a class=\"pubDOI\" href=\"http:\/\/dx.doi.org\/$1\">$1<\/a><\/span>\n/; # activate silent DOIs
      $_ .= "<span class=\"pubNOTE\">".$bibnote."</span>\n";
    }  

    my $publicationentry = texcleanedstring($_); # just to backup $_ to somewhere

    if ($entry->exists('doi')) { # some publications do have a doi-field, and this is how it gets formatted
      my $doi = $entry->get('doi');
      $publicationentry .= "\n<span class=\"pubDOI\">DOI:<a class=\"pubDOI\" href=\"http://dx.doi.org/$doi\">$doi</a></span>\n";
    }

    if ($entry->exists('online-information')) { # need that for Springer/Positivity disclaimer "The original publication is available at www.springerlink.com." 
      $publicationentry .= "\n<br /><span class=\"pubONLINE-INFORMATION\">".$entry->get('online-information')."</span>\n";
    }

    if ($entry->exists('year')) { # now we generate hashes and reverse sort them by year (and publication title to make the keys unique)
      my $tag = "";
      $tag = $entry->get('year'); 
      $tag .= texcleanedstring($entry->get('title'));

      # finally decide to which category the publication entry we have
      # been working on belongs to and file it
      if ($entry->type =~ /article|incollection/) { 
        $jpapers{$tag} = $publicationentry;
      } elsif ($entry->type eq 'inproceedings') {
        $cpapers{$tag} = $publicationentry;    
      } elsif ($entry->type =~ /thesis|report/) {
        $miscpapers{$tag} = $publicationentry;    
      }      

    } else {
      warn "ERROR: Found an entry without a year. You'd want to correct that in the bibtex file! Meanwhile I'll ignore that entry.\nThe entry in question is: $_\n\n"; 
      # this may actually happen despite good intentions, e.g., if
      # there is a hard-coded html hyperlink in one of the special
      # tags (e.g., the "online-information" key is a candidate for
      # that).
    }
  }

print STDERR "Read $publicationcounter publication entries from BibTeX file.\n"; # report status 

# we are not actually generating a complete and valid html file, just
# a snipped to paste into something bigger, but could do something like this:

# print "<html><head><title>My list of publications</title></head><body>\n";

print "<h3>Journal papers and book chapters</h3>\n\n";
my @orderedkeys =  reverse sort { $a cmp $b } (keys %jpapers);
foreach my $key (@orderedkeys) {
  print "<div class=\"pub\"><div class=\"pubCOUNTER\">[$publicationcounter]</div>\n".$jpapers{$key}."</div>\n";
  $publicationcounter--;
}

print "\n\n<h3>Conference articles</h3>\n\n";
@orderedkeys =  reverse sort { $a cmp $b }  (keys %cpapers);
foreach my $key (@orderedkeys) {
  print "<div class=\"pub\"><div class=\"pubCOUNTER\">[$publicationcounter]</div>\n".$cpapers{$key}."</div>\n";
  $publicationcounter--;
}

print "\n\n<h3>Theses and reports</h3>\n\n";
@orderedkeys =  reverse sort { $a cmp $b }  (keys %miscpapers);
foreach my $key (@orderedkeys) {
  print "<div class=\"pub\"><div class=\"pubCOUNTER\">[$publicationcounter]</div>\n".$miscpapers{$key}."</div>\n";
  $publicationcounter--;
}

# print "</body></html>\n"; # we don't need this for our purposes

$bibdeskfile->close;            # not sure that this isn't being taken care of by Text::BibTeX anyways

Björn Rüffer, Copyright 2009
Disclaimer: The author of this page cannot accept any responsibility for content of pages that this page provides hyperlinks to. Also the author cannot accept responsibility or liability for any damage caused by following or not following instructions given on this page. This page, its contents and style, are the responsibility of the author and do not represent the views, policies or opinions of any other party. The author will not be held responsible for comments posted by third parties on this site. Inappropriate comments will be removed. There is no warranty for any program or source code provided here, to the extent permitted by applicable law. Except when otherwise stated in writing the copyright holders and/or other parties provide the program “as is” without warranty of any kind, either expressed or implied, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose. The entire risk as to the quality and performance of the program is with you. Should the program prove defective, you assume the cost of all necessary servicing, repair or correction.