| 1 | %EXPORT_FIG Exports figures suitable for publication
|
|---|
| 2 | %
|
|---|
| 3 | % Examples:
|
|---|
| 4 | % im = export_fig
|
|---|
| 5 | % [im alpha] = export_fig
|
|---|
| 6 | % export_fig filename
|
|---|
| 7 | % export_fig filename -format1 -format2
|
|---|
| 8 | % export_fig ... -nocrop
|
|---|
| 9 | % export_fig ... -transparent
|
|---|
| 10 | % export_fig ... -native
|
|---|
| 11 | % export_fig ... -m<val>
|
|---|
| 12 | % export_fig ... -r<val>
|
|---|
| 13 | % export_fig ... -a<val>
|
|---|
| 14 | % export_fig ... -q<val>
|
|---|
| 15 | % export_fig ... -p<val>
|
|---|
| 16 | % export_fig ... -<renderer>
|
|---|
| 17 | % export_fig ... -<colorspace>
|
|---|
| 18 | % export_fig ... -append
|
|---|
| 19 | % export_fig ... -bookmark
|
|---|
| 20 | % export_fig(..., handle)
|
|---|
| 21 | %
|
|---|
| 22 | % This function saves a figure or single axes to one or more vector and/or
|
|---|
| 23 | % bitmap file formats, and/or outputs a rasterized version to the
|
|---|
| 24 | % workspace, with the following properties:
|
|---|
| 25 | % - Figure/axes reproduced as it appears on screen
|
|---|
| 26 | % - Cropped borders (optional)
|
|---|
| 27 | % - Embedded fonts (vector formats)
|
|---|
| 28 | % - Improved line and grid line styles
|
|---|
| 29 | % - Anti-aliased graphics (bitmap formats)
|
|---|
| 30 | % - Render images at native resolution (optional for bitmap formats)
|
|---|
| 31 | % - Transparent background supported (pdf, eps, png)
|
|---|
| 32 | % - Semi-transparent patch objects supported (png only)
|
|---|
| 33 | % - RGB, CMYK or grayscale output (CMYK only with pdf, eps, tiff)
|
|---|
| 34 | % - Variable image compression, including lossless (pdf, eps, jpg)
|
|---|
| 35 | % - Optionally append to file (pdf, tiff)
|
|---|
| 36 | % - Vector formats: pdf, eps
|
|---|
| 37 | % - Bitmap formats: png, tiff, jpg, bmp, export to workspace
|
|---|
| 38 | %
|
|---|
| 39 | % This function is especially suited to exporting figures for use in
|
|---|
| 40 | % publications and presentations, because of the high quality and
|
|---|
| 41 | % portability of media produced.
|
|---|
| 42 | %
|
|---|
| 43 | % Note that the background color and figure dimensions are reproduced
|
|---|
| 44 | % (the latter approximately, and ignoring cropping & magnification) in the
|
|---|
| 45 | % output file. For transparent background (and semi-transparent patch
|
|---|
| 46 | % objects), use the -transparent option or set the figure 'Color' property
|
|---|
| 47 | % to 'none'. To make axes transparent set the axes 'Color' property to
|
|---|
| 48 | % 'none'. Pdf, eps and png are the only file formats to support a
|
|---|
| 49 | % transparent background, whilst the png format alone supports transparency
|
|---|
| 50 | % of patch objects.
|
|---|
| 51 | %
|
|---|
| 52 | % The choice of renderer (opengl, zbuffer or painters) has a large impact
|
|---|
| 53 | % on the quality of output. Whilst the default value (opengl for bitmaps,
|
|---|
| 54 | % painters for vector formats) generally gives good results, if you aren't
|
|---|
| 55 | % satisfied then try another renderer. Notes: 1) For vector formats (eps,
|
|---|
| 56 | % pdf), only painters generates vector graphics. 2) For bitmaps, only
|
|---|
| 57 | % opengl can render transparent patch objects correctly. 3) For bitmaps,
|
|---|
| 58 | % only painters will correctly scale line dash and dot lengths when
|
|---|
| 59 | % magnifying or anti-aliasing. 4) Fonts may be substitued with Courier when
|
|---|
| 60 | % using painters.
|
|---|
| 61 | %
|
|---|
| 62 | % When exporting to vector format (pdf & eps) and bitmap format using the
|
|---|
| 63 | % painters renderer, this function requires that ghostscript is installed
|
|---|
| 64 | % on your system. You can download this from:
|
|---|
| 65 | % http://www.ghostscript.com
|
|---|
| 66 | % When exporting to eps it additionally requires pdftops, from the Xpdf
|
|---|
| 67 | % suite of functions. You can download this from:
|
|---|
| 68 | % http://www.foolabs.com/xpdf
|
|---|
| 69 | %
|
|---|
| 70 | %IN:
|
|---|
| 71 | % filename - string containing the name (optionally including full or
|
|---|
| 72 | % relative path) of the file the figure is to be saved as. If
|
|---|
| 73 | % a path is not specified, the figure is saved in the current
|
|---|
| 74 | % directory. If no name and no output arguments are specified,
|
|---|
| 75 | % the default name, 'export_fig_out', is used. If neither a
|
|---|
| 76 | % file extension nor a format are specified, a ".png" is added
|
|---|
| 77 | % and the figure saved in that format.
|
|---|
| 78 | % -format1, -format2, etc. - strings containing the extensions of the
|
|---|
| 79 | % file formats the figure is to be saved as.
|
|---|
| 80 | % Valid options are: '-pdf', '-eps', '-png',
|
|---|
| 81 | % '-tif', '-jpg' and '-bmp'. All combinations
|
|---|
| 82 | % of formats are valid.
|
|---|
| 83 | % -nocrop - option indicating that the borders of the output are not to
|
|---|
| 84 | % be cropped.
|
|---|
| 85 | % -transparent - option indicating that the figure background is to be
|
|---|
| 86 | % made transparent (png, pdf and eps output only).
|
|---|
| 87 | % -m<val> - option where val indicates the factor to magnify the
|
|---|
| 88 | % on-screen figure pixel dimensions by when generating bitmap
|
|---|
| 89 | % outputs. Default: '-m1'.
|
|---|
| 90 | % -r<val> - option val indicates the resolution (in pixels per inch) to
|
|---|
| 91 | % export bitmap and vector outputs at, keeping the dimensions
|
|---|
| 92 | % of the on-screen figure. Default: '-r864' (for vector output
|
|---|
| 93 | % only). Note that the -m option overides the -r option for
|
|---|
| 94 | % bitmap outputs only.
|
|---|
| 95 | % -native - option indicating that the output resolution (when outputting
|
|---|
| 96 | % a bitmap format) should be such that the vertical resolution
|
|---|
| 97 | % of the first suitable image found in the figure is at the
|
|---|
| 98 | % native resolution of that image. To specify a particular
|
|---|
| 99 | % image to use, give it the tag 'export_fig_native'. Notes:
|
|---|
| 100 | % This overrides any value set with the -m and -r options. It
|
|---|
| 101 | % also assumes that the image is displayed front-to-parallel
|
|---|
| 102 | % with the screen. The output resolution is approximate and
|
|---|
| 103 | % should not be relied upon. Anti-aliasing can have adverse
|
|---|
| 104 | % effects on image quality (disable with the -a1 option).
|
|---|
| 105 | % -a1, -a2, -a3, -a4 - option indicating the amount of anti-aliasing to
|
|---|
| 106 | % use for bitmap outputs. '-a1' means no anti-
|
|---|
| 107 | % aliasing; '-a4' is the maximum amount (default).
|
|---|
| 108 | % -<renderer> - option to force a particular renderer (painters, opengl
|
|---|
| 109 | % or zbuffer) to be used over the default: opengl for
|
|---|
| 110 | % bitmaps; painters for vector formats.
|
|---|
| 111 | % -<colorspace> - option indicating which colorspace color figures should
|
|---|
| 112 | % be saved in: RGB (default), CMYK or gray. CMYK is only
|
|---|
| 113 | % supported in pdf, eps and tiff output.
|
|---|
| 114 | % -q<val> - option to vary bitmap image quality (in pdf, eps and jpg
|
|---|
| 115 | % files only). Larger val, in the range 0-100, gives higher
|
|---|
| 116 | % quality/lower compression. val > 100 gives lossless
|
|---|
| 117 | % compression. Default: '-q95' for jpg, ghostscript prepress
|
|---|
| 118 | % default for pdf & eps. Note: lossless compression can
|
|---|
| 119 | % sometimes give a smaller file size than the default lossy
|
|---|
| 120 | % compression, depending on the type of images.
|
|---|
| 121 | % -p<val> - option to add a border of width val to eps and pdf files,
|
|---|
| 122 | % where val is in units of the intermediate eps file. Default:
|
|---|
| 123 | % 0 (i.e. no padding).
|
|---|
| 124 | % -append - option indicating that if the file (pdfs only) already
|
|---|
| 125 | % exists, the figure is to be appended as a new page, instead
|
|---|
| 126 | % of being overwritten (default).
|
|---|
| 127 | % -bookmark - option to indicate that a bookmark with the name of the
|
|---|
| 128 | % figure is to be created in the output file (pdf only).
|
|---|
| 129 | % handle - The handle of the figure, axes or uipanels (can be an array of
|
|---|
| 130 | % handles, but the objects must be in the same figure) to be
|
|---|
| 131 | % saved. Default: gcf.
|
|---|
| 132 | %
|
|---|
| 133 | %OUT:
|
|---|
| 134 | % im - MxNxC uint8 image array of the figure.
|
|---|
| 135 | % alpha - MxN single array of alphamatte values in range [0,1], for the
|
|---|
| 136 | % case when the background is transparent.
|
|---|
| 137 | %
|
|---|
| 138 | % Some helpful examples and tips can be found at:
|
|---|
| 139 | % https://github.com/ojwoodford/export_fig
|
|---|
| 140 | %
|
|---|
| 141 | % See also PRINT, SAVEAS.
|
|---|
| 142 |
|
|---|
| 143 | % Copyright (C) Oliver Woodford 2008-2014
|
|---|
| 144 |
|
|---|
| 145 | % The idea of using ghostscript is inspired by Peder Axensten's SAVEFIG
|
|---|
| 146 | % (fex id: 10889) which is itself inspired by EPS2PDF (fex id: 5782).
|
|---|
| 147 | % The idea for using pdftops came from the MATLAB newsgroup (id: 168171).
|
|---|
| 148 | % The idea of editing the EPS file to change line styles comes from Jiro
|
|---|
| 149 | % Doke's FIXPSLINESTYLE (fex id: 17928).
|
|---|
| 150 | % The idea of changing dash length with line width came from comments on
|
|---|
| 151 | % fex id: 5743, but the implementation is mine :)
|
|---|
| 152 | % The idea of anti-aliasing bitmaps came from Anders Brun's MYAA (fex id:
|
|---|
| 153 | % 20979).
|
|---|
| 154 | % The idea of appending figures in pdfs came from Matt C in comments on the
|
|---|
| 155 | % FEX (id: 23629)
|
|---|
| 156 |
|
|---|
| 157 | % Thanks to Roland Martin for pointing out the colour MATLAB
|
|---|
| 158 | % bug/feature with colorbar axes and transparent backgrounds.
|
|---|
| 159 | % Thanks also to Andrew Matthews for describing a bug to do with the figure
|
|---|
| 160 | % size changing in -nodisplay mode. I couldn't reproduce it, but included a
|
|---|
| 161 | % fix anyway.
|
|---|
| 162 | % Thanks to Tammy Threadgill for reporting a bug where an axes is not
|
|---|
| 163 | % isolated from gui objects.
|
|---|
| 164 |
|
|---|
| 165 | % 23/02/12: Ensure that axes limits don't change during printing
|
|---|
| 166 | % 14/03/12: Fix bug in fixing the axes limits (thanks to Tobias Lamour for
|
|---|
| 167 | % reporting it).
|
|---|
| 168 | % 02/05/12: Incorporate patch of Petr Nechaev (many thanks), enabling
|
|---|
| 169 | % bookmarking of figures in pdf files.
|
|---|
| 170 | % 09/05/12: Incorporate patch of Arcelia Arrieta (many thanks), to keep
|
|---|
| 171 | % tick marks fixed.
|
|---|
| 172 | % 12/12/12: Add support for isolating uipanels. Thanks to michael for
|
|---|
| 173 | % suggesting it.
|
|---|
| 174 | % 25/09/13: Add support for changing resolution in vector formats. Thanks
|
|---|
| 175 | % to Jan Jaap Meijer for suggesting it.
|
|---|
| 176 | % 07/05/14: Add support for '~' at start of path. Thanks to Sally Warner
|
|---|
| 177 | % for suggesting it.
|
|---|
| 178 |
|
|---|
| 179 | function [im, alpha] = export_fig(varargin)
|
|---|
| 180 | % Make sure the figure is rendered correctly _now_ so that properties like
|
|---|
| 181 | % axes limits are up-to-date.
|
|---|
| 182 | drawnow;
|
|---|
| 183 | % Parse the input arguments
|
|---|
| 184 | [fig, options] = parse_args(nargout, varargin{:});
|
|---|
| 185 | % Isolate the subplot, if it is one
|
|---|
| 186 | cls = all(ismember(get(fig, 'Type'), {'axes', 'uipanel'}));
|
|---|
| 187 | if cls
|
|---|
| 188 | % Given handles of one or more axes, so isolate them from the rest
|
|---|
| 189 | fig = isolate_axes(fig);
|
|---|
| 190 | else
|
|---|
| 191 | % Check we have a figure
|
|---|
| 192 | if ~isequal(get(fig, 'Type'), 'figure');
|
|---|
| 193 | error('Handle must be that of a figure, axes or uipanel');
|
|---|
| 194 | end
|
|---|
| 195 | % Get the old InvertHardcopy mode
|
|---|
| 196 | old_mode = get(fig, 'InvertHardcopy');
|
|---|
| 197 | end
|
|---|
| 198 | % Hack the font units where necessary (due to a font rendering bug in
|
|---|
| 199 | % print?). This may not work perfectly in all cases. Also it can change the
|
|---|
| 200 | % figure layout if reverted, so use a copy.
|
|---|
| 201 | magnify = options.magnify * options.aa_factor;
|
|---|
| 202 | if isbitmap(options) && magnify ~= 1
|
|---|
| 203 | fontu = findobj(fig, 'FontUnits', 'normalized');
|
|---|
| 204 | if ~isempty(fontu)
|
|---|
| 205 | % Some normalized font units found
|
|---|
| 206 | if ~cls
|
|---|
| 207 | fig = copyfig(fig);
|
|---|
| 208 | set(fig, 'Visible', 'off');
|
|---|
| 209 | fontu = findobj(fig, 'FontUnits', 'normalized');
|
|---|
| 210 | cls = true;
|
|---|
| 211 | end
|
|---|
| 212 | set(fontu, 'FontUnits', 'points');
|
|---|
| 213 | end
|
|---|
| 214 | end
|
|---|
| 215 | % MATLAB "feature": axes limits and tick marks can change when printing
|
|---|
| 216 | Hlims = findall(fig, 'Type', 'axes');
|
|---|
| 217 | if ~cls
|
|---|
| 218 | % Record the old axes limit and tick modes
|
|---|
| 219 | Xlims = make_cell(get(Hlims, 'XLimMode'));
|
|---|
| 220 | Ylims = make_cell(get(Hlims, 'YLimMode'));
|
|---|
| 221 | Zlims = make_cell(get(Hlims, 'ZLimMode'));
|
|---|
| 222 | Xtick = make_cell(get(Hlims, 'XTickMode'));
|
|---|
| 223 | Ytick = make_cell(get(Hlims, 'YTickMode'));
|
|---|
| 224 | Ztick = make_cell(get(Hlims, 'ZTickMode'));
|
|---|
| 225 | end
|
|---|
| 226 | % Set all axes limit and tick modes to manual, so the limits and ticks can't change
|
|---|
| 227 | set(Hlims, 'XLimMode', 'manual', 'YLimMode', 'manual', 'ZLimMode', 'manual');
|
|---|
| 228 | set_tick_mode(Hlims, 'X');
|
|---|
| 229 | set_tick_mode(Hlims, 'Y');
|
|---|
| 230 | set_tick_mode(Hlims, 'Z');
|
|---|
| 231 | % Set to print exactly what is there
|
|---|
| 232 | set(fig, 'InvertHardcopy', 'off');
|
|---|
| 233 | % Set the renderer
|
|---|
| 234 | switch options.renderer
|
|---|
| 235 | case 1
|
|---|
| 236 | renderer = '-opengl';
|
|---|
| 237 | case 2
|
|---|
| 238 | renderer = '-zbuffer';
|
|---|
| 239 | case 3
|
|---|
| 240 | renderer = '-painters';
|
|---|
| 241 | otherwise
|
|---|
| 242 | renderer = '-opengl'; % Default for bitmaps
|
|---|
| 243 | end
|
|---|
| 244 | % Do the bitmap formats first
|
|---|
| 245 | if isbitmap(options)
|
|---|
| 246 | % Get the background colour
|
|---|
| 247 | if options.transparent && (options.png || options.alpha)
|
|---|
| 248 | % Get out an alpha channel
|
|---|
| 249 | % MATLAB "feature": black colorbar axes can change to white and vice versa!
|
|---|
| 250 | hCB = findobj(fig, 'Type', 'axes', 'Tag', 'Colorbar');
|
|---|
| 251 | if isempty(hCB)
|
|---|
| 252 | yCol = [];
|
|---|
| 253 | xCol = [];
|
|---|
| 254 | else
|
|---|
| 255 | yCol = get(hCB, 'YColor');
|
|---|
| 256 | xCol = get(hCB, 'XColor');
|
|---|
| 257 | if iscell(yCol)
|
|---|
| 258 | yCol = cell2mat(yCol);
|
|---|
| 259 | xCol = cell2mat(xCol);
|
|---|
| 260 | end
|
|---|
| 261 | yCol = sum(yCol, 2);
|
|---|
| 262 | xCol = sum(xCol, 2);
|
|---|
| 263 | end
|
|---|
| 264 | % MATLAB "feature": apparently figure size can change when changing
|
|---|
| 265 | % colour in -nodisplay mode
|
|---|
| 266 | pos = get(fig, 'Position');
|
|---|
| 267 | % Set the background colour to black, and set size in case it was
|
|---|
| 268 | % changed internally
|
|---|
| 269 | tcol = get(fig, 'Color');
|
|---|
| 270 | set(fig, 'Color', 'k', 'Position', pos);
|
|---|
| 271 | % Correct the colorbar axes colours
|
|---|
| 272 | set(hCB(yCol==0), 'YColor', [0 0 0]);
|
|---|
| 273 | set(hCB(xCol==0), 'XColor', [0 0 0]);
|
|---|
| 274 | % Print large version to array
|
|---|
| 275 | B = print2array(fig, magnify, renderer);
|
|---|
| 276 | % Downscale the image
|
|---|
| 277 | B = downsize(single(B), options.aa_factor);
|
|---|
| 278 | % Set background to white (and set size)
|
|---|
| 279 | set(fig, 'Color', 'w', 'Position', pos);
|
|---|
| 280 | % Correct the colorbar axes colours
|
|---|
| 281 | set(hCB(yCol==3), 'YColor', [1 1 1]);
|
|---|
| 282 | set(hCB(xCol==3), 'XColor', [1 1 1]);
|
|---|
| 283 | % Print large version to array
|
|---|
| 284 | A = print2array(fig, magnify, renderer);
|
|---|
| 285 | % Downscale the image
|
|---|
| 286 | A = downsize(single(A), options.aa_factor);
|
|---|
| 287 | % Set the background colour (and size) back to normal
|
|---|
| 288 | set(fig, 'Color', tcol, 'Position', pos);
|
|---|
| 289 | % Compute the alpha map
|
|---|
| 290 | alpha = round(sum(B - A, 3)) / (255 * 3) + 1;
|
|---|
| 291 | A = alpha;
|
|---|
| 292 | A(A==0) = 1;
|
|---|
| 293 | A = B ./ A(:,:,[1 1 1]);
|
|---|
| 294 | clear B
|
|---|
| 295 | % Convert to greyscale
|
|---|
| 296 | if options.colourspace == 2
|
|---|
| 297 | A = rgb2grey(A);
|
|---|
| 298 | end
|
|---|
| 299 | A = uint8(A);
|
|---|
| 300 | % Crop the background
|
|---|
| 301 | if options.crop
|
|---|
| 302 | [alpha, v] = crop_borders(alpha, 0, 1);
|
|---|
| 303 | A = A(v(1):v(2),v(3):v(4),:);
|
|---|
| 304 | end
|
|---|
| 305 | if options.png
|
|---|
| 306 | % Compute the resolution
|
|---|
| 307 | res = options.magnify * get(0, 'ScreenPixelsPerInch') / 25.4e-3;
|
|---|
| 308 | % Save the png
|
|---|
| 309 | imwrite(A, [options.name '.png'], 'Alpha', double(alpha), 'ResolutionUnit', 'meter', 'XResolution', res, 'YResolution', res);
|
|---|
| 310 | % Clear the png bit
|
|---|
| 311 | options.png = false;
|
|---|
| 312 | end
|
|---|
| 313 | % Return only one channel for greyscale
|
|---|
| 314 | if isbitmap(options)
|
|---|
| 315 | A = check_greyscale(A);
|
|---|
| 316 | end
|
|---|
| 317 | if options.alpha
|
|---|
| 318 | % Store the image
|
|---|
| 319 | im = A;
|
|---|
| 320 | % Clear the alpha bit
|
|---|
| 321 | options.alpha = false;
|
|---|
| 322 | end
|
|---|
| 323 | % Get the non-alpha image
|
|---|
| 324 | if isbitmap(options)
|
|---|
| 325 | alph = alpha(:,:,ones(1, size(A, 3)));
|
|---|
| 326 | A = uint8(single(A) .* alph + 255 * (1 - alph));
|
|---|
| 327 | clear alph
|
|---|
| 328 | end
|
|---|
| 329 | if options.im
|
|---|
| 330 | % Store the new image
|
|---|
| 331 | im = A;
|
|---|
| 332 | end
|
|---|
| 333 | else
|
|---|
| 334 | % Print large version to array
|
|---|
| 335 | if options.transparent
|
|---|
| 336 | % MATLAB "feature": apparently figure size can change when changing
|
|---|
| 337 | % colour in -nodisplay mode
|
|---|
| 338 | pos = get(fig, 'Position');
|
|---|
| 339 | tcol = get(fig, 'Color');
|
|---|
| 340 | set(fig, 'Color', 'w', 'Position', pos);
|
|---|
| 341 | A = print2array(fig, magnify, renderer);
|
|---|
| 342 | set(fig, 'Color', tcol, 'Position', pos);
|
|---|
| 343 | tcol = 255;
|
|---|
| 344 | else
|
|---|
| 345 | [A, tcol] = print2array(fig, magnify, renderer);
|
|---|
| 346 | end
|
|---|
| 347 | % Crop the background
|
|---|
| 348 | if options.crop
|
|---|
| 349 | A = crop_borders(A, tcol, 1);
|
|---|
| 350 | end
|
|---|
| 351 | % Downscale the image
|
|---|
| 352 | A = downsize(A, options.aa_factor);
|
|---|
| 353 | if options.colourspace == 2
|
|---|
| 354 | % Convert to greyscale
|
|---|
| 355 | A = rgb2grey(A);
|
|---|
| 356 | else
|
|---|
| 357 | % Return only one channel for greyscale
|
|---|
| 358 | A = check_greyscale(A);
|
|---|
| 359 | end
|
|---|
| 360 | % Outputs
|
|---|
| 361 | if options.im
|
|---|
| 362 | im = A;
|
|---|
| 363 | end
|
|---|
| 364 | if options.alpha
|
|---|
| 365 | im = A;
|
|---|
| 366 | alpha = zeros(size(A, 1), size(A, 2), 'single');
|
|---|
| 367 | end
|
|---|
| 368 | end
|
|---|
| 369 | % Save the images
|
|---|
| 370 | if options.png
|
|---|
| 371 | res = options.magnify * get(0, 'ScreenPixelsPerInch') / 25.4e-3;
|
|---|
| 372 | imwrite(A, [options.name '.png'], 'ResolutionUnit', 'meter', 'XResolution', res, 'YResolution', res);
|
|---|
| 373 | end
|
|---|
| 374 | if options.bmp
|
|---|
| 375 | imwrite(A, [options.name '.bmp']);
|
|---|
| 376 | end
|
|---|
| 377 | % Save jpeg with given quality
|
|---|
| 378 | if options.jpg
|
|---|
| 379 | quality = options.quality;
|
|---|
| 380 | if isempty(quality)
|
|---|
| 381 | quality = 95;
|
|---|
| 382 | end
|
|---|
| 383 | if quality > 100
|
|---|
| 384 | imwrite(A, [options.name '.jpg'], 'Mode', 'lossless');
|
|---|
| 385 | else
|
|---|
| 386 | imwrite(A, [options.name '.jpg'], 'Quality', quality);
|
|---|
| 387 | end
|
|---|
| 388 | end
|
|---|
| 389 | % Save tif images in cmyk if wanted (and possible)
|
|---|
| 390 | if options.tif
|
|---|
| 391 | if options.colourspace == 1 && size(A, 3) == 3
|
|---|
| 392 | A = double(255 - A);
|
|---|
| 393 | K = min(A, [], 3);
|
|---|
| 394 | K_ = 255 ./ max(255 - K, 1);
|
|---|
| 395 | C = (A(:,:,1) - K) .* K_;
|
|---|
| 396 | M = (A(:,:,2) - K) .* K_;
|
|---|
| 397 | Y = (A(:,:,3) - K) .* K_;
|
|---|
| 398 | A = uint8(cat(3, C, M, Y, K));
|
|---|
| 399 | clear C M Y K K_
|
|---|
| 400 | end
|
|---|
| 401 | append_mode = {'overwrite', 'append'};
|
|---|
| 402 | imwrite(A, [options.name '.tif'], 'Resolution', options.magnify*get(0, 'ScreenPixelsPerInch'), 'WriteMode', append_mode{options.append+1});
|
|---|
| 403 | end
|
|---|
| 404 | end
|
|---|
| 405 | % Now do the vector formats
|
|---|
| 406 | if isvector(options)
|
|---|
| 407 | % Set the default renderer to painters
|
|---|
| 408 | if ~options.renderer
|
|---|
| 409 | renderer = '-painters';
|
|---|
| 410 | end
|
|---|
| 411 | % Generate some filenames
|
|---|
| 412 | tmp_nam = [tempname '.eps'];
|
|---|
| 413 | if options.pdf
|
|---|
| 414 | pdf_nam = [options.name '.pdf'];
|
|---|
| 415 | else
|
|---|
| 416 | pdf_nam = [tempname '.pdf'];
|
|---|
| 417 | end
|
|---|
| 418 | % Generate the options for print
|
|---|
| 419 | p2eArgs = {renderer, sprintf('-r%d', options.resolution)};
|
|---|
| 420 | if options.colourspace == 1
|
|---|
| 421 | p2eArgs = [p2eArgs {'-cmyk'}];
|
|---|
| 422 | end
|
|---|
| 423 | if ~options.crop
|
|---|
| 424 | p2eArgs = [p2eArgs {'-loose'}];
|
|---|
| 425 | end
|
|---|
| 426 | try
|
|---|
| 427 | % Generate an eps
|
|---|
| 428 | print2eps(tmp_nam, fig, options.bb_padding, p2eArgs{:});
|
|---|
| 429 | % Remove the background, if desired
|
|---|
| 430 | if options.transparent && ~isequal(get(fig, 'Color'), 'none')
|
|---|
| 431 | eps_remove_background(tmp_nam, 1 + using_hg2(fig));
|
|---|
| 432 | end
|
|---|
| 433 | % Add a bookmark to the PDF if desired
|
|---|
| 434 | if options.bookmark
|
|---|
| 435 | fig_nam = get(fig, 'Name');
|
|---|
| 436 | if isempty(fig_nam)
|
|---|
| 437 | warning('export_fig:EmptyBookmark', 'Bookmark requested for figure with no name. Bookmark will be empty.');
|
|---|
| 438 | end
|
|---|
| 439 | add_bookmark(tmp_nam, fig_nam);
|
|---|
| 440 | end
|
|---|
| 441 | % Generate a pdf
|
|---|
| 442 | eps2pdf(tmp_nam, pdf_nam, 1, options.append, options.colourspace==2, options.quality);
|
|---|
| 443 | catch ex
|
|---|
| 444 | % Delete the eps
|
|---|
| 445 | delete(tmp_nam);
|
|---|
| 446 | rethrow(ex);
|
|---|
| 447 | end
|
|---|
| 448 | % Delete the eps
|
|---|
| 449 | delete(tmp_nam);
|
|---|
| 450 | if options.eps
|
|---|
| 451 | try
|
|---|
| 452 | % Generate an eps from the pdf
|
|---|
| 453 | pdf2eps(pdf_nam, [options.name '.eps']);
|
|---|
| 454 | catch ex
|
|---|
| 455 | if ~options.pdf
|
|---|
| 456 | % Delete the pdf
|
|---|
| 457 | delete(pdf_nam);
|
|---|
| 458 | end
|
|---|
| 459 | rethrow(ex);
|
|---|
| 460 | end
|
|---|
| 461 | if ~options.pdf
|
|---|
| 462 | % Delete the pdf
|
|---|
| 463 | delete(pdf_nam);
|
|---|
| 464 | end
|
|---|
| 465 | end
|
|---|
| 466 | end
|
|---|
| 467 | if cls
|
|---|
| 468 | % Close the created figure
|
|---|
| 469 | close(fig);
|
|---|
| 470 | else
|
|---|
| 471 | % Reset the hardcopy mode
|
|---|
| 472 | set(fig, 'InvertHardcopy', old_mode);
|
|---|
| 473 | % Reset the axes limit and tick modes
|
|---|
| 474 | for a = 1:numel(Hlims)
|
|---|
| 475 | set(Hlims(a), 'XLimMode', Xlims{a}, 'YLimMode', Ylims{a}, 'ZLimMode', Zlims{a}, 'XTickMode', Xtick{a}, 'YTickMode', Ytick{a}, 'ZTickMode', Ztick{a});
|
|---|
| 476 | end
|
|---|
| 477 | end
|
|---|
| 478 | end
|
|---|
| 479 |
|
|---|
| 480 | function [fig, options] = parse_args(nout, varargin)
|
|---|
| 481 | % Parse the input arguments
|
|---|
| 482 | % Set the defaults
|
|---|
| 483 | fig = get(0, 'CurrentFigure');
|
|---|
| 484 | options = struct('name', 'export_fig_out', ...
|
|---|
| 485 | 'crop', true, ...
|
|---|
| 486 | 'transparent', false, ...
|
|---|
| 487 | 'renderer', 0, ... % 0: default, 1: OpenGL, 2: ZBuffer, 3: Painters
|
|---|
| 488 | 'pdf', false, ...
|
|---|
| 489 | 'eps', false, ...
|
|---|
| 490 | 'png', false, ...
|
|---|
| 491 | 'tif', false, ...
|
|---|
| 492 | 'jpg', false, ...
|
|---|
| 493 | 'bmp', false, ...
|
|---|
| 494 | 'colourspace', 0, ... % 0: RGB/gray, 1: CMYK, 2: gray
|
|---|
| 495 | 'append', false, ...
|
|---|
| 496 | 'im', nout == 1, ...
|
|---|
| 497 | 'alpha', nout == 2, ...
|
|---|
| 498 | 'aa_factor', 0, ...
|
|---|
| 499 | 'bb_padding', 0, ...
|
|---|
| 500 | 'magnify', [], ...
|
|---|
| 501 | 'resolution', [], ...
|
|---|
| 502 | 'bookmark', false, ...
|
|---|
| 503 | 'quality', []);
|
|---|
| 504 | native = false; % Set resolution to native of an image
|
|---|
| 505 |
|
|---|
| 506 | % Go through the other arguments
|
|---|
| 507 | for a = 1:nargin-1
|
|---|
| 508 | if all(ishandle(varargin{a}))
|
|---|
| 509 | fig = varargin{a};
|
|---|
| 510 | elseif ischar(varargin{a}) && ~isempty(varargin{a})
|
|---|
| 511 | if varargin{a}(1) == '-'
|
|---|
| 512 | switch lower(varargin{a}(2:end))
|
|---|
| 513 | case 'nocrop'
|
|---|
| 514 | options.crop = false;
|
|---|
| 515 | case {'trans', 'transparent'}
|
|---|
| 516 | options.transparent = true;
|
|---|
| 517 | case 'opengl'
|
|---|
| 518 | options.renderer = 1;
|
|---|
| 519 | case 'zbuffer'
|
|---|
| 520 | options.renderer = 2;
|
|---|
| 521 | case 'painters'
|
|---|
| 522 | options.renderer = 3;
|
|---|
| 523 | case 'pdf'
|
|---|
| 524 | options.pdf = true;
|
|---|
| 525 | case 'eps'
|
|---|
| 526 | options.eps = true;
|
|---|
| 527 | case 'png'
|
|---|
| 528 | options.png = true;
|
|---|
| 529 | case {'tif', 'tiff'}
|
|---|
| 530 | options.tif = true;
|
|---|
| 531 | case {'jpg', 'jpeg'}
|
|---|
| 532 | options.jpg = true;
|
|---|
| 533 | case 'bmp'
|
|---|
| 534 | options.bmp = true;
|
|---|
| 535 | case 'rgb'
|
|---|
| 536 | options.colourspace = 0;
|
|---|
| 537 | case 'cmyk'
|
|---|
| 538 | options.colourspace = 1;
|
|---|
| 539 | case {'gray', 'grey'}
|
|---|
| 540 | options.colourspace = 2;
|
|---|
| 541 | case {'a1', 'a2', 'a3', 'a4'}
|
|---|
| 542 | options.aa_factor = str2double(varargin{a}(3));
|
|---|
| 543 | case 'append'
|
|---|
| 544 | options.append = true;
|
|---|
| 545 | case 'bookmark'
|
|---|
| 546 | options.bookmark = true;
|
|---|
| 547 | case 'native'
|
|---|
| 548 | native = true;
|
|---|
| 549 | otherwise
|
|---|
| 550 | val = str2double(regexp(varargin{a}, '(?<=-(m|M|r|R|q|Q|p|P))-?\d*.?\d+', 'match'));
|
|---|
| 551 | if ~isscalar(val)
|
|---|
| 552 | error('option %s not recognised', varargin{a});
|
|---|
| 553 | end
|
|---|
| 554 | switch lower(varargin{a}(2))
|
|---|
| 555 | case 'm'
|
|---|
| 556 | options.magnify = val;
|
|---|
| 557 | case 'r'
|
|---|
| 558 | options.resolution = val;
|
|---|
| 559 | case 'q'
|
|---|
| 560 | options.quality = max(val, 0);
|
|---|
| 561 | case 'p'
|
|---|
| 562 | options.bb_padding = val;
|
|---|
| 563 | end
|
|---|
| 564 | end
|
|---|
| 565 | else
|
|---|
| 566 | [p, options.name, ext] = fileparts(varargin{a});
|
|---|
| 567 | if ~isempty(p)
|
|---|
| 568 | options.name = [p filesep options.name];
|
|---|
| 569 | end
|
|---|
| 570 | switch lower(ext)
|
|---|
| 571 | case {'.tif', '.tiff'}
|
|---|
| 572 | options.tif = true;
|
|---|
| 573 | case {'.jpg', '.jpeg'}
|
|---|
| 574 | options.jpg = true;
|
|---|
| 575 | case '.png'
|
|---|
| 576 | options.png = true;
|
|---|
| 577 | case '.bmp'
|
|---|
| 578 | options.bmp = true;
|
|---|
| 579 | case '.eps'
|
|---|
| 580 | options.eps = true;
|
|---|
| 581 | case '.pdf'
|
|---|
| 582 | options.pdf = true;
|
|---|
| 583 | otherwise
|
|---|
| 584 | options.name = varargin{a};
|
|---|
| 585 | end
|
|---|
| 586 | end
|
|---|
| 587 | end
|
|---|
| 588 | end
|
|---|
| 589 |
|
|---|
| 590 | % Set default anti-aliasing now we know the renderer
|
|---|
| 591 | if options.aa_factor == 0
|
|---|
| 592 | options.aa_factor = 1 + 2 * (~(using_hg2(fig) && strcmp(get(ancestor(fig, 'figure'), 'GraphicsSmoothing'), 'on')) | (options.renderer == 3));
|
|---|
| 593 | end
|
|---|
| 594 |
|
|---|
| 595 | % Convert user dir '~' to full path
|
|---|
| 596 | if numel(options.name) > 2 && options.name(1) == '~' && (options.name(2) == '/' || options.name(2) == '\')
|
|---|
| 597 | options.name = fullfile(char(java.lang.System.getProperty('user.home')), options.name(2:end));
|
|---|
| 598 | end
|
|---|
| 599 |
|
|---|
| 600 | % Compute the magnification and resolution
|
|---|
| 601 | if isempty(options.magnify)
|
|---|
| 602 | if isempty(options.resolution)
|
|---|
| 603 | options.magnify = 1;
|
|---|
| 604 | options.resolution = 864;
|
|---|
| 605 | else
|
|---|
| 606 | options.magnify = options.resolution ./ get(0, 'ScreenPixelsPerInch');
|
|---|
| 607 | end
|
|---|
| 608 | elseif isempty(options.resolution)
|
|---|
| 609 | options.resolution = 864;
|
|---|
| 610 | end
|
|---|
| 611 |
|
|---|
| 612 | % Check we have a figure handle
|
|---|
| 613 | if isempty(fig)
|
|---|
| 614 | error('No figure found');
|
|---|
| 615 | end
|
|---|
| 616 |
|
|---|
| 617 | % Set the default format
|
|---|
| 618 | if ~isvector(options) && ~isbitmap(options)
|
|---|
| 619 | options.png = true;
|
|---|
| 620 | end
|
|---|
| 621 |
|
|---|
| 622 | % Check whether transparent background is wanted (old way)
|
|---|
| 623 | if isequal(get(ancestor(fig(1), 'figure'), 'Color'), 'none')
|
|---|
| 624 | options.transparent = true;
|
|---|
| 625 | end
|
|---|
| 626 |
|
|---|
| 627 | % If requested, set the resolution to the native vertical resolution of the
|
|---|
| 628 | % first suitable image found
|
|---|
| 629 | if native && isbitmap(options)
|
|---|
| 630 | % Find a suitable image
|
|---|
| 631 | list = findobj(fig, 'Type', 'image', 'Tag', 'export_fig_native');
|
|---|
| 632 | if isempty(list)
|
|---|
| 633 | list = findobj(fig, 'Type', 'image', 'Visible', 'on');
|
|---|
| 634 | end
|
|---|
| 635 | for hIm = list(:)'
|
|---|
| 636 | % Check height is >= 2
|
|---|
| 637 | height = size(get(hIm, 'CData'), 1);
|
|---|
| 638 | if height < 2
|
|---|
| 639 | continue
|
|---|
| 640 | end
|
|---|
| 641 | % Account for the image filling only part of the axes, or vice
|
|---|
| 642 | % versa
|
|---|
| 643 | yl = get(hIm, 'YData');
|
|---|
| 644 | if isscalar(yl)
|
|---|
| 645 | yl = [yl(1)-0.5 yl(1)+height+0.5];
|
|---|
| 646 | else
|
|---|
| 647 | if ~diff(yl)
|
|---|
| 648 | continue
|
|---|
| 649 | end
|
|---|
| 650 | yl = yl + [-0.5 0.5] * (diff(yl) / (height - 1));
|
|---|
| 651 | end
|
|---|
| 652 | hAx = get(hIm, 'Parent');
|
|---|
| 653 | yl2 = get(hAx, 'YLim');
|
|---|
| 654 | % Find the pixel height of the axes
|
|---|
| 655 | oldUnits = get(hAx, 'Units');
|
|---|
| 656 | set(hAx, 'Units', 'pixels');
|
|---|
| 657 | pos = get(hAx, 'Position');
|
|---|
| 658 | set(hAx, 'Units', oldUnits);
|
|---|
| 659 | if ~pos(4)
|
|---|
| 660 | continue
|
|---|
| 661 | end
|
|---|
| 662 | % Found a suitable image
|
|---|
| 663 | % Account for stretch-to-fill being disabled
|
|---|
| 664 | pbar = get(hAx, 'PlotBoxAspectRatio');
|
|---|
| 665 | pos = min(pos(4), pbar(2)*pos(3)/pbar(1));
|
|---|
| 666 | % Set the magnification to give native resolution
|
|---|
| 667 | options.magnify = (height * diff(yl2)) / (pos * diff(yl));
|
|---|
| 668 | break
|
|---|
| 669 | end
|
|---|
| 670 | end
|
|---|
| 671 | end
|
|---|
| 672 |
|
|---|
| 673 | function A = downsize(A, factor)
|
|---|
| 674 | % Downsample an image
|
|---|
| 675 | if factor == 1
|
|---|
| 676 | % Nothing to do
|
|---|
| 677 | return
|
|---|
| 678 | end
|
|---|
| 679 | try
|
|---|
| 680 | % Faster, but requires image processing toolbox
|
|---|
| 681 | A = imresize(A, 1/factor, 'bilinear');
|
|---|
| 682 | catch
|
|---|
| 683 | % No image processing toolbox - resize manually
|
|---|
| 684 | % Lowpass filter - use Gaussian as is separable, so faster
|
|---|
| 685 | % Compute the 1d Gaussian filter
|
|---|
| 686 | filt = (-factor-1:factor+1) / (factor * 0.6);
|
|---|
| 687 | filt = exp(-filt .* filt);
|
|---|
| 688 | % Normalize the filter
|
|---|
| 689 | filt = single(filt / sum(filt));
|
|---|
| 690 | % Filter the image
|
|---|
| 691 | padding = floor(numel(filt) / 2);
|
|---|
| 692 | for a = 1:size(A, 3)
|
|---|
| 693 | A(:,:,a) = conv2(filt, filt', single(A([ones(1, padding) 1:end repmat(end, 1, padding)],[ones(1, padding) 1:end repmat(end, 1, padding)],a)), 'valid');
|
|---|
| 694 | end
|
|---|
| 695 | % Subsample
|
|---|
| 696 | A = A(1+floor(mod(end-1, factor)/2):factor:end,1+floor(mod(end-1, factor)/2):factor:end,:);
|
|---|
| 697 | end
|
|---|
| 698 | end
|
|---|
| 699 |
|
|---|
| 700 | function A = rgb2grey(A)
|
|---|
| 701 | A = cast(reshape(reshape(single(A), [], 3) * single([0.299; 0.587; 0.114]), size(A, 1), size(A, 2)), class(A));
|
|---|
| 702 | end
|
|---|
| 703 |
|
|---|
| 704 | function A = check_greyscale(A)
|
|---|
| 705 | % Check if the image is greyscale
|
|---|
| 706 | if size(A, 3) == 3 && ...
|
|---|
| 707 | all(reshape(A(:,:,1) == A(:,:,2), [], 1)) && ...
|
|---|
| 708 | all(reshape(A(:,:,2) == A(:,:,3), [], 1))
|
|---|
| 709 | A = A(:,:,1); % Save only one channel for 8-bit output
|
|---|
| 710 | end
|
|---|
| 711 | end
|
|---|
| 712 |
|
|---|
| 713 | function eps_remove_background(fname, count)
|
|---|
| 714 | % Remove the background of an eps file
|
|---|
| 715 | % Open the file
|
|---|
| 716 | fh = fopen(fname, 'r+');
|
|---|
| 717 | if fh == -1
|
|---|
| 718 | error('Not able to open file %s.', fname);
|
|---|
| 719 | end
|
|---|
| 720 | % Read the file line by line
|
|---|
| 721 | while count
|
|---|
| 722 | % Get the next line
|
|---|
| 723 | l = fgets(fh);
|
|---|
| 724 | if isequal(l, -1)
|
|---|
| 725 | break; % Quit, no rectangle found
|
|---|
| 726 | end
|
|---|
| 727 | % Check if the line contains the background rectangle
|
|---|
| 728 | if isequal(regexp(l, ' *0 +0 +\d+ +\d+ +r[fe] *[\n\r]+', 'start'), 1)
|
|---|
| 729 | % Set the line to whitespace and quit
|
|---|
| 730 | l(1:regexp(l, '[\n\r]', 'start', 'once')-1) = ' ';
|
|---|
| 731 | fseek(fh, -numel(l), 0);
|
|---|
| 732 | fprintf(fh, l);
|
|---|
| 733 | % Reduce the count
|
|---|
| 734 | count = count - 1;
|
|---|
| 735 | end
|
|---|
| 736 | end
|
|---|
| 737 | % Close the file
|
|---|
| 738 | fclose(fh);
|
|---|
| 739 | end
|
|---|
| 740 |
|
|---|
| 741 | function b = isvector(options)
|
|---|
| 742 | b = options.pdf || options.eps;
|
|---|
| 743 | end
|
|---|
| 744 |
|
|---|
| 745 | function b = isbitmap(options)
|
|---|
| 746 | b = options.png || options.tif || options.jpg || options.bmp || options.im || options.alpha;
|
|---|
| 747 | end
|
|---|
| 748 |
|
|---|
| 749 | % Helper function
|
|---|
| 750 | function A = make_cell(A)
|
|---|
| 751 | if ~iscell(A)
|
|---|
| 752 | A = {A};
|
|---|
| 753 | end
|
|---|
| 754 | end
|
|---|
| 755 |
|
|---|
| 756 | function add_bookmark(fname, bookmark_text)
|
|---|
| 757 | % Adds a bookmark to the temporary EPS file after %%EndPageSetup
|
|---|
| 758 | % Read in the file
|
|---|
| 759 | fh = fopen(fname, 'r');
|
|---|
| 760 | if fh == -1
|
|---|
| 761 | error('File %s not found.', fname);
|
|---|
| 762 | end
|
|---|
| 763 | try
|
|---|
| 764 | fstrm = fread(fh, '*char')';
|
|---|
| 765 | catch ex
|
|---|
| 766 | fclose(fh);
|
|---|
| 767 | rethrow(ex);
|
|---|
| 768 | end
|
|---|
| 769 | fclose(fh);
|
|---|
| 770 |
|
|---|
| 771 | % Include standard pdfmark prolog to maximize compatibility
|
|---|
| 772 | fstrm = strrep(fstrm, '%%BeginProlog', sprintf('%%%%BeginProlog\n/pdfmark where {pop} {userdict /pdfmark /cleartomark load put} ifelse'));
|
|---|
| 773 | % Add page bookmark
|
|---|
| 774 | fstrm = strrep(fstrm, '%%EndPageSetup', sprintf('%%%%EndPageSetup\n[ /Title (%s) /OUT pdfmark',bookmark_text));
|
|---|
| 775 |
|
|---|
| 776 | % Write out the updated file
|
|---|
| 777 | fh = fopen(fname, 'w');
|
|---|
| 778 | if fh == -1
|
|---|
| 779 | error('Unable to open %s for writing.', fname);
|
|---|
| 780 | end
|
|---|
| 781 | try
|
|---|
| 782 | fwrite(fh, fstrm, 'char*1');
|
|---|
| 783 | catch ex
|
|---|
| 784 | fclose(fh);
|
|---|
| 785 | rethrow(ex);
|
|---|
| 786 | end
|
|---|
| 787 | fclose(fh);
|
|---|
| 788 | end
|
|---|
| 789 |
|
|---|
| 790 | function set_tick_mode(Hlims, ax)
|
|---|
| 791 | % Set the tick mode of linear axes to manual
|
|---|
| 792 | % Leave log axes alone as these are tricky
|
|---|
| 793 | M = get(Hlims, [ax 'Scale']);
|
|---|
| 794 | if ~iscell(M)
|
|---|
| 795 | M = {M};
|
|---|
| 796 | end
|
|---|
| 797 | M = cellfun(@(c) strcmp(c, 'linear'), M);
|
|---|
| 798 | set(Hlims(M), [ax 'TickMode'], 'manual');
|
|---|
| 799 | end
|
|---|