Saturday, October 13, 2007

Joining ps files to make a quick report

Often I need to join a bunch of postscript graph files so I can print and review them. When you've got 100 odd graphs this is a pain. You could just join the ps files with gs, but if the graphs don't have titles and are only identified by filename your kind of screwed. The solution I used was to create a C++ program to generate a latex file which includes the postscripts and a note showing their filename. This program takes a list of postscipt files and an output tex file as it's arguments so I'd typically do:

find . -name *.ps > list
./makereport list report.tex
latex report.tex
dvips report.dvi


Below is the C++ source code to makereport.cpp. Paste it in to a text file and compile it using g++ i.e.:

g++ makereport.cpp -o makereport


#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <sys/stat.h>
#include <sys/types.h>
#include <stdlib.h>
#include <algorithm>
#include <vector>

using namespace std;

int addEscapes(string &input);
int findAndReplace(string &input,char find,string replace);
int findAndReplaceS(string &input,string find,string replace);

int main(int argc,char **argv) {

if(argc < 2) {
cout << "makereport <file list> <tex file>" << endl;
cout << "tex is written to tex file standard output" << endl;
return 0;
}

// start texFile
ofstream texFile (argv[2], ios::out);

ifstream listFile(argv[1], ios::in);

vector<string> filenames;
for(;!listFile.eof();) {
string temp;
listFile >> temp;

if(!listFile.eof()) filenames.push_back(temp);
}

sort(filenames.begin(),filenames.end());

texFile << "\\documentclass[11pt,a4paper,english]{report}" << endl;
texFile << "\\usepackage[T1]{fontenc}" << endl;
texFile << "\\usepackage[latin1]{inputenc}" << endl;
texFile << "\\usepackage{graphicx}" << endl;
texFile << "\\usepackage[left=2cm,right=2cm,top=1.5cm,bottom=1.5cm]{geometry}" << endl;
texFile << "\\begin{document}" << endl;
texFile << "\\title{Report}" << endl;
texFile << "\\author{Report}" << endl;

texFile << "\\maketitle" << endl << endl << endl;

vector<string>::const_iterator iter = filenames.begin();
for(;iter!=filenames.end();iter++) {
string currentInputName=(*iter);

texFile << "\\noindent" << endl;
texFile << "\\begin{minipage}{\\textwidth}" << endl;
texFile << "\\includegraphics[width=0.98\\textwidth]";
texFile << "{" << currentInputName << "}" << endl;
addEscapes(currentInputName);
texFile << "{\\footnotesize " << currentInputName << "}" << endl;
texFile << "\\end{minipage}" << endl << endl;

}

texFile << "\\end{document}" << endl;
}

int addEscapes(string &input) {
findAndReplace(input,'\\',"\\\\");
findAndReplace(input,'_',"\\_");
findAndReplace(input,'{',"\\{");
findAndReplace(input,'}',"\\}");
findAndReplace(input,'%',"\\%");
}

int findAndReplace(string &input,char find,string replace) {
string::size_type s;
s = input.find(find,0);
for(;s != string::npos;) {
input.replace(s,1,string(replace));
s = input.find(find,s+2);
}
}

int findAndReplaceS(string &input,string find,string replace) {
string::size_type s;
s = input.find(find,0);
for(;s != string::npos;) {
input.replace(s,1,string(replace));
s = input.find(find,s+find.size());
}
}



It's crap quickly hacked together code but it worked for my application. It's yours under GPL. If you make a better one, find this useful, have a better solution I'd be interested.