Home > atmlab > handy > combine_pdfs.m

combine_pdfs

PURPOSE ^

COMBINE_PDFS Combine several PDF files on one page

SYNOPSIS ^

function outfile = combine_pdfs(pdffiles, options)

DESCRIPTION ^

 COMBINE_PDFS Combine several PDF files on one page

 USAGE: outfile = combine_pdfs(pdffiles, options)

 E.g. file = combine_pdfs(files,struct('cols',[2 2 2 2 1],...))

 By default, pdf files are created in gmtlab('outdir')

 PURPOSE:  Merge several pdf's (one page each) into a pdf file

 EXTERNAL DEPENDENCIES: pdfinfo, pdflatex.

 IN:   pdffiles            cell: Each element contains the path to a
                                 file.
                           OR
                           string: 'dir(<string>)' will get a list of files,
                                   globbing is allowed:
                             e.g pdffiles = figs/*{CAlow,CAmiddle,CAhigh}*


 OPT
       % HOW TO READ VARIABLE DESCRIPTION OF OPTIONAL VARIABLES:
         KEY:  in.variable   in: = expect input type, ex: = Explanation/Example, de: = Default value/behavior
         NOTE: If "def" is missing for a variable it means the variable is not used by default

         types: %s=character or string
                %f=numeric/logical
                {}=cell

       Structure where the following fields are considered options:

       options.cols
            in:  %d
            ex:  Number of columns per row. option.cols can also be a vector
                 containing the number of columns that should be on each each row.
                 In that case, the sum of this vector must match the number of pdf files.
            def: 2
       options.filename
            in:  %s
            ex:  name of output file
            def: 'out.pdf'
       options.outdir
            in:  %s
            ex:  directory for output
            def: gmtlab('outdir')
       options.vspace
            in:  %f
            ex:  space between rows in cm
            def: 1
       options.hspace
            in:  %f
            ex:  space between columns in cm
            def: 1
       options.rightPadding
            in:  %f
            ex:  padding to the right of a row in cm. Note: Needs one value per row.
                 e.g., for 4 rows something like options.rightPadding=[1.7,1.7,0,0]
            def: 1 
       options.valign
            in:  %s
            ex:  vertical alignment. 'm' = middle, 'b' = bottom, 't'=top
            def: 'm'
       options.scale
            in:  [%d,%d; etc]
            ex:  scale individual figures. [filenumber,scale]
            def: 1
       options.title
            in:  %s
            ex:  title to be displayed above the combined pdfs
            def: <none>
       options.fontsize
            in:  %s
            ex:  E.g., '4cm' or '40pt'
            def: 1/22 * rowwidth (but only relevent if title is given)
       options.display
            in:  %f
            ex:  tries to open pdf file with 'gnome-open' or 'kde-open'
                 if you are on a linux and 'open' if called from Mac
            def: false
       options.pdfviewer
            in:  %s
            ex:  Your pdf-viewing program of choice
            def: Mac: 'open'
                 Linux: 'gnome-open' or if that does work,
                        'kde-open'. If neither exists, nothing is displayed
       options.pdfcrop
            in:  %f
            ex:  Do a pdfcrop when finished putting everything together
            def: false

 OUT:      outfile = The fullpath to the created pdf-file

 NOTE: The files are appended left to right and from top to bottom.

CROSS-REFERENCE INFORMATION ^

This function calls: This function is called by:

SUBFUNCTIONS ^

DOWNLOAD ^

combine_pdfs.m

SOURCE CODE ^

0001 function outfile = combine_pdfs(pdffiles, options)
0002 % COMBINE_PDFS Combine several PDF files on one page
0003 %
0004 % USAGE: outfile = combine_pdfs(pdffiles, options)
0005 %
0006 % E.g. file = combine_pdfs(files,struct('cols',[2 2 2 2 1],...))
0007 %
0008 % By default, pdf files are created in gmtlab('outdir')
0009 %
0010 % PURPOSE:  Merge several pdf's (one page each) into a pdf file
0011 %
0012 % EXTERNAL DEPENDENCIES: pdfinfo, pdflatex.
0013 %
0014 % IN:   pdffiles            cell: Each element contains the path to a
0015 %                                 file.
0016 %                           OR
0017 %                           string: 'dir(<string>)' will get a list of files,
0018 %                                   globbing is allowed:
0019 %                             e.g pdffiles = figs/*{CAlow,CAmiddle,CAhigh}*
0020 %
0021 %
0022 % OPT
0023 %       % HOW TO READ VARIABLE DESCRIPTION OF OPTIONAL VARIABLES:
0024 %         KEY:  in.variable   in: = expect input type, ex: = Explanation/Example, de: = Default value/behavior
0025 %         NOTE: If "def" is missing for a variable it means the variable is not used by default
0026 %
0027 %         types: %s=character or string
0028 %                %f=numeric/logical
0029 %                {}=cell
0030 %
0031 %       Structure where the following fields are considered options:
0032 %
0033 %       options.cols
0034 %            in:  %d
0035 %            ex:  Number of columns per row. option.cols can also be a vector
0036 %                 containing the number of columns that should be on each each row.
0037 %                 In that case, the sum of this vector must match the number of pdf files.
0038 %            def: 2
0039 %       options.filename
0040 %            in:  %s
0041 %            ex:  name of output file
0042 %            def: 'out.pdf'
0043 %       options.outdir
0044 %            in:  %s
0045 %            ex:  directory for output
0046 %            def: gmtlab('outdir')
0047 %       options.vspace
0048 %            in:  %f
0049 %            ex:  space between rows in cm
0050 %            def: 1
0051 %       options.hspace
0052 %            in:  %f
0053 %            ex:  space between columns in cm
0054 %            def: 1
0055 %       options.rightPadding
0056 %            in:  %f
0057 %            ex:  padding to the right of a row in cm. Note: Needs one value per row.
0058 %                 e.g., for 4 rows something like options.rightPadding=[1.7,1.7,0,0]
0059 %            def: 1
0060 %       options.valign
0061 %            in:  %s
0062 %            ex:  vertical alignment. 'm' = middle, 'b' = bottom, 't'=top
0063 %            def: 'm'
0064 %       options.scale
0065 %            in:  [%d,%d; etc]
0066 %            ex:  scale individual figures. [filenumber,scale]
0067 %            def: 1
0068 %       options.title
0069 %            in:  %s
0070 %            ex:  title to be displayed above the combined pdfs
0071 %            def: <none>
0072 %       options.fontsize
0073 %            in:  %s
0074 %            ex:  E.g., '4cm' or '40pt'
0075 %            def: 1/22 * rowwidth (but only relevent if title is given)
0076 %       options.display
0077 %            in:  %f
0078 %            ex:  tries to open pdf file with 'gnome-open' or 'kde-open'
0079 %                 if you are on a linux and 'open' if called from Mac
0080 %            def: false
0081 %       options.pdfviewer
0082 %            in:  %s
0083 %            ex:  Your pdf-viewing program of choice
0084 %            def: Mac: 'open'
0085 %                 Linux: 'gnome-open' or if that does work,
0086 %                        'kde-open'. If neither exists, nothing is displayed
0087 %       options.pdfcrop
0088 %            in:  %f
0089 %            ex:  Do a pdfcrop when finished putting everything together
0090 %            def: false
0091 %
0092 % OUT:      outfile = The fullpath to the created pdf-file
0093 %
0094 % NOTE: The files are appended left to right and from top to bottom.
0095 
0096 
0097 % 2013-04-10 Oliver Lemke and Salomon Eliasson.
0098 
0099 if ~nargin, error(['atmlab:' mfilename],'Needs input. See help.'); end
0100 if nargin == 1, options = struct; end
0101 
0102 %% Set defaults and use them if not specified by user
0103 default.cols        = 2;
0104 default.vspace      = 1;
0105 default.hspace      = 1;
0106 default.valign      = 'm';
0107 if isfield(options,'title') && ~isempty(options.title)
0108     default.filename = [sanitise(options.title),'.pdf'];
0109 else
0110     default.filename = 'out.pdf';
0111 end
0112 default.outdir     = gmtlab('outdir');
0113 default.secret_latex_spacing_tweak = 0.5;
0114 default.display    = false;
0115 default.title      = ''; 
0116 default.pdfcrop    = false;
0117 default.pdfviewer  = '';
0118 options = optargs_struct(options, default);
0119 
0120 %% Convert outdir to fullpath
0121 options.outdir = path_replace_tilde(options.outdir);
0122 if (isempty(options.outdir) || options.outdir(1) ~= filesep())
0123     options.outdir = fullfile(pwd(), options.outdir);
0124 end
0125 
0126 %% Sanity checks
0127 
0128 assert(length(options.valign) == 1 && any('mbt' == options.valign), ...
0129     'atmlab:combine_pdfs', 'Vertical alignment must be either ''m'' or ''b''');
0130 assert(isdir(options.outdir), 'atmlab:combine_pdfs', ...
0131     '%s does not exist', options.outdir);
0132 
0133 
0134 %% If we got a regexp, use ls to get the file list
0135 if ischar(pdffiles), files = dir(pdffiles); pdffiles = {files.name}; end
0136 
0137 %% Make sure filenames are in a row vector and non-empty
0138 pdffiles = pdffiles(:)';
0139 pdffiles = pdffiles(~cellfun('isempty',pdffiles));
0140 pdffiles = path_replace_tilde(pdffiles);
0141 assert(~isempty(pdffiles), 'atmlab:combine_pdfs', 'List of input pdf files is empty');
0142 
0143 %% We trust that the user can count! Not really, better double check here
0144 if (length(options.cols) ~= 1 && sum(options.cols) ~= length(pdffiles))
0145     error (['atmlab:' mfilename]', ...
0146         'Number of plots (%d) doesn''t match sum of columns (%d)', ...
0147         length(pdffiles), sum(options.cols));
0148 end
0149 
0150 %% If cols is only one number, replicate it to match the number files
0151 if (length(options.cols) == 1)
0152     options.cols = repmat(options.cols, ceil(length(pdffiles)/options.cols), 1);
0153 end
0154 
0155 %% Setup is complete, let's get cracking
0156 layout = create_layout_from_pdffiles(pdffiles, options);
0157 
0158 create_pdf_from_layout_with_latex(layout, options);
0159 
0160 outfile = fullfile(options.outdir, options.filename);
0161 
0162 %% Display output
0163 % This only works for the following pre-programmed options
0164 if options.display && isempty(options.pdfviewer)
0165     if ismac
0166         exec_system_cmd(sprintf('open %s',outfile));
0167     elseif isunix
0168         [~,b1]=system('which gnome-open');
0169         [~,b2]=system('which kde-open');
0170         if ~isempty(b1)
0171             exec_system_cmd(sprintf('gnome-open %s &',outfile));
0172         elseif ~isempty(b2)
0173             exec_system_cmd(sprintf('kde-open %s &',outfile));
0174         end
0175     end
0176 elseif options.display
0177     exec_system_cmd(sprintf('%s %s',options.pdfviewer,outfile));
0178 end
0179 
0180 end
0181 
0182 %% Only subfunctions allowed after this point!!!
0183 
0184 function layout = create_layout_from_pdffiles(pdffiles, options)
0185 
0186 % Create a fancy regexp that gives us width and height from pdfinfo output
0187 regx = 'Page size: +(?<width>\d.+).x.(?<height>\d.+) pts';
0188 
0189 col = 1; row = 1;filenum=1;
0190 for P = pdffiles
0191     info  = exec_system_cmd(sprintf('pdfinfo ''%s''', P{1}), gmtlab('VERBOSITY'));
0192     
0193     % Great, pdfinfo uses BigPoints as a unit. Let's throw in some fancy
0194     % numbers and hope we end up with centimeters after that (DPI*INCH).
0195     % This might break for some PDFs, better keep your fingers crossed.
0196     psize =  structfun(@(x) str2double(x)/72*2.54, ...
0197         regexp(info{1},regx, 'names'), ...
0198         'uniformoutput', 0);
0199     
0200     % --------------
0201     % Apply scaling
0202     % --------------
0203     if isfield(options,'scale') && ~isempty(options.scale(options.scale(:,1)==filenum,2))
0204         sc = options.scale(options.scale(:,1)==filenum,2);
0205     else
0206         sc = 1;
0207     end
0208     layout.plotsbyrow{row}(col) = struct('height', psize.height*sc, ...
0209         'width', psize.width*sc, ...
0210         'file', P{1});
0211     
0212     col = col + 1;
0213     if (col > options.cols(row) ...
0214             || length([layout.plotsbyrow{:}]) == length(pdffiles))
0215         col = 1;
0216         R = layout.plotsbyrow(row);
0217         layout.rowwidth(row) = sum([R{:}.width]) ...
0218             + options.hspace*(length(R{:})-1) ...
0219             + options.secret_latex_spacing_tweak*length(R{:});
0220         layout.rowheight(row) = max([R{:}.height]);
0221         row = row + 1;
0222     end
0223     filenum = filenum+1;
0224 end
0225 
0226 end
0227 
0228 function file = create_pdf_from_layout_with_latex(layout, options)
0229 
0230 tmpfolder = create_tmpfolder();
0231 
0232 %% Hook up a cleanup callback.
0233 % In case we screw up the LaTeX run, we don't want to leave any
0234 % evidence behind.
0235 c = onCleanup(@() delete_tmpfolder(tmpfolder));
0236 
0237 %% Assemble our pretty tex file
0238 file = fullfile(tmpfolder, 'out.tex');
0239 fid = fopen(file,'w');
0240 Cob = onCleanup(@(x) fclose(fid));
0241 
0242 fprintf(fid,'\\documentclass[a4paper,10pt]{report}\n');
0243 
0244 margin=0.1; %cm
0245 if ~isempty(options.title)
0246     options = optargs_struct(options,struct('fontsize',sprintf('%gcm',1/20 * max(layout.rowwidth)))); % cm
0247     tmp=regexp(options.fontsize,'(?<fz>\d+\.?\d*)(?<unit>.+)','names');
0248     fz=str2double(tmp.fz);
0249     unit=tmp.unit;
0250 else
0251     fz=0;
0252 end
0253 
0254 fprintf(fid,['\\usepackage[margin=%gcm,paperwidth=%gcm,' ...
0255     'paperheight=%gcm]{geometry}\n'], ...
0256     margin, ...
0257     max(layout.rowwidth) + 2*margin, ...
0258     sum(layout.rowheight) + 2*margin + 2*fz ...
0259     + options.vspace*(length(layout.rowheight)));
0260 
0261 fprintf(fid,'\\pagestyle{empty}\n');
0262 fprintf(fid,'\\usepackage{graphicx}\n');
0263 fprintf(fid,'\\usepackage{anyfontsize}\n');
0264 fprintf(fid,'\\setlength{\\parindent}{0in}\n');
0265 fprintf(fid,'\\begin{document}\n');
0266 fprintf(fid,'\\centering\n');
0267 if ~isempty(options.title)
0268     if ~any(ismember(unit,{'cm','mm','pt'})), error(['atmlab:' mfilename],'Don''t know fontsize unit "%s"',unit),end
0269     fprintf(fid,'\\begin{minipage}[%s]{%gcm}\n', options.valign, max(layout.rowwidth));
0270     fprintf(fid,'\\begin{center}\n');
0271     fprintf(fid,'{\\fontsize{%g%s}{%g%s}\\selectfont \\sffamily %s}\n',...
0272         fz,unit,1.2*fz,unit,strrep(options.title,'_','\_'));
0273     fprintf(fid,'\\end{center}\n');
0274     fprintf(fid,'\\end{minipage}\n');
0275     fprintf(fid, '\\vspace{%gcm}\\\\\n', options.vspace);
0276 end
0277 
0278 row = 1; filenum = 1;
0279 for R = layout.plotsbyrow
0280     col=1;
0281     for P = R{:}
0282         fprintf(fid,'\\begin{minipage}[%s]{%gcm}\n', options.valign, P.width);
0283         tmpplotname=sprintf('plot%d.pdf', filenum);
0284         mvplotcmd{filenum}=sprintf('cp ''%s'' ''%s/%s''', P.file, tmpfolder, tmpplotname); %#ok
0285         if isfield(options,'scale') && ~isempty(options.scale(options.scale(:,1)==filenum,2))
0286             strscale = num2str(options.scale(options.scale(:,1)==filenum,2));
0287         else
0288             strscale = '1';
0289         end
0290         fprintf(fid,'\\includegraphics[scale=%s]{%s}\n', strscale,tmpplotname);
0291         fprintf(fid,'\\end{minipage}\n');
0292         if (col ~= length(R{:}))
0293             fprintf(fid,'\\hspace{%gcm}\n', options.hspace);
0294         elseif isfield(options,'rightPadding')
0295             fprintf(fid,'\\hspace{%gcm}\n', options.rightPadding(row));
0296         end
0297         col = col+1;
0298         filenum=filenum+1;
0299     end
0300     if (row ~= length(layout.rowheight))
0301         fprintf(fid, '\\vspace{%gcm}\\\\\n', options.vspace);
0302     end
0303     row = row+1;
0304 end
0305 fprintf(fid,'\\end{document}\n');
0306 
0307 %% Hand the TeX file over to pdflatex
0308 exec_system_cmd(mvplotcmd, gmtlab('VERBOSITY'));
0309 exec_system_cmd(sprintf('cd %s && pdflatex -interaction nonstopmode out.tex', ...
0310     tmpfolder), ...
0311     gmtlab('VERBOSITY'));
0312 
0313 file = fullfile(options.outdir, options.filename); 
0314 exec_system_cmd(sprintf('cd %s && mv -f out.pdf ''%s''', ...
0315     tmpfolder, file), ...
0316     gmtlab('VERBOSITY'));
0317 
0318 if options.pdfcrop
0319     [message,test]=exec_system_cmd(sprintf('pdfcrop %s',file),gmtlab('VERBOSITY'));
0320     if logical(test)
0321         error(['atmlab' mfilename],'There was a problem with pdfcrop: %s',message{1})
0322     end
0323     exec_system_cmd(sprintf('mv %s-crop.pdf %s',file(1:end-4),file),gmtlab('VERBOSITY'));
0324 end
0325 
0326 end

Generated on Mon 15-Sep-2014 13:31:28 by m2html © 2005