2%> Export figures in a publication-quality format.<br>
5%> This function saves a figure or single axes to one or more vectors and/or
6%> bitmap file formats and/or outputs a rasterized
version to the workspace,
7%> with the following properties:<br>
9%> <li>
Figure/axes reproduced as it appears on the screen.
10%> <li> Cropped/padded borders (optional).
11%> <li> Embedded fonts (vector formats).
12%> <li> Improved line and grid line styles.
13%> <li> Anti-aliased graphics (bitmap formats).
14%> <li> Render images at native resolution (optional
for bitmap formats).
15%> <li> Transparent background supported (pdf, eps, png, tif, gif).
16%> <li> Semi-transparent patch objects supported (png, tif).
17%> <li> RGB, CMYK, or grayscale output (CMYK only with pdf, eps, tif).
18%> <li> Variable image compression, including lossless (pdf, eps, jpg).
19%> <li> Optional rounded line-caps (pdf, eps).
20%> <li> Optionally append to file (pdf, tif, gif).
21%> <li> Vector formats: pdf, eps, emf, svg.
22%> <li> Bitmap formats: png, tif, jpg, bmp, gif, clipboard, export to workspace.
25%> This function is especially suited to exporting figures
for use
26%> in publications and presentations because of the high quality
27%> and portability of media produced.<br>
29%> Note that the background color and figure dimensions are reproduced
30%> (the latter approximately, and ignoring cropping & magnification) in the output file.<br>
31%> For transparent background (and semi-transparent patch objects), use the ``-transparent``
32%> option or set the figure ``
'Color'``
property to ``
'none'``.<br>
33%> To make axes transparent set the axes ``
'Color'``
property to ``
'none'``.<br>
34%> PDF, EPS, TIF, and PNG are the only formats that support a transparent background.<br>
35%> Only TIF and PNG formats support the transparency of patch objects.<br>
37%> The choice of renderer (opengl/zbuffer/painters) greatly impacts the output quality.<br>
38%> The
default value (opengl
for bitmaps, painters
for vector formats) generally gives good results,
39%> but
if you are not satisfied, then
try another renderer.<br>
43%> <li> For vector formats (EPS, PDF), only painters generate vector graphics.<br>
44%> <li> For bitmap formats, only OPENGL correctly renders transparent patches.<br>
45%> <li> For bitmap formats, only painters correctly scale line dash and dot
46%> lengths when magnifying or anti-aliasing.<br>
47%> <li> Fonts may be substitued with Courier when
using painters.<br>
50%> When exporting to vector format (PDF & EPS) and bitmap format
using the painters
51%> renderer,
this function
requires that
ghostscript is installed on your system.<br>
52%> You can download
this from: http:
53%> When exporting to EPS,
pdftops from the Xpdf function suite are also required.<br>
54%> You can download
this from: http:
56%> SVG output uses MATLAB
's built-in SVG export if available, or otherwise the
57%> fig2svg (https://github.com/kupiqu/fig2svg) or plot2svg
58%> (https://github.com/jschwizer99/plot2svg) utilities, if available.<br>
61%> Cropping and padding are not supported in SVG and EMF output.<br>
63%> \param[in] varargin : Any ``property, value`` pair of the following set:<br>
65%> <li> The option ``filename`` - string containing the name (optionally including full or
66%> relative path) of the file, the figure is to be saved as.<br>
67%> If no path is specified, the figure is saved in the current folder.<br>
68%> If no name and output arguments are specified, the figure ``FileName`` property is used.<br>
69%> If this property is empty, then the default name ``'export_fig_out
'`` is used.<br>
70%> If neither file extension nor a format parameter is specified, a ``".png"``
71%> is added to the filename, and the figure is saved in PNG format.<br>
73%> <li> The option ``-<format>`` - string(s) containing the output file extension(s).<br>
74%> Options are ``'-pdf
'``, ``'-eps
'``, ``'emf
'``, ``'-svg
'``, ``'-png
'``, ``'-tif
'``, ``'-jpg
'``, ``'-gif
'``, ``'-bmp
'``.<br>
75%> Multiple formats can be specified without restriction.<br>
76%> For example: ``savefig('-jpg
', '-pdf
', '-png
', ...)``.<br>
77%> Note that ``'-tif
'``, ``'-tiff
'`` are equivalent, and so are '-jpg
'``, ``'-jpeg
'.<br>
79%> <li> The option ``-transparent`` indicating that the figure background is to be made
80%> transparent (PNG, PDF, TIF, EPS, EMF formats only). Implies ``-noinvert``.<br>
82%> <li> The option ``-nocrop`` indicating that empty margins should not be cropped.<br>
84%> <li> The option ``-c[<val>,<val>,<val>,<val>]`` indicating crop amounts.<br>
85%> It must be a 4-element vector of numeric values: ``[top, right, bottom, left]``.<br>
86%> where NaN/Inf indicates auto-cropping, 0 means no cropping, any
87%> other value means cropping in pixel amounts. e.g. ``'-c7,15,0,NaN
'``
88%> Note that SVG and EMF formats do not support this option.<br>
90%> <li> The option ``-p<val>`` to pad a border of width val to exported files,
91%> where ``val`` is either a relative size with respect to the cropped image
92%> size (i.e. ``p=0.01`` adds a ``1%`` border).<br>
93%> For EPS and PDF formats, ``val`` can also be integer in units of ``1/72`` points (``abs(val)>1``).<br>
94%> The input ``val`` can be positive (padding) or negative (extra cropping).<br>
95%> If used, the ``-nocrop`` flag will be ignored, i.e. the image will
96%> always be cropped and then padded. Default: 0 (i.e. no padding).<br>
97%> Note that SVG and EMF formats do not support this option.<br>
99%> <li> The option ``-m<val>`` where ``val`` indicates the factor to magnify the figure dimensions
100%> when generating bitmap outputs (does not affect vector formats).<br>
101%> Default: ``'-m1
'`` (i.e. ``val=1``). Note: ``val~=1`` slows down savefig.<br>
103%> <li> The option ``-r<val>`` where ``val`` indicates the resolution (in pixels per inch) to
104%> export bitmap and vector outputs, without changing the dimensions of
105%> the on-screen figure. Default: ``'-r864
'`` (for vector output only).<br>
106%> Note: ``-m`` option overrides ``-r`` option for bitmap exports only.<br>
108%> <li> The option ``-native`` indicating that the output resolution
109%> (when outputting a bitmap format) should be such that the vertical resolution
110%> of the first suitable image found in the figure is at the native resolution of that image.<br>
111%> To specify a particular image to use, give it the tag ``'export_fig_native
'``.<br>
112%> Notes: This overrides any value set with the -m and -r options.<br>
113%> It also assumes that the image is displayed front-to-parallel with the screen.<br>
114%> The output resolution is approximate and should not be relied upon.<br>
115%> Anti-aliasing can have adverse effects on image quality (disable with the ``-a1`` option).<br>
117%> <li> The option ``-a1, -a2, -a3, -a4`` indicating the amount of anti-aliasing (AA) to
118%> use for bitmap outputs when GraphicsSmoothing is not available.<br>
119%> ``'-a1
'=no AA; '-a4
'=max``.<br>
120%> Default: 3 for HG1, 1 for HG2.<br>
122%> <li> The option ``-<renderer>`` to force a particular renderer
123%> (painters, opengl or [in R2014a or older] zbuffer).<br>
124%> Default value: opengl for bitmap formats or figures with patches and/or
125%> transparent annotations and painters for vector formats without patches/transparencies.<br>
127%> <li> The option ``-<colorspace>`` indicating which colorspace color figures should
128%> be saved in: RGB (default), CMYK or gray. Usage example: ``'-gray
'``.<br>
129%> Note: CMYK is only supported in PDF, EPS and TIF formats.<br>
131%> <li> The option ``-q<val>`` to vary bitmap image quality (PDF, EPS, JPG formats only).<br>
132%> A larger val, in the range ``0-100``, produces higher quality and
133%> lower compression. ``val > 100`` results in lossless compression.<br>
134%> Default: '-q95
' for JPG, ghostscript prepress default for PDF,EPS.<br>
135%> Note: lossless compression can sometimes give a smaller file size
136%> than the default lossy compression, depending on the image type.<br>
138%> <li> The option ``-n<val>`` to set minimum output image size (bitmap formats only).<br>
139%> The output size can be specified as a single value (for both rows
140%> and cols, e.g. ``-n200``) or comma-separated values (e.g. ``-n300,400``).<br>
141%> Use an Inf value to keep a dimension unchanged (e.g., ``-n50,inf``).<br>
142%> Use a ``NaN`` value to keep the aspect ratio unchanged (e.g., ``-n50, nan``).<br>
144%> <li> The option ``-x<val>`` to set maximum output image size (bitmap formats only).<br>
145%> The output size can be specified as a single value (for both rows
146%> and cols, e.g. ``-x200``) or comma-separated values (e.g. ``-x300, 400``).<br>
147%> Use an ``Inf`` value to keep a dimension unchanged (e.g. ``-x50,inf``).<br>
148%> Use a ``NaN`` value to keep aspect ratio unchanged (e.g. ``-x50,nan``).<br>
150%> <li> The option ``-s<val>`` to scale output image to a specific size (bitmap formats only).<br>
151%> The fixed size can be specified as a single value (for rows=cols) or
152%> comma-separated values (e.g. ``-s300,400``).<br>
153%> Each value can be a scalar integer (signifying pixels) or percentage (e.g., ``-s125%``).<br>
154%> The scaling is done last, after any other cropping/rescaling due to other params.<br>
156%> <li> The option ``-append`` indicates that if the file already exists the figure is to
157%> be appended as a new page, instead of being overwritten (default).<br>
158%> PDF, TIF & GIF output formats only (multi-image GIF = animated).<br>
160%> <li> The option ``-bookmark`` to indicate that a bookmark with the name of the
161%> figure is to be created in the output file (PDF format only).<br>
163%> <li> The option ``-clipboard`` to save the output as an image on the system clipboard.<br>
165%> <li> The option ``-clipboard<:format>`` - copies to clipboard in the specified format:
166%> image (default), bitmap, emf, or pdf.<br>
167%> Note that Only ``-clipboard`` (or ``-clipboard:image``, which is the same)
168%> applies ``savefig`` parameters such as cropping, padding, etc:<br>
170%> <li> The option ``-clipboard:image`` creates a bitmap image using savefig processing.<br>
171%> <li> The option ``-clipboard:bitmap`` creates a bitmap image as-is (no auto-cropping etc.).<br>
172%> <li> The option ``-clipboard:emf`` is vector format without auto-cropping; Windows-only.<br>
173%> <li> The option ``-clipboard:pdf`` is vector format without cropping; not universally supported.<br>
176%> <li> The option ``-d<gs_option>`` to indicate a ghostscript setting. For example,
177%> ``-dMaxBitmap=0`` or ``-dNoOutputFonts`` (Ghostscript 9.15+).<br>
179%> <li> The option ``-depsc`` - option to use EPS level-3 rather than the default level-2 print
180%> device. This solves some bugs with MATLAB's
default ``-depsc2`` device
181%> such as discolored subplot lines on images (vector formats only).<br>
183%> <li> The option ``-metadata <metaDataInfo>`` - adds the specified meta-data information to the
184%> exported file (PDF format only). metaDataInfo must be either a
struct
185%> or a cell array with pairs of values: {``
'fieldName'``, ``fieldValue``, ...}.<br>
186%> Common metadata fields: Title, Author, Creator, Producer, Subject, Keywords.<br>
188%> <li> The option ``-update`` - downloads and installs the latest
version of ``
savefig``.<br>
192%> <li> The option ``-nofontswap`` - avoids font swapping. Font swapping is automatically
193%> done in vector formats (only): 11 standard MATLAB fonts are
194%> replaced by the original figure fonts. This option prevents this.<br>
196%> <li> The option ``-font_space <char>`` sets a spacer character for font-names that
197%> contain spaces used by EPS/PDF. Default: ``
''``.<br>
199%> <li> The option ``-linecaps`` creates rounded line-caps (vector formats only).<br>
201%> <li> The option ``-noinvert`` avoids setting figure
's InvertHardcopy property to
202%> 'off
' during output (this solves some problems of empty outputs).<br>
204%> <li> The option ``-preserve_size`` preserves the figure's PaperSize property in output
205%> file (PDF/EPS formats only;
default is to not preserve it).<br>
207%> <li> The option ``-options <optionsStruct>`` - format-specific parameters as defined in MATLAB
208%> documentation of the ``imwrite`` function, contained in a
struct under
209%> the format
name. For example, to specify the JPG Comment parameter,
210%> pass a
struct such as
this: ``options.JPG.Comment=
'abc'``. Similarly,
211%> ``options.PNG.BitDepth=4``. Only used by PNG, TIF, JPG, and GIF output formats.<br>
212%> Options can also be specified as a cell array of
name-value pairs,
213%> e.g., ``{
'BitDepth', 4,
'Author',
'Yair'}`` - these options will be used
214%> by all supported output formats of the
savefig command.<br>
216%> <li> The option ``-silent`` to avoid various warning and informational messages, such
217%> as
version update checks, transparency or renderer issues, etc.<br>
219%> <li> The option ``-notify`` to notify the user when export is done, in both a console
220%> message and a popup dialog (allow opening the exported file/folder).<br>
222%> <li> The option ``-regexprep <old> <
new>`` - replaces all occurrences of ``<old>`` (a regular expression
223%>
string or array of strings;
case-sensitive), with the corresponding
224%> ``<
new>``
string(s), in EPS/PDF files (only). See ``regexp`` function doc.<br>
225%> Warning: invalid replacement can make your EPS/PDF file unreadable!<br>
227%> <li> The option ``-toolbar`` - adds an interactive export button to the figure toolbar.<br>
229%> <li> The option ``-menubar`` - adds an interactive export menu to the figure menubar.<br>
231%> <li> The option ``-contextmenu`` - adds interactive export menu to figure context-menu (right-click)<br>
233%> <li> The option ``handle`` - handle of the figure, axes, or uipanels (can be an array of handles
234%> but all the objects must be in the same figure) to be exported.<br>
235%> Default: ``gcf`` (handle of the current figure).<br>
237%> <li> The option ``figName`` -
name (title) of the figure to export (e.g., ``
'Figure 1'`` or ``
'My fig'``).<br>
238%> Overridden by handle (
if specified); Default: current figure.<br>
242%> ``imageData`` : The output image cube of type ``uint8`` of
243%> shape ``[M, N, C]`` containing the exported figure data.<br>
244%> ``alpha`` : The output image matrix of shape ``[M, N]`` of alpha-matte
245%> values in the range ``[0, 1]``
for the
case of transparent background.<br>
250%> imageData = pm.vis.figure.savefig(varargin)
251%> [imageData, alpha] = pm.vis.figure.savefig(varargin)
252%> pm.vis.figure.savefig filename
253%> pm.vis.figure.savefig ... -<format>
254%> pm.vis.figure.savefig ... -nocrop
255%> pm.vis.figure.savefig ... -c[<val>,<val>,<val>,<val>]
256%> pm.vis.figure.savefig ... -transparent
257%> pm.vis.figure.savefig ... -native
258%> pm.vis.figure.savefig ... -m<val>
259%> pm.vis.figure.savefig ... -r<val>
260%> pm.vis.figure.savefig ... -a<val>
261%> pm.vis.figure.savefig ... -q<val>
262%> pm.vis.figure.savefig ... -p<val>
263%> pm.vis.figure.savefig ... -n<val> or: -n<val>,<val>
264%> pm.vis.figure.savefig ... -x<val> or: -x<val>,<val>
265%> pm.vis.figure.savefig ... -s<val> or: -s<val>,<val>
266%> pm.vis.figure.savefig ... -d<gs_option>
267%> pm.vis.figure.savefig ... -depsc
268%> pm.vis.figure.savefig ... -metadata <metaDataInfo>
269%> pm.vis.figure.savefig ... -<renderer>
270%> pm.vis.figure.savefig ... -<colorspace>
271%> pm.vis.figure.savefig ... -append
272%> pm.vis.figure.savefig ... -bookmark
273%> pm.vis.figure.savefig ... -clipboard<:format>
274%> pm.vis.figure.savefig ... -update
275%> pm.vis.figure.savefig ... -
version
276%> pm.vis.figure.savefig ... -nofontswap
277%> pm.vis.figure.savefig ... -font_space <char>
278%> pm.vis.figure.savefig ... -linecaps
279%> pm.vis.figure.savefig ... -noinvert
280%> pm.vis.figure.savefig ... -preserve_size
281%> pm.vis.figure.savefig ... -options <optionsStruct>
282%> pm.vis.figure.savefig ... -silent
283%> pm.vis.figure.savefig ... -notify
284%> pm.vis.figure.savefig ... -regexprep <pattern> <replace>
285%> pm.vis.figure.savefig ... -toolbar
286%> pm.vis.figure.savefig ... -menubar
287%> pm.vis.figure.savefig ... -contextmenu
288%> pm.vis.figure.savefig(..., handle)
289%> pm.vis.figure.savefig(..., figName)
294%> This function is a reimplementation of the MATLAB
package
295%> [export_fig](https:
300%> pm.vis.figure.savefig(); % export the current figure with the
default name.
301%> pm.vis.figure.savefig(
"gridplot.pdf") % export figure to the specified PDF file.
302%> pm.vis.figure.savefig(
"gridplot.png",
"-m4 -transparent") % export a large png plot of magnitude 4 with transparency.
308%> Copyright (C) Oliver Woodford 2008-2014, Yair Altman 2015-
311%> The idea of
using ghostscript is inspired by Peder Axensten
's SAVEFIG
312%> (fex id: 10889) which is itself inspired by EPS2PDF (fex id: 5782).
313%> The idea for using pdftops came from the MATLAB newsgroup (id: 168171).
314%> The idea of editing the EPS file to change line styles comes from Jiro
315%> Doke's FIXPSLINESTYLE (fex
id: 17928).
316%> The idea of changing dash length with line width came from comments on
317%> fex id: 5743, but the implementation is mine :)
318%> The idea of anti-aliasing bitmaps came from Anders Brun
's MYAA (fex id: 20979).
319%> The idea of appending figures in pdfs came from Matt C in comments on the
321%> Thanks to Roland Martin for pointing out the colour MATLAB
322%> bug/feature with colorbar axes and transparent backgrounds.
323%> Thanks also to Andrew Matthews for describing a bug to do with the figure
324%> size changing in -nodisplay mode. I couldn't reproduce it, but included a
326%> Thanks to Tammy Threadgill
for reporting a bug where an axes is not
327%> isolated from gui objects.
329%> 23/02/12: Ensure that axes limits don
't change during printing
330%> 14/03/12: Fix bug in fixing the axes limits (thanks to Tobias Lamour for reporting it).
331%> 02/05/12: Incorporate patch of Petr Nechaev (many thanks), enabling bookmarking of figures in pdf files.
332%> 09/05/12: Incorporate patch of Arcelia Arrieta (many thanks), to keep tick marks fixed.
333%> 12/12/12: Add support for isolating uipanels. Thanks to michael for suggesting it.
334%> 25/09/13: Add support for changing resolution in vector formats. Thanks to Jan Jaap Meijer for suggesting it.
335%> 07/05/14: Add support for '~
' at start of path. Thanks to Sally Warner for suggesting it.
336%> 24/02/15: Fix MATLAB R2014b bug (issue #34): plot markers are not displayed when ZLimMode='manual
'
337%> 25/02/15: Fix issue #4 (using HG2 on R2014a and earlier)
338%> 25/02/15: Fix issue #21 (bold TeX axes labels/titles in R2014b)
339%> 26/02/15: If temp dir is not writable, use the user-specified folder for temporary EPS/PDF files (Javier Paredes)
340%> 27/02/15: Modified repository URL from github.com/ojwoodford to /altmany; Indented main function; Added top-level try-catch block to display useful workarounds
341%> 28/02/15: Enable users to specify optional ghostscript options (issue #36)
342%> 06/03/15: Improved image padding & cropping thanks to Oscar Hartogensis
343%> 26/03/15: Fixed issue #49 (bug with transparent grayscale images); fixed out-of-memory issue
344%> 26/03/15: Fixed issue #42: non-normalized annotations on HG1
345%> 26/03/15: Fixed issue #46: Ghostscript crash if figure units <> pixels
346%> 27/03/15: Fixed issue #39: bad export of transparent annotations/patches
347%> 28/03/15: Fixed issue #50: error on some MATLAB versions with the fix for issue #42
348%> 29/03/15: Fixed issue #33: bugs in MATLAB's print() function with -cmyk
349%> 29/03/15: Improved processing of input args (accept space between param
name & value, related to issue #51)
350%> 30/03/15: When exporting *.fig files, then saveas *.fig
if figure is open, otherwise export the specified fig file
351%> 30/03/15: Fixed edge
case bug introduced yesterday (commit #ae1755bd2e11dc4e99b95a7681f6e211b3fa9358)
352%> 09/04/15: Consolidated header comment sections; initialize output vars only
if requested (nargout>0)
353%> 14/04/15: Workaround
for issue #45: lines in image subplots are exported in invalid color
354%> 15/04/15: Fixed edge-
case in parsing input parameters; fixed help section to
show the -depsc option (issue #45)
355%> 21/04/15: Bug fix: Ghostscript croaks on % chars in output PDF file (reported by Sven on FEX page, 15-Jul-2014)
356%> 22/04/15: Bug fix: Pdftops croaks on relative paths (reported by Tintin Milou on FEX page, 19-Jan-2015)
357%> 04/05/15: Merged fix #63 (Kevin Mattheus Moerman): prevent tick-label changes during export
358%> 07/05/15: Partial fix
for issue #65: PDF export used painters rather than opengl renderer (thanks Nguyenr)
359%> 08/05/15: Fixed issue #65: bad PDF append since commit #e9f3cdf 21/04/15 (thanks Robert Nguyen)
360%> 12/05/15: Fixed issue #67: exponent labels cropped in export, since fix #63 (04/05/15)
361%> 28/05/15: Fixed issue #69: set non-bold label font only
if the
string contains symbols (\beta etc.), followup to issue #21
362%> 29/05/15: Added informative error message in
case user requested SVG output (issue #72)
363%> 09/06/15: Fixed issue #58: -transparent removed anti-aliasing when exporting to PNG
365%> 07/07/15: Added -nofontswap option to avoid font-swapping in EPS/PDF
366%> 16/07/15: Fixed problem with anti-aliasing on old MATLAB releases
367%> 11/09/15: Fixed issue #103: magnification must never become negative; also fixed reported error msg in parsing input params
368%> 26/09/15: Alert
if trying to export transparent patches/areas to non-PNG outputs (issue #108)
369%> 04/10/15: Do not suggest workarounds
for certain errors that have already been handled previously
370%> 01/11/15: Fixed issue #112: use same renderer in
print2eps as
savefig (thanks to Jesús Pestana Puerta)
371%> 10/11/15: Custom GS installation webpage
for MacOS. Thanks to Andy Hueni via FEX
372%> 19/11/15: Fixed clipboard export in R2015b (thanks to Dan K via FEX)
373%> 21/02/16: Added -c option
for indicating specific crop amounts (idea by Cedric Noordam on FEX)
374%> 08/05/16: Added message about possible error reason when groot.Units~=pixels (issue #149)
375%> 17/05/16: Fixed
case of image YData containing more than 2 elements (issue #151)
376%> 08/08/16: Enabled exporting transparency to TIF, in addition to PNG/PDF (issue #168)
377%> 11/12/16: Added alert in
case of error creating output PDF/EPS file (issue #179)
378%> 13/12/16: Minor fix to the commit
for issue #179 from 2 days ago
379%> 22/03/17: Fixed issue #187: only set manual ticks when no exponent is present
380%> 09/04/17: Added -linecaps option (idea by Baron Finer, issue #192)
381%> 15/09/17: Fixed issue #205: incorrect tick-labels when Ticks number don
't match the TickLabels number
382%> 15/09/17: Fixed issue #210: initialize alpha map to ones instead of zeros when -transparent is not used
383%> 18/09/17: Added -font_space option to replace font-name spaces in EPS/PDF (workaround for issue #194)
384%> 18/09/17: Added -noinvert option to solve some export problems with some graphic cards (workaround for issue #197)
385%> 08/11/17: Fixed issue #220: axes exponent is removed in HG1 when TickMode is 'manual
' (internal MATLAB bug)
386%> 08/11/17: Fixed issue #221: alert if the requested folder does not exist
387%> 19/11/17: Workaround for issue #207: alert when trying to use transparent bgcolor with -opengl
388%> 29/11/17: Workaround for issue #206: warn if exporting PDF/EPS for a figure that contains an image
389%> 11/12/17: Fixed issue #230: use OpenGL renderer when exported image contains transparency (also see issue #206)
390%> 30/01/18: Updated SVG message to point to https://github.com/kupiqu/plot2svg and display user-selected filename if available
391%> 27/02/18: Fixed issue #236: axes exponent cropped from output if on right-hand axes
392%> 29/05/18: Fixed issue #245: process "string" inputs just like 'char' inputs
393%> 13/08/18: Fixed issue #249: correct black axes color to off-black to avoid extra cropping with -transparent
394%> 27/08/18: Added a possible file-open reason in EPS/PDF write-error message (suggested by "craq" on FEX page)
395%> 22/09/18: Xpdf website changed to xpdfreader.com
396%> 23/09/18: Fixed issue #243: only set non-bold font (workaround for issue #69) in R2015b or earlier; warn if changing font
397%> 23/09/18: Workaround for issue #241: don't use -r864 in EPS/PDF outputs when -native is requested (solves black lines problem)
398%> 18/11/18: Issue #261: Added informative alert when trying to export a uifigure (
which is not currently supported)
399%> 13/12/18: Issue #261: Fixed last commit
for cases of specifying axes/panel handle as input, rather than a figure handle
400%> 13/01/19: Issue #72: Added basic SVG output support
401%> 04/02/19: Workaround
for issues #207 and #267: -transparent implies -noinvert
402%> 08/03/19: Issue #269: Added ability to specify format-specific options
for PNG,TIF,JPG outputs; fixed help section
403%> 21/03/19: Fixed the workaround
for issues #207 and #267 from 4/2/19 (-transparent now does *NOT* imply -noinvert; -transparent output should now be ok in all formats)
404%> 12/06/19: Issue #277: Enabled preservation of figure
's PaperSize in output PDF/EPS file
405%> 06/08/19: Remove warning message about obsolete JavaFrame in R2019b
406%> 30/10/19: Fixed issue #261: added support for exporting uifigures and uiaxes (thanks to idea by @MarvinILA)
407%> 12/12/19: Added warning in case user requested anti-aliased output on an aliased HG2 figure (issue #292)
408%> 15/12/19: Added promo message
409%> 08/01/20: (3.00) Added check for newer version online (initialized to version 3.00)
410%> 15/01/20: (3.01) Clarified/fixed error messages; Added error IDs; easier -update; various other small fixes
411%> 20/01/20: (3.02) Attempted fix for issue #285 (unsupported patch transparency in some Ghostscript versions); Improved suggested fixes message upon error
412%> 03/03/20: (3.03) Suggest to upload problematic EPS file in case of a Ghostscript error in eps2pdf (& don't
delete this file)
413%> 22/03/20: (3.04) Workaround
for issue #15; Alert
if ghostscript file not found on MATLAB path
414%> 10/05/20: (3.05) Fix the generated SVG file, based on Cris Luengo
's SVG_FIX_VIEWBOX; Don't generate PNG when only SVG is requested
415%> 02/07/20: (3.06) Significantly improved performance (speed) and fidelity of bitmap images; Return alpha matrix
for bitmap images; Fixed issue #302 (-update bug); Added EMF output; Added -clipboard formats (image,bitmap,emf,pdf); Added hints
for exportgraphics/copygraphics usage in certain use-cases; Added description of
new version features in the update message; Fixed issue #306 (yyaxis cropping); Fixed EPS/PDF
auto-cropping with -transparent
416%> 06/07/20: (3.07) Fixed issue #307 (bug in padding of bitmap images); Fixed axes transparency in -clipboard:emf with -transparent
417%> 07/07/20: (3.08) Fixed issue #308 (bug in R2019a and earlier)
418%> 18/07/20: (3.09) Fixed issue #310 (bug with tiny image on HG1); Fixed title cropping bug
419%> 23/07/20: (3.10) Fixed issues #313,314 (figure position changes
if units ~= pixels); Display multiple versions change-log,
if relevant; Fixed issue #312 (PNG: only use alpha channel
if -transparent was requested)
420%> 30/07/20: (3.11) Fixed issue
#317 (bug when exporting figure with non-pixels units); Potential solve also of issue #303 (size change upon export)
421%> 14/08/20: (3.12) Fixed some exportgraphics/copygraphics compatibility messages; Added -silent option to suppress non-critical messages; Reduced promo message display rate to once a week; Added progress messages during
savefig(
'-update')
422%> 07/10/20: (3.13) Added
version info and change-log links to update message (issue #322); Added -
version option to
return the current
savefig version; Avoid JavaFrame warning message; Improved exportgraphics/copygraphics infomercial message inc. support of upcoming MATLAB R2021a
423%> 10/12/20: (3.14) Enabled user-specified regexp replacements in generated EPS/PDF files (issue #324)
424%> 01/07/21: (3.15) Added informative message in
case of setopacityalpha error (issue #285)
425%> 26/08/21: (3.16) Fixed problem of white elements appearing transparent (issue #330); clarified some error messages
426%> 27/09/21: (3.17) Made MATLAB
's builtin export the default for SVG, rather than fig2svg/plot2svg (issue #316); updated transparency error message (issues #285, #343); reduced promo message frequency
427%> 03/10/21: (3.18) Fixed warning about invalid escaped character when the output folder does not exist (issue #345)
428%> 25/10/21: (3.19) Fixed print error when exporting a specific subplot (issue #347); avoid duplicate error messages
429%> 11/12/21: (3.20) Added GIF support, including animated & transparent-background; accept format options as cell-array, not just nested struct
430%> 20/12/21: (3.21) Speedups; fixed exporting non-current figure (hopefully fixes issue #318); fixed warning when appending to animated GIF
431%> 02/03/22: (3.22) Fixed small potential memory leak during screen-capture; expanded exportgraphics message for vector exports; fixed rotated tick labels on R2021a+
432%> 02/03/22: (3.23) Added -toolbar and -menubar options to add figure toolbar/menubar items for interactive figure export (issue #73); fixed edge-case bug with GIF export
433%> 14/03/22: (3.24) Added support for specifying figure name in addition to handle; added warning when trying to export TIF/JPG/BMP with transparency; use current figure as default handle even when its HandleVisibility is not 'on
'
434%> 16/03/22: (3.25) Fixed occasional empty files due to excessive cropping (issues #318, #350, #351)
435%> 01/05/22: (3.26) Added -transparency option for TIFF files
436%> 15/05/22: (3.27) Fixed EPS bounding box (issue #356)
437%> 04/12/22: (3.28) Added -metadata option to add custom info to PDF files; fixed -clipboard export (transparent and gray-scale images; deployed apps; old Matlabs)
438%> 03/01/23: (3.29) Use silent mode by default in deployed apps; suggest installing ghostscript/pdftops if required yet missing; fixed invalid chars in export filename; reuse existing figure toolbar if available
439%> 03/02/23: (3.30) Added -contextmenu option to add interactive context-menu items; fixed: -menubar,-toolbar created the full default figure menubar/toolbar if not shown; enlarged toolbar icon; support adding savefig icon to custom toolbars; alert if specifying multiple or invalid handle(s)
440%> 20/02/23: (3.31) Fixed PDF quality issues as suggested by @scholnik (issues #285, #368); minor fixes for MacOS/Linux; use figure's FileName property (
if available) as the
default export filename; added -gif optional format parameter; Display the export folder (full pathname) in menu items when
using -toolbar, -menubar and/or -contextmenu
441%> 21/02/23: (3.32) Fixed EPS export error handling in deployed apps; use MATLAB
's builtin EPS export if pdftops is not installed or fails; disabled EMF export option on MacOS/Linux; fixed some EMF warning messages; don't export PNG
if only -toolbar etc were specified
442%> 23/02/23: (3.33) Fixed PDF -append (issue #369); Added -notify option to notify user when the export is done; propagate all specified
savefig options to -toolbar,-menubar,-contextmenu exports; -silent is no longer set by
default in deployed apps (i.e. you need to call -silent explicitly)
443%> 23/03/23: (3.34) Fixed error when exporting axes handle to clipboard without filename (issue #372)
444%> 11/04/23: (3.35) Added -n,-x,-s options to set min, max, and fixed output image size (issue #315)
445%> 13/04/23: (3.36) Reduced (hopefully fixed) unintended EPS/PDF image cropping (issues #97, #318); clarified warning in
case of PDF/EPS export of transparent patches (issues #94, #106, #108)
446%> 23/04/23: (3.37) Fixed run-time error with old MATLAB releases (issue #374); -notify console message about exported image now displays black (STDOUT) not red (STDERR)
447%> 15/05/23: (3.38) Fixed endless recursion when
using savefig in Live Scripts (issue #375); don
't warn about exportgraphics/copygraphics alternatives in deployed mode
448%> 30/05/23: (3.39) Fixed exported bgcolor of uifigures or figures in Live Scripts (issue #377)
449%> 06/07/23: (3.40) For Tiff compression, use AdobeDeflate codec (if available) instead of Deflate (issue #379)
450%> 29/11/23: (3.41) Fixed error when no filename is specified nor available in the exported figure (issue #381)
451%> 05/12/23: (3.42) Fixed unintended cropping of colorbar title in PDF export with -transparent (issues #382, #383)
452%> 07/12/23: (3.43) Fixed unintended modification of colorbar in bitmap export (issue #385)
457%> \FatemehBagheri, May 20 2024, 1:25 PM, NASA Goddard Space Flight Center (GSFC), Washington, D.C.<br>
458%> \AmirShahmoradi, May 16 2016, 9:03 AM, Oden Institute for Computational Engineering and Sciences (ICES), UT Austin<br>
459function [imageData, alpha] = savefig(varargin) %#ok<*STRCL1,*DATST,*TNOW1>
462 %%%% Ensure all input string values are characters.
465 for i = 1:length(varargin)
466 if isa(varargin{i}, "string")
467 varargin{i} = convertStringsToChars(varargin{i});
476 [imageData, alpha] = deal([]);
478 displaySuggestedWorkarounds = true;
480 % Ensure the figure is rendered correctly _now_ so that properties like axes limits are up-to-date
482 pause(0.05); % this solves timing issues with Java Swing's EDT (http:
484 % Display promo (just once every 10 days!)
485 persistent promo_time
486 if isempty(promo_time)
487 try promo_time = getpref(
'savefig',
'promo_time');
catch, promo_time=-inf; end
489 if abs(now-promo_time) > 10 && ~isdeployed
491 msg = char(
'Gps!qspgfttjpobm!Nbumbc!bttjtubodf-!qmfbtf!dpoubdu!=%?'-1);
492 url = char(
'iuuqt;00VoepdvnfoufeNbumbc/dpn0dpotvmujoh'-1);
493 displayPromoMsg(msg, url);
495 setpref(
'savefig',
'promo_time',now)
498 % Use the current figure as the
default figure handle
499 % temporarily set ShowHiddenHandles=
'on' to access figure with HandleVisibility=
'off'
500 try oldValue = get(0,
'ShowHiddenHandles'); set(0,
'ShowHiddenHandles',
'on');
catch, end
501 fig = get(0,
'CurrentFigure');
502 try set(0,
'ShowHiddenHandles',oldValue);
catch, end
504 % Parse the input arguments
506 for idx = nargin:-1:1, argNames{idx} = inputname(idx); end
507 [fig, options] =
parse_args(nargout, fig, argNames, varargin{:});
509 % Check
for newer
version and exportgraphics/copygraphics compatibility
510 currentVersion = 3.43;
511 if options.version %
savefig's version requested - return it and bail out
512 imageData = currentVersion;
515 if ~options.silent && ~isdeployed
516 % Check for newer version (not too often)
517 checkForNewerVersion(currentVersion); % this breaks in version 3.05- due to regexp limitation in checkForNewerVersion()
519 % Hint to users to use exportgraphics/copygraphics in certain cases
520 alertForExportOrCopygraphics(options);
524 % Ensure that we have a scalar valid figure handle
526 return % silent bail-out
528 error('savefig:NoFigure
','No figure found
');
529 elseif numel(fig) > 1
530 error('savefig:MultipleFigures
','savefig can only process one figure at a time
');
531 elseif ~ishandle(fig)
532 error('savefig:InvalidHandle
','invalid figure handle specified to
savefig');
533 elseif ~isequal(getappdata(fig,'isExportFigCopy
'),true)
534 oldWarn = warning('off
','MATLAB:HandleGraphics:ObsoletedProperty:JavaFrame
');
535 warning off MATLAB:ui:javaframe:PropertyToBeRemoved
536 hFig = handle(ancestor(fig,'figure
'));
537 try jf = get(hFig,'JavaFrame_I
'); catch, try jf = get(hFig,'JavaFrame
'); catch, jf=1; end, end %#ok<JAVFM>
539 if isempty(jf) % this is a uifigure
540 %error('savefig:uifigures
','Figures created
using the uifigure command or App Designer are not supported by
savefig. See %s
for details.
', hyperlink('https:
542 error(
'savefig:uifigure:multipleHandles',
'savefig only supports exporting a single uifigure handle at a time; array of handles is not currently supported.')
543 elseif ~any(strcmpi(fig.Type,{
'figure',
'axes'}))
544 error('
savefig:uifigure:notFigureOrAxes', '
savefig only supports exporting a uifigure or uiaxes handle; other handles of a uifigure are not currently supported.')
546 % fig is either a uifigure or uiaxes handle
547 isUiaxes = strcmpi(fig.Type,'axes');
549 % Label the specified axes so that we can
find it in the legacy figure
550 oldUserData = fig.UserData;
552 fig.UserData = tempStr;
555 % Create an invisible legacy figure at the same position/size as the uifigure
556 hNewFig = figure('Visible','off', 'Color',hFig.Color, ...
557 'Units',hFig.Units, 'Position',hFig.Position, ...
558 'MenuBar','none', 'ToolBar','none');
559 % Copy the uifigure contents onto the new invisible legacy figure
561 hChildren = allchild(hFig); %=uifig.Children;
562 copyobj(hChildren,hNewFig);
565 warning('
savefig:uifigure:controls', 'Some uifigure controls cannot be exported by
savefig and will not appear in the generated output.');
568 try fig.UserData = oldUserData; catch, end % restore axes UserData, if modified above
569 setappdata(hNewFig,'isExportFigCopy',true); % avoid endless recursion (issue
#375)
570 % Replace the uihandle in the input args with the legacy handle
572 % Locate the corresponding axes handle in the
new legacy figure
573 hAxes = findall(hNewFig,
'type',
'axes',
'UserData',tempStr);
574 if isempty(hAxes) % should never happen, check just in
case
575 hNewHandle = hNewFig; % export the figure instead of the axes
577 hNewHandle = hAxes; %
new axes handle found: use it instead of the uiaxes
580 hNewHandle = hNewFig;
582 varargin(cellfun(@(c)isequal(c,fig),varargin)) = {hNewHandle};
583 % Rerun
savefig on the legacy figure (with the replaced handle)
584 [imageData, alpha] =
savefig(varargin{:});
585 % Delete the temp legacy figure and bail out
586 try delete(hNewFig);
catch, end
589 % Clean up the temp legacy figure and report the error
590 try delete(hNewFig);
catch, end
596 % If toolbar button was requested, add it to the specified figure(s)
598 addToolbarButton(hFig, options);
601 % If menubar menu was requested, add it to the specified figure(s)
603 addMenubarMenu(hFig, options);
606 % If context-menu was requested, add it to the specified handle(s)
607 if options.contextmenu
608 addContextMenu(hFig, options);
611 % Isolate the subplot,
if it is one
612 cls = all(ismember(get(fig,
'Type'), {
'axes',
'uipanel'}));
614 % Given handles of one or more axes, so isolate them from the rest
617 % Check we have a figure
618 if ~isequal(get(fig,
'Type'),
'figure')
619 error('
savefig:BadHandle','
Handle must be that of a figure, axes or uipanel');
621 % Get the old InvertHardcopy mode
622 old_mode = get(fig, 'InvertHardcopy');
624 % from this point onward, fig is assured to be a figure handle
626 % Hack the font units where necessary (due to a font rendering bug in print?).
627 % This may not work perfectly in all cases.
628 % Also it can change the figure layout if reverted, so use a
copy.
629 magnify = options.magnify * options.aa_factor;
630 if isbitmap(options) && magnify ~= 1
631 fontu = findall(fig, 'FontUnits', 'normalized');
633 % Some normalized font units found
636 set(fig, 'Visible', 'off');
637 fontu = findall(fig, 'FontUnits', 'normalized');
640 set(fontu, 'FontUnits', 'points');
645 % MATLAB "feature": axes limits and tick marks can change when printing
646 Hlims = findall(fig, 'Type', 'axes');
648 % Record the old axes limit and tick modes
649 Xlims = make_cell(get(Hlims, 'XLimMode'));
650 Ylims = make_cell(get(Hlims, 'YLimMode'));
651 Zlims = make_cell(get(Hlims, 'ZLimMode'));
652 Xtick = make_cell(get(Hlims, 'XTickMode'));
653 Ytick = make_cell(get(Hlims, 'YTickMode'));
654 Ztick = make_cell(get(Hlims, 'ZTickMode'));
655 Xlabel = make_cell(get(Hlims, 'XTickLabelMode'));
656 Ylabel = make_cell(get(Hlims, 'YTickLabelMode'));
657 Zlabel = make_cell(get(Hlims, 'ZTickLabelMode'));
658 try % XTickLabelRotation etc. was added in R2021a
659 Xtkrot = make_cell(get(Hlims, 'XTickLabelRotationMode'));
660 Ytkrot = make_cell(get(Hlims, 'YTickLabelRotationMode'));
661 Ztkrot = make_cell(get(Hlims, 'ZTickLabelRotationMode'));
663 end % only in R2021a+
666 % Set all axes limit and tick modes to manual, so the limits and ticks can't change
667 % Fix MATLAB R2014b bug (issue
#34): plot markers are not displayed when ZLimMode='manual'
668 set_manual_axes_modes(Hlims,
'X');
669 set_manual_axes_modes(Hlims,
'Y');
671 set_manual_axes_modes(Hlims,
'Z');
674 % ignore - fix issue #4 (
using HG2 on R2014a and earlier)
677 % Fix issue #21 (bold TeX axes labels/titles in R2014b when exporting to EPS/PDF)
680 % Set the FontWeight of axes labels/titles to
'normal'
681 % Fix issue #69: set non-bold font only
if the
string contains symbols (\beta etc.)
682 % Issue #243: only set non-bold font (workaround
for issue #69) in R2015b or earlier
683 try isPreR2016a = verLessThan(
'matlab',
'8.7');
catch, isPreR2016a =
true; end
685 texLabels = findall(fig,
'type',
'text',
'FontWeight',
'bold');
686 symbolIdx = ~cellfun(
'isempty',strfind({texLabels.String},
'\'));
687 if ~isempty(symbolIdx)
688 set(texLabels(symbolIdx),
'FontWeight',
'normal');
690 warning(
'savefig:BoldTexLabels',
'Bold labels with Tex symbols converted into non-bold in savefig (fix for issue #69)');
699 % Fix issue #42: non-normalized annotations on HG1 (internal MATLAB bug)
700 annotationHandles = [];
703 annotationHandles = findall(fig,
'Type',
'hggroup',
'-and',
'-property',
'Units',
'-and',
'-not',
'Units',
'norm');
704 try % suggested by Jesús Pestana Puerta (jespestana) 30/9/2015
705 originalUnits = get(annotationHandles,
'Units');
706 set(annotationHandles,
'Units',
'norm');
711 % should never happen, but ignore in any
case - issue #50
714 % Fix issue #46: Ghostscript crash
if figure units <> pixels
715 pos = get(fig,
'Position'); % Fix issues #313, #314
716 oldFigUnits = get(fig,
'Units');
717 set(fig,
'Units',
'pixels');
718 pixelpos = get(fig,
'Position'); %=getpixelposition(fig);
720 tcol = get(fig,
'Color');
723 % Set to print exactly what is there
724 if options.invert_hardcopy
725 try set(fig,
'InvertHardcopy',
'off');
catch, end % fail silently in uifigures
729 switch options.renderer
731 renderer =
'-opengl';
733 renderer =
'-zbuffer';
735 renderer =
'-painters';
737 renderer =
'-opengl'; % Default
for bitmaps
748 % Export bitmap formats first
750 if abs(options.bb_padding) > 1
751 displaySuggestedWorkarounds =
false;
752 error(
'savefig:padding',
'For bitmap output (png,jpg,tif,bmp) the padding value (-p) must be between -1<p<1')
755 [A, tcol, alpha] = getFigImage(fig, magnify, renderer, options, pixelpos);
756 % Get the background colour
757 if options.transparent
758 if (options.png || options.alpha || options.gif || options.tif)
759 try %options.aa_factor < 4 % default, faster but lines are not anti-aliased
760 % If all pixels are indicated as opaque (i.e. something went wrong with the Java screen-capture)
761 isBgColor = A(:,:,1) == tcol(1) & ...
762 A(:,:,2) == tcol(2) & ...
764 % Set the bgcolor pixels to be fully-transparent
765 A(repmat(isBgColor,[1,1,3])) = 254; %=off-white % TODO: more memory efficient without repmat
766 alpha(isBgColor) = 0;
767 catch % older logic - much slower and causes figure flicker
768 if true % to fold the code below...
769 % Get out an alpha channel
770 % MATLAB "feature": black colorbar axes can change to white and vice versa!
771 hCB = findall(fig, 'Type','axes', 'Tag','Colorbar');
776 yCol = get(hCB, 'YColor');
777 xCol = get(hCB, 'XColor');
779 yCol = cell2mat(yCol);
780 xCol = cell2mat(xCol);
785 % MATLAB "feature": apparently figure size can change when changing
786 % colour in -nodisplay mode
787 % Set the background colour to black, and set size in case it was
789 set(fig, 'Color', 'k', 'Position', pos);
790 % Correct the colorbar axes colours
791 set(hCB(yCol==0), 'YColor', [0 0 0]);
792 set(hCB(xCol==0), 'XColor', [0 0 0]);
793 % Correct black axes color to off-black (issue
#249)
794 hAxes = findall(fig,
'Type',
'axes');
795 [hXs,hXrs] = fixBlackAxle(hAxes,
'XColor');
796 [hYs,hYrs] = fixBlackAxle(hAxes,
'YColor');
797 [hZs,hZrs] = fixBlackAxle(hAxes,
'ZColor');
799 % The following code might cause out-of-memory errors
803 % Downscale the image
804 B = downsize(single(B), options.aa_factor);
806 % This is more conservative in memory, but kills transparency (issue #58)
807 B = single(
print2array(fig, magnify/options.aa_factor, renderer));
810 % Set background to white (and set size)
811 set(fig,
'Color',
'w',
'Position', pos);
812 % Correct the colorbar axes colours
813 set(hCB(yCol==3),
'YColor', [1 1 1]);
814 set(hCB(xCol==3),
'XColor', [1 1 1]);
815 % Revert the black axes colors
816 set(hXs,
'XColor', [0,0,0]);
817 set(hYs,
'YColor', [0,0,0]);
818 set(hZs,
'ZColor', [0,0,0]);
819 set(hXrs,
'Color', [0,0,0]);
820 set(hYrs,
'Color', [0,0,0]);
821 set(hZrs,
'Color', [0,0,0]);
823 % The following code might cause out-of-memory errors
827 % Downscale the image
828 A = downsize(single(A), options.aa_factor);
830 % This is more conservative in memory, but kills transparency (issue #58)
831 A = single(
print2array(fig, magnify/options.aa_factor, renderer));
834 % Workaround
for issue #15
838 A = A(1:min(szA(1),szB(1)), 1:min(szA(2),szB(2)), :);
839 B = B(1:min(szA(1),szB(1)), 1:min(szA(2),szB(2)), :);
841 warning(
'savefig:bitmap:sizeMismatch',
'Problem detected by savefig generation of a bitmap image; the generated export may look bad. Try to reduce the figure size to fit the screen, or avoid using savefig''s -transparent option.')
844 % Compute the alpha
map
845 alpha = round(sum(B - A, 3)) / (255 * 3) + 1;
848 A = B ./ A(:,:,[1 1 1]);
854 warning('
savefig:unsupported:background','MATLAB cannot set transparency when exporting JPG/BMP image files (see imwrite function documentation)')
857 % Downscale the image if its size was increased (for anti-aliasing)
858 if size(A,1) > 1.1 * options.magnify * pixelpos(4) %1.1 to avoid edge-cases
859 % Downscale the image
860 A = downsize(A, options.aa_factor);
861 alpha = downsize(alpha, options.aa_factor);
863 % Crop the margins based on the bgcolor, if requested
865 %[alpha, v] =
crop_borders(alpha, 0, 1, options.crop_amounts);
866 %A = A(v(1):v(2),v(3):v(4),:);
867 [A, vA, vB] =
crop_borders(A, tcol, options.bb_padding, options.crop_amounts);
868 if ~any(isnan(vB)) % positive padding
869 sz = size(A); % Fix issue
#308
870 B = repmat(uint8(zeros(1,1,size(alpha,3))),sz([1,2])); % Fix issue #307 %=zeros(sz([1,2]),
'uint8');
871 B(vB(1):vB(2), vB(3):vB(4), :) = alpha(vA(1):vA(2), vA(3):vA(4), :); % ADDED BY OH
873 else % negative padding
874 alpha = alpha(vA(1):vA(2), vA(3):vA(4), :);
877 % Get the non-alpha image (presumably unneeded with Java-based screen-capture)
880 % Modify the intensity of the pixels
' RGB values based on their alpha transparency
881 % TODO: not sure that we want this with Java screen-capture values!
882 alph = alpha(:,:,ones(1, size(A, 3)));
883 A = uint8(single(A) .* alph + 255 * (1 - alph));
886 % Revert the figure properties back to their original values
887 set(fig, 'Units
',oldFigUnits, 'Position
',pos, 'Color
',tcol_orig);
888 % Check for greyscale images
889 if options.colourspace == 2
890 % Convert to greyscale
893 % Return only one channel for greyscale
894 A = check_greyscale(A);
896 % Change alpha from [0:255] uint8 => [0:1] single from here onward:
897 alpha = single(alpha) / 255;
898 % Clamp the image's min/max size (
if specified)
899 sz = size(A); sz(3:end) = []; %sz=size(A,1:2); %issue #374 & X. Xu PM
900 szNew = options.min_size;
902 szNew(isinf(szNew)) = 0;
903 szNew = round(max(sz,szNew,
'includenan'));
904 idxToCompare = ~isnan(szNew);
905 if ~isequal(sz(idxToCompare), szNew(idxToCompare))
906 A = imresize(A,szNew);
907 alpha = imresize(alpha,szNew);
910 szNew = options.max_size;
912 szNew(szNew <= 1) = inf;
913 szNew = round(min(sz,szNew,
'includenan'));
914 idxToCompare = ~isnan(szNew);
915 if ~isequal(sz(idxToCompare), szNew(idxToCompare))
916 A = imresize(A,szNew);
917 alpha = imresize(alpha,szNew);
920 % Clamp the image
's fixed size (if specified) - has to be done last!
921 szNew = options.fixed_size;
923 if szNew(1) < 0, szNew(1) = round(-sz(1)*szNew(1)/100); end
924 if szNew(2) < 0, szNew(2) = round(-sz(2)*szNew(2)/100); end
925 szNew(szNew <= 1) = inf;
926 szNew(isinf(szNew)) = sz(isinf(szNew)); % unchanged dimensions
927 idxToCompare = ~isnan(szNew);
928 if ~isequal(sz(idxToCompare), szNew(idxToCompare))
929 A = imresize(A,szNew);
930 alpha = imresize(alpha,szNew);
939 %alpha = ones(size(A, 1), size(A, 2), 'single
'); %=all pixels opaque
943 % Compute the resolution
944 res = options.magnify * get(0, 'ScreenPixelsPerInch
') / 25.4e-3;
946 [format_options, bitDepth] = getFormatOptions(options, 'png
'); %Issue #269
947 filename = [options.name '.png
'];
948 pngOptions = {filename, 'ResolutionUnit
','meter
', 'XResolution
',res, 'YResolution
',res, format_options{:}}; %#ok<CCAT>
949 if options.transparent % Fix issue #312: only use alpha channel if -transparent was requested
950 pngOptions = [pngOptions 'Alpha
',double(alpha)];
952 if ~isempty(bitDepth) && bitDepth < 16 && size(A,3) == 3
953 % BitDepth specification requires using a color-map
954 [img, map] = rgb2ind(A, 256);
955 imwrite(img, map, pngOptions{:});
957 imwrite(A, pngOptions{:});
959 if options.notify, notify(filename); end
962 filename = [options.name '.bmp
'];
963 imwrite(A, filename);
964 if options.notify, notify(filename); end
967 % Save jpeg with the specified quality
968 quality = options.quality;
972 format_options = getFormatOptions(options, 'jpg
'); %Issue #269
973 filename = [options.name '.jpg
'];
975 imwrite(A, filename, 'Mode
','lossless
', format_options{:});
977 imwrite(A, filename, 'Quality
',quality, format_options{:});
979 if options.notify, notify(filename); end
982 % Save tif images in cmyk if wanted (and possible)
983 if options.colourspace == 1 && size(A, 3) == 3
984 img = double(255 - A);
986 K_ = 255 ./ max(255 - K, 1);
987 C = (img(:,:,1) - K) .* K_;
988 M = (img(:,:,2) - K) .* K_;
989 Y = (img(:,:,3) - K) .* K_;
990 img = uint8(cat(3, C, M, Y, K));
995 resolution = options.magnify * get(0,'ScreenPixelsPerInch
');
996 filename = [options.name '.tif
'];
997 if options.transparent && any(alpha(:) < 1) && any(isBgColor(:))
998 % Need to use low-level Tiff library since imwrite/writetif doesn't support alpha channel
999 alpha8 = uint8(alpha*255);
1000 tag = [
'MATLAB ' version ' savefig v' num2str(currentVersion)];
1001 mode =
'w';
if options.append, mode =
'a'; end
1002 t = Tiff(filename,mode); %R2009a or newer
1004 t.setTag(
'ImageLength', size(img,1));
1005 t.setTag(
'ImageWidth', size(img,2));
1006 t.setTag(
'Photometric', Tiff.Photometric.RGB);
1007 try %issue #379 use Tiff.Compression.AdobeDeflate by
default
1008 compressionMode = Tiff.Compression.AdobeDeflate;
1010 warning off imageio:tiffmexutils:libtiffWarning %issue #379
1011 compressionMode = Tiff.Compression.Deflate;
1013 t.setTag(
'Compression', compressionMode);
1014 t.setTag(
'PlanarConfiguration', Tiff.PlanarConfiguration.Chunky);
1015 t.setTag(
'ExtraSamples', Tiff.ExtraSamples.AssociatedAlpha);
1016 t.setTag(
'ResolutionUnit', Tiff.ResolutionUnit.Inch);
1017 t.setTag(
'BitsPerSample', 8);
1018 t.setTag(
'SamplesPerPixel',size(img,3)+1); %+1=alpha channel
1019 t.setTag(
'XResolution', resolution);
1020 t.setTag(
'YResolution', resolution);
1021 t.setTag(
'Software', tag);
1022 t.write(cat(3,img,alpha8));
1025 % Use the builtin imwrite/writetif function
1026 append_mode = {
'overwrite',
'append'};
1027 mode = append_mode{options.append+1};
1028 format_options = getFormatOptions(options,
'tif'); %Issue #269
1029 imwrite(img, filename,
'Resolution',resolution,
'WriteMode',mode, format_options{:});
1031 if options.notify, notify(filename); end
1034 % TODO - merge contents with
im2gif.m
1035 % Convert to color-
map image required by GIF specification
1036 [img,
map] = rgb2ind(A, 256);
1037 %
Handle the
case of trying to append to non-existing GIF file
1038 % (imwrite() croaks when asked to append to a non-existing file)
1039 filename = [options.
name '.gif'];
1040 options.append = options.append && existFile(filename);
1041 % Set the default GIF options for imwrite()
1042 append_mode = {
'overwrite',
'append'};
1043 writeMode = append_mode{options.append+1};
1044 gifOptions = {
'WriteMode',writeMode};
1045 if options.transparent % only use alpha channel
if -transparent was requested
1047 mapVals = sum(round(
map*255).*exp,2);
1048 tcolVal = sum(round(
double(tcol)).*exp);
1049 alphaIdx =
find(mapVals==tcolVal,1);
1050 if isempty(alphaIdx) || alphaIdx <= 0, alphaIdx = 1; end
1051 % GIF color
index of uint8/logical images starts at 0, not 1
1052 if ~isfloat(img), alphaIdx = alphaIdx - 1; end
1053 gifOptions = [gifOptions,
'TransparentColor',alphaIdx, ...
1054 'DisposalMethod',
'restoreBG'];
1059 % LoopCount and BackgroundColor can only be specified in the
1060 % 1st GIF frame (not in append mode)
1061 % Set
default LoopCount=65535 to enable looping within MS Office
1062 gifOptions = [gifOptions,
'LoopCount',65535,
'BackgroundColor',alphaIdx];
1064 % Set GIF-specific options specified by the user (
if any)
1065 format_options = getFormatOptions(options,
'gif');
1066 gifOptions = [gifOptions, format_options{:}];
1068 imwrite(img,
map, filename, gifOptions{:});
1069 if options.notify, notify(filename); end
1073 % Now export the vector formats
which are based on EPS
1074 if isvector(options)
1075 hImages = findall(fig,
'type',
'image');
1076 % Set the
default renderer to painters
1077 if ~options.renderer
1078 %
Handle transparent patches
1079 hasTransparency = ~isempty(findall(fig,
'-property',
'FaceAlpha',
'-and',
'-not',
'FaceAlpha',1));
1081 % Alert
if trying to export transparent patches/areas to non-supported outputs (issues #94, #106, #108)
1083 % TODO - use transparency when exporting to PDF by not passing via
print2eps
1084 if options.pdf, format =
'PDF';
else, format =
'non-PNG formats'; end
1085 msg =
'savefig supports transparent patches/areas best in PNG output format. ';
1086 msg = sprintf(
'%s\nExporting figures with transparency to %s sometimes renders incorrectly. ', msg, format);
1087 msg = sprintf(
'%s\nIn such cases, try to use the', msg);
1088 if options.pdf && ~options.silent
1089 warning(
'savefig:transparency',
'%s print command: print(gcf, ''-dpdf'', ''%s.pdf'');', msg, options.name);
1090 elseif ~options.png && ~options.tif && ~options.silent % issue #168
1091 warning(
'savefig:transparency',
'%s ScreenCapture utility on the MATLAB File Exchange: http://bit.ly/1QFrBip', msg);
1093 elseif ~isempty(hImages)
1094 % Fix
for issue #230: use OpenGL renderer when exported image contains transparency
1095 for idx = 1 : numel(hImages)
1096 cdata = get(hImages(idx),
'CData');
1097 if any(isnan(cdata(:)))
1098 hasTransparency = true;
1103 hasPatches = ~isempty(findall(fig,'type','patch'));
1104 if hasTransparency || hasPatches
1105 % This is *MUCH* slower, but more accurate for patches and transparent annotations (issue
#39)
1106 renderer =
'-opengl';
1108 renderer =
'-painters';
1111 options.rendererStr = renderer; % fix
for issue #112
1112 % Generate some filenames
1113 tmp_nam = [tempname
'.eps'];
1115 % Ensure that the temp dir is writable (Javier Paredes 30/1/15)
1116 fid = fopen(tmp_nam,'w');
1120 pdf_nam_tmp = [tempname '.pdf'];
1122 % Temp dir is not writable, so use the user-specified folder
1123 [dummy,fname,fext] = fileparts(tmp_nam); %
#ok<ASGLU>
1124 fpath = fileparts(options.name);
1125 tmp_nam = fullfile(fpath,[fname fext]);
1126 pdf_nam_tmp = fullfile(fpath,[fname
'.pdf']);
1129 pdf_nam = [options.name
'.pdf'];
1130 try copyfile(pdf_nam, pdf_nam_tmp,
'f');
catch, end % fix
for issue #65
1132 pdf_nam = pdf_nam_tmp;
1134 % Generate the options
for print
1135 printArgs = {renderer};
1136 if ~isempty(options.resolution) % issue #241
1137 printArgs{end+1} = sprintf(
'-r%d', options.resolution);
1139 if options.colourspace == 1 % CMYK
1140 % Issue #33: due to internal bugs in MATLAB
's print() function, we can't use its -cmyk option
1141 %printArgs{end+1} =
'-cmyk';
1144 % Issue #56: due to internal bugs in MATLAB
's print() function, we can't use its internal cropping mechanism,
1146 %printArgs{end+1} =
'-loose';
1148 if any(strcmpi(varargin,
'-depsc'))
1149 % Issue #45: lines in image subplots are exported in invalid color.
1150 % The workaround is to use the -depsc parameter instead of the
default -depsc2
1151 printArgs{end+1} =
'-depsc';
1153 % Print to base EPS file (
if this fails, we cannot proceed further)
1155 % Remove background
if requested (issue #207)
1156 originalBgColor = get(fig,
'Color');
1157 [hXs, hXrs, hYs, hYrs, hZs, hZrs] = deal([]);
1158 if options.transparent %&& ~isequal(get(fig,
'Color'),
'none')
1159 if options.renderer == 1 && ~options.silent % OpenGL
1160 warning('
savefig:openglTransparentBG', '-opengl sometimes fails to produce transparent backgrounds; in such a case, try to use -painters instead');
1163 % Fix for issue
#207, #267 (corrected)
1164 set(fig,
'Color',
'none');
1166 % Correct black axes color to off-black (issue #249)
1167 hAxes = findall(fig,
'Type',
'axes');
1168 [hXs,hXrs] = fixBlackAxle(hAxes,
'XColor');
1169 [hYs,hYrs] = fixBlackAxle(hAxes,
'YColor');
1170 [hZs,hZrs] = fixBlackAxle(hAxes,
'ZColor');
1172 % Correct black titles to off-black
1174 hTitle = fixBlackText(hAxes,
'Title');
1175 try hCBs = getappdata(hAxes,
'LayoutPeers');
catch, hCBs=[]; end %issue #383
1176 hCBs = unique([findall(fig,
'tag',
'Colorbar'), hCBs]);
1177 hCbTxt = fixBlackText(hCBs,
'Title'); % issue #382
1180 print2eps(tmp_nam, fig, options, printArgs{:}); %winopen(tmp_nam)
1182 % Remove the background,
if desired
1183 if options.transparent %&& ~isequal(get(fig,
'Color'),
'none')
1184 eps_remove_background(tmp_nam, 1 +
using_hg2(fig));
1186 % Revert the black axles/titles colors
1187 set(hXs, 'XColor', [0,0,0]);
1188 set(hYs, 'YColor', [0,0,0]);
1189 set(hZs, 'ZColor', [0,0,0]);
1190 set(hXrs, 'Color', [0,0,0]);
1191 set(hYrs, 'Color', [0,0,0]);
1192 set(hZrs, 'Color', [0,0,0]);
1193 set(hTitle,'Color',[0,0,0]);
1194 set(hCbTxt,'Color',[0,0,0]);
1197 % Restore the figure's previous background color (if modified)
1198 try set(fig,'Color',originalBgColor); drawnow; catch, end
1199 % Fix colorspace to CMYK, if requested (workaround for issue
#33)
1200 if options.colourspace == 1 % CMYK
1201 % Issue #33: due to internal bugs in MATLAB
's print() function, we can't use its -cmyk option
1202 change_rgb_to_cmyk(tmp_nam);
1204 % Add a bookmark to the PDF
if desired
1206 fig_nam = get(fig,
'Name');
1207 if isempty(fig_nam) && ~options.silent
1208 warning(
'savefig:EmptyBookmark',
'Bookmark requested for figure with no name. Bookmark will be empty.');
1210 add_bookmark(tmp_nam, fig_nam);
1213 % Restore the figure
's previous background color (in case it was not already restored)
1214 try set(fig,'Color
',originalBgColor); drawnow; catch, end
1215 % Delete the temporary eps file - NOT! (Yair 3/3/2020)
1217 % Rethrow the EPS-generation error
1220 % Generate a PDF file from the base EPS
1221 % (if this fails, we can still proceed if only EPS was requested)
1223 %if existFile(pdf_nam_tmp), delete(pdf_nam_tmp); end %issue #369
1224 eps2pdf(tmp_nam, pdf_nam_tmp, 1, options.append, options.colourspace==2, options.quality, options.gs_options);
1225 % Ghostscript croaks on % chars in the output PDF file, so use tempname and then rename the file
1227 % Rename the file (except if it is already the same)
1228 % Abbie K's comment on the commit
for issue #179 (#commitcomment-20173476)
1229 if ~isequal(pdf_nam_tmp, pdf_nam)
1230 movefile(pdf_nam_tmp, pdf_nam,
'f');
1232 catch %movefile failed
1233 % Alert in
case of error creating output PDF file (issue #179)
1234 if existFile(pdf_nam_tmp)
1235 fpath = fileparts(pdf_nam);
1236 if ~isempty(fpath) && exist(fpath,
'dir')==0
1237 errMsg = [
'Could not create ' pdf_nam
' - folder "' fpath
'" does not exist'];
1238 else % output folder exists
1239 errMsg = [
'Could not create ' pdf_nam
' - perhaps you do not have write permissions, or the file is open in another application'];
1241 error(
'savefig:PDF:create',errMsg);
1242 else % impossible: movefile succeeded but still an error
1243 error(
'savefig:NoEPS',
'Could not generate intermediary PDF file from %s.',tmp_name);
1247 % If EPS export was requested, use base EPS file without passing through PDF
1249 eps_filename = [options.name
'.eps'];
1250 warning(
'savefig:EPSviaPDF',
'Could not generate intermediary PDF file, %s might be sub-optimal',eps_filename);
1251 movefile(tmp_nam,eps_filename,
'f');
1253 % If PDF export was requested, rethrow the error
1255 % Rethrow the PDF-generation error
1259 % Convert the PDF to EPS,
if EPS export was requested
1260 % (
if this fails, use the base EPS file, without going through PDF)
1261 if options.eps || options.linecaps
1263 % Generate an EPS from the PDF
using the
pdftops utility
1264 % since
pdftops can
't handle relative paths (e.g., '..\
'), use a temp file
1265 eps_nam_tmp = strrep(pdf_nam_tmp,'.pdf
','.eps
');
1267 pdf2eps(pdf_nam, eps_nam_tmp);
1268 catch % pdftops failed - use original eps file
1269 movefile(tmp_nam,eps_nam_tmp,'f
');
1272 % Issue #192: enable rounded line-caps
1274 fstrm = read_write_entire_textfile(eps_nam_tmp);
1275 fstrm = regexprep(fstrm, '[02] J
', '1 J
');
1276 read_write_entire_textfile(eps_nam_tmp, fstrm);
1278 eps2pdf(eps_nam_tmp, pdf_nam, 1, options.append, options.colourspace==2, options.quality, options.gs_options);
1282 % Move the EPS file from temp folder/name to target location
1284 filename = [options.name '.eps
'];
1285 movefile(eps_nam_tmp, filename, 'f
');
1286 if options.notify, notify(filename); end
1287 else % if options.pdf
1288 try delete(eps_nam_tmp); catch, end
1295 try delete(eps_nam_tmp); catch, end
1298 if ~options.pdf && existFile(pdf_nam)
1300 try delete(pdf_nam); catch, end
1303 % Delete the base EPS file if it still exists
1304 try if existFile(tmp_nam), delete(tmp_nam); end, catch, end
1305 % Notify user about the file export (if -notify option specified)
1306 if options.pdf && options.notify, filename=pdf_nam; notify(filename); end
1307 % Issue #206: warn if the figure contains an image
1308 if ~isempty(hImages) && strcmpi(renderer,'-opengl
') && ~options.silent % see addendum to issue #206
1309 warnMsg = ['exporting images to PDF/EPS may result in blurry images on some viewers.
' ...
1310 'If so,
try to change viewer, or increase the image
''s CData resolution, or use -opengl renderer, or export via the print function.
' ...
1311 'See
' hyperlink('https:
1312 warning(
'savefig:pdf_eps:blurry_image', warnMsg);
1318 filename = [options.name
'.svg'];
1319 % Adapted from Dan Joshea
's https://github.com/djoshea/matlab-save-figure :
1320 try %if ~verLessThan('matlab
', '8.4')
1321 % Try MATLAB
's built-in svg engine (from Batik Graphics2D for java)
1322 set(fig,'Units
','pixels
'); % All data in the svg-file is saved in pixels
1323 printArgs = {renderer};
1324 if ~isempty(options.resolution)
1325 printArgs{end+1} = sprintf('-r%d
', options.resolution);
1328 print(fig, '-dsvg
', printArgs{:}, filename);
1330 % built-in print() failed, try saveas()
1331 % Note: saveas() currently just calls print(fig,filename,'-dsvg
')
1332 % so since print() failed, saveas() will probably also fail
1333 saveas(fig, filename);
1336 warning('savefig:SVG:print
', 'savefig used MATLAB
''s built-in SVG output engine. Better results may be gotten via the fig2svg utility (https:
1338 catch %
else % built-in print()/saveas() failed - maybe an old MATLAB
release (no -dsvg)
1339 % Try using the fig2svg/plot2svg utilities
1342 fig2svg(filename, fig); %https:
1344 plot2svg(filename, fig); %https:
1346 warning('
savefig:SVG:plot2svg', '
savefig used the plot2svg utility for SVG output. Better results may be gotten via the fig2svg utility (https:
1350 filename = strrep(filename,'export_fig_out','filename');
1351 msg = ['SVG output is not supported for your figure: ' err.message '\n' ...
1352 'Try one of the following alternatives:\n' ...
1353 ' 1. saveas(gcf,''' filename ''')\n' ...
1354 ' 2. fig2svg utility: https:
1355 ' 3.
savefig to EPS/PDF, then convert to SVG using non-MATLAB tools\n'];
1356 error('
savefig:SVG:error',msg);
1359 % SVG output was successful if we reached this point
1360 % Add warning about unsupported
savefig options with SVG output
1361 if ~options.silent && (any(~isnan(options.crop_amounts)) || any(options.bb_padding))
1362 warning('
savefig:SVG:options', '
savefig''s SVG output does not [currently] support cropping/padding.');
1365 % Fix the generated SVG file, based on Cris Luengo's SVG_FIX_VIEWBOX:
1370 % Fix fonts
#1: 'SansSerif' doesn't work on my browser, the correct CSS is 'sans-serif'
1371 s = regexprep(s,
'font-family:SansSerif;|font-family:''SansSerif'';',
'font-family:''sans-serif'';');
1372 % Fix fonts #1: The document-wide
default font is
'Dialog'. What is
this anyway?
1373 s = regexprep(s,
'font-family:''Dialog'';',
'font-family:''sans-serif'';');
1374 % Replace
'width="xxx" height="yyy"' with
'width="100%" viewBox="0 0 xxx yyy"'
1375 t = regexp(s,
'<svg.* width="(?<width>[0-9]*)" height="(?<height>[0-9]*)"',
'names');
1377 relativeWidth = 100; %TODO - user-settable via input parameter?
1378 s = regexprep(s,
'(?<=<svg[^\n]*) width="[0-9]*" height="[0-9]*"',sprintf(
' width="%d\\%%" viewBox="0 0 %s %s"',relativeWidth,t.width,t.height));
1380 % Write updated SVG file
1383 % never mind - ignore
1385 if options.notify, notify(filename); end
1391 anythingChanged =
false;
1392 %
Handle transparent bgcolor request
1393 if options.transparent && ~isequal(tcol_orig,
'none')
1394 anythingChanged = true;
1395 set(fig, 'Color','none');
1398 isDefaultMag = options.resolution==864 && options.magnify==1;
1400 warning('
savefig:EMF:NotWindows', 'EMF is only supported on Windows; exporting to EMF format on this machine may result in unexpected behavior.');
1401 elseif isequal(renderer,'-painters') && ~isDefaultMag
1402 warning('
savefig:EMF:Painters', '
savefig -r and -m options are ignored for EMF export using the -painters renderer.');
1403 elseif ~isDefaultMag &&
abs(get(0,'ScreenPixelsPerInch')*options.magnify - options.resolution) > 1e-6
1404 warning('
savefig:EMF:Magnify', '
savefig -m option is ignored for EMF export.');
1406 if ~isequal(options.bb_padding,0) || ~isempty(options.quality)
1407 warning('
savefig:EMF:Options', '
savefig cropping, padding and quality options are ignored for EMF export.');
1409 if ~anythingChanged && ~isdeployed
1410 warning('
savefig:EMF:print', 'For a figure without background transparency,
savefig uses MATLAB''s built-in print(''-dmeta'') function without any extra processing, so try using it directly.');
1413 printArgs = {renderer};
1414 if ~isempty(options.resolution)
1415 printArgs{end+1} = sprintf(
'-r%d', options.resolution);
1417 filename = [options.name
'.emf'];
1418 print(fig,
'-dmeta', printArgs{:}, filename);
1419 if options.notify, notify(filename); end
1420 catch err % built-in print() failed - maybe an old MATLAB
release (no -dsvg)
1421 msg = ['EMF output is not supported: ' err.message '\n' ...
1422 'Try to use
savefig with other formats, such as PDF or EPS.\n'];
1423 error('
savefig:EMF:error',msg);
1427 % Revert the figure or close it (if requested)
1428 if cls || options.closeFig
1429 % Close the created figure
1432 % Reset the hardcopy mode
1433 try set(fig, 'InvertHardcopy', old_mode); catch, end % fail silently in uifigures
1434 % Reset the axes limit and tick modes
1435 for a = 1:numel(Hlims)
1437 set(Hlims(a), 'XLimMode', Xlims{a},
'YLimMode', Ylims{a},
'ZLimMode', Zlims{a},...
1438 'XTickMode', Xtick{a},
'YTickMode', Ytick{a},
'ZTickMode', Ztick{a},...
1439 'XTickLabelMode', Xlabel{a},
'YTickLabelMode', Ylabel{a},
'ZTickLabelMode', Zlabel{a});
1440 try % only in R2021a+
1441 set(Hlims(a),
'XTickLabelRotationMode', Xtkrot{a}, ...
1442 'YTickLabelRotationMode', Ytkrot{a}, ...
1443 'ZTickLabelRotationMode', Ztkrot{a});
1445 % ignore - possibly R2020b or earlier
1448 % ignore - fix issue #4 (
using HG2 on R2014a and earlier)
1451 % Revert the tex-labels font weights
1452 try set(texLabels,
'FontWeight',
'bold');
catch, end
1453 % Revert annotation units
1454 for handleIdx = 1 : numel(annotationHandles)
1456 oldUnits = originalUnits{handleIdx};
1458 oldUnits = originalUnits;
1460 try set(annotationHandles(handleIdx),
'Units',oldUnits);
catch, end
1462 % Revert figure properties in
case they were changed
1463 try set(fig,
'Units',oldFigUnits,
'Position',pos,
'Color',tcol_orig);
catch, end
1466 % Output to clipboard (
if requested)
1467 if options.clipboard
1468 % Use Java clipboard by
default
1469 if strcmpi(options.clipformat,
'image')
1470 % Save the image in the system clipboard
1471 % credit: Jiro Doke
's IMCLIPBOARD: http://www.mathworks.com/matlabcentral/fileexchange/28708-imclipboard
1473 error(javachk('awt
', 'savefig -clipboard output
'));
1476 warning('savefig:clipboardJava
', 'savefig -clipboard output failed:
requires Java to work
');
1481 % Import necessary Java classes
1482 import java.awt.Toolkit %#ok<SIMPT>
1483 import java.awt.image.BufferedImage %#ok<SIMPT>
1484 import java.awt.datatransfer.DataFlavor %#ok<SIMPT>
1486 % Get System Clipboard object (java.awt.Toolkit)
1487 cb = Toolkit.getDefaultToolkit.getSystemClipboard; % can't use () in ML6!
1491 % Obtain the directory of the executable (or of the M-file if not deployed)
1492 %javaaddpath(fileparts(
which(mfilename)), '-end');
1493 if isdeployed % Stand-alone mode
1494 [status, result] = system('path'); %
#ok<ASGLU>
1495 MatLabFilePath = char(regexpi(result,
'Path=(.*?);',
'tokens',
'once'));
1497 MatLabFilePath = fileparts(mfilename(
'fullpath'));
1499 javaaddpath(MatLabFilePath,
'-end');
1503 ht = size(imageData, 1);
1504 wd = size(imageData, 2);
1506 % Convert to Blue-Green-Red format
1508 imageData2 = imageData(:, :, [3 2 1]);
1510 % Probably gray-scaled image (2D, without the 3rd [RGB] dimension)
1511 imageData2 = imageData(:, :, [1 1 1]);
1514 % Convert to 3xWxH format
1515 imageData2 = permute(imageData2, [3, 2, 1]);
1517 % Append Alpha data (unused - transparency is not supported in clipboard
copy)
1518 alphaData2 = uint8(permute(255*alpha,[3,2,1])); %=255*ones(1,wd,ht,
'uint8')
1519 imageData2 = cat(1, imageData2, alphaData2);
1521 % Create image buffer
1523 % ^^^^ a Java screencaptured BufferedImage into RGBA and must
1524 % use TYPE_INT_RGB for this, here we do the reverse and
1525 % must use TYPE_INT_ARGB to preserve the alpha channel.
1526 imBuffer = BufferedImage(wd, ht, BufferedImage.TYPE_INT_ARGB);
1527 imBuffer.setRGB(0, 0, wd, ht, typecast(imageData2(:), 'int32'), 0, wd);
1532 % Set clipboard content to the image
1533 cb.setContents(imSelection, []);
1536 warning('
savefig:clipboardFailed', '
savefig -clipboard output failed: %s', lasterr); %
#ok<LERR>
1539 else % use one of print()
's builtin clipboard formats
1540 % Remove background if requested (EMF format only)
1541 if options.transparent && strcmpi(options.clipformat,'meta
')
1542 % Set figure bgcolor to none
1543 originalBgColor = get(fig, 'Color
');
1544 set(fig,'Color
','none
');
1546 % Set axes bgcolor to none
1547 hAxes = findall(fig, 'Type
','axes
');
1548 originalAxColor = get(hAxes, 'Color
');
1549 set(hAxes,'Color
','none
');
1551 drawnow; %repaint before export
1554 % Call print() to create the clipboard output
1555 clipformat = ['-d
' options.clipformat];
1556 printArgs = {renderer};
1557 if ~isempty(options.resolution)
1558 printArgs{end+1} = sprintf('-r%d
', options.resolution);
1560 print(fig, '-clipboard
', clipformat, printArgs{:});
1562 % Restore the original background color
1563 try set(fig, 'Color
',originalBgColor); catch, end
1564 try set(hAxes, 'Color
',originalAxColor); catch, end
1567 %if options.notify, notify('system clipboard
'); end
1570 % Delete the output file if unchanged from the default name ('export_fig_out.png
')
1571 % and clipboard/toolbar/menubar/contextmenu were requested
1572 if options.clipboard || options.toolbar || options.menubar || options.contextmenu
1573 if strcmpi(options.name,'export_fig_out
')
1575 fileInfo = dir('export_fig_out.png
');
1576 if ~isempty(fileInfo)
1577 timediff = now - fileInfo.datenum;
1578 ONE_SEC = 1/24/60/60;
1579 if timediff < ONE_SEC
1580 delete('export_fig_out.png
');
1589 % Notify user by popup, if -notify option was specified
1590 if options.notify && exported_files > 0
1591 % TODO don't notify when exporting to file just
for clipboard output
1592 folder = fileparts(filename);
1593 if isempty(folder), folder = pwd; end
1595 if exported_files == 1
1596 msg = [
'Exported screenshot image to ' filename];
1597 if ispc, options = {
'Open image',
'Open folder'}; end
1599 msg = sprintf(
'Exported %d screenshot images to %s', exported_files, folder);
1600 if ispc, options = {
'Open folder'}; end
1602 answer = questdlg(msg,
'Screenshot export',options{:},
'OK',
'OK');
1603 drawnow; pause(0.05); % avoid MATLAB hang
1605 case 'Open image', winopen(filename);
1606 case 'Open folder', winopen(folder);
1607 otherwise %
do nothing
1611 % Don
't output the data to console unless requested
1613 clear imageData alpha
1616 % Revert warnings state
1619 % Revert warnings state
1621 % Revert figure properties in case they were changed
1622 try set(fig,'Units
',oldFigUnits, 'Position
',pos, 'Color
',tcol_orig); catch, end
1623 % Display possible workarounds before the error message
1624 if ~isempty(regexpi(err.message,'setopacityalpha
')) %#ok<RGXPI>
1625 % Alert the user that transparency is not supported (issue #285)
1627 [unused, msg] = ghostscript('-v
'); %#ok<ASGLU>
1628 verStr = regexprep(msg, '.*hostscript ([\d.]+).*
', '$1
');
1629 if isempty(verStr) || any(verStr==' ')
1632 verStr = [' (
' verStr ')
'];
1639 errMsg = sprintf(
'Transparancy is not supported by your savefig (%s) and Ghostscript%s versions. \nInstall GS version 9.28 or earlier to use transparency (%s).', num2str(currentVersion), verStr, urlStr);
1640 %fprintf(2,
'%s\n',errMsg);
1641 error(
'savefig:setopacityalpha',errMsg) %#ok<SPERR>
1642 elseif displaySuggestedWorkarounds && ~strcmpi(err.message,
'savefig error')
1643 isNewerVersionAvailable = checkForNewerVersion(currentVersion); % alert if a newer
version exists
1644 if isempty(regexpi(err.message,'Ghostscript')) %
#ok<RGXPI>
1645 fprintf(2,
'savefig error. ');
1647 fprintf(2,
'Please ensure:\n');
1649 fprintf(2,
' * that the function you used (%s.m) version %s is from the expected location\n', mfilename(
'fullpath'), num2str(currentVersion));
1650 paths =
which(mfilename,
'-all');
1651 if iscell(paths) && numel(paths) > 1
1652 fprintf(2,
' (you appear to have %s of savefig installed)\n',
hyperlink(
'matlab:which savefig -all',
'multiple versions'));
1655 if isNewerVersionAvailable
1656 fprintf(2,
' * and that you are using the %s of savefig (you are not: run %s to update it)\n', ...
1657 hyperlink(
'https://github.com/altmany/savefig/archive/master.zip',
'latest version'), ...
1658 hyperlink(
'matlab:savefig(''-update'')',
'savefig(''-update'')'));
1660 fprintf(2,
' * and that you did not made a mistake in savefig''s %s\n',
hyperlink(
'matlab:help savefig',
'expected input arguments'));
1661 if isvector(options) % EPS/PDF require ghostscipt
1663 url =
'http://pages.uoregon.edu/koch';
1665 url =
'http://ghostscript.com';
1669 if ispc % winopen only works on Windows
1670 fpath_link =
hyperlink([
'matlab:winopen(''' fileparts(fpath)
''')
'], fpath);
1672 fprintf(2, ' * and that %s is properly installed in %s\n
', ...
1674 if isempty(strtrim(char(fpath)))
1675 selectUtilityPath('Ghostscript
',url,'Exporting to vector format (EPS, PDF etc.)
');
1679 try % EPS require pdftops
1684 if ispc % winopen only works on Windows
1685 fpath_link =
hyperlink([
'matlab:winopen(''' fileparts(fpath)
''')
'], fpath);
1687 fprintf(2, ' * and that %s is properly installed in %s\n
', ...
1688 hyperlink(url,'pdftops'), fpath_link);
1690 selectUtilityPath('pdftops',url,'Exporting to EPS format
');
1695 % ignore - probably an error in parse_args
1698 % Alert per issue #149
1699 if ~strncmpi(get(0,'Units
'),'pixel
',5)
1700 fprintf(2, ' * or
try to set groot
''s Units
property back to its
default value of
''pixels
'' (%s)\n
', hyperlink('https:
1703 % ignore - maybe an old MAtlab
release
1705 fprintf(2,
'\nIf the problem persists, then please %s.\n',
hyperlink(
'https://github.com/altmany/savefig/issues',
'report a new issue'));
1706 if existFile(tmp_nam)
1707 fprintf(2,
'In your report, please upload the problematic EPS file: %s (you can then delete this file).\n', tmp_nam);
1715 % Notify user about a successful file export
1716 function notify(filename)
1717 fprintf(
'Exported screenshot image to %s\n', filename)
1718 exported_files = exported_files + 1;
1725function isOk = selectUtilityPath(utilName,url,msg)
1727 msg = [msg
' requires the ' utilName
' utility from ' url];
1728 fprintf(2,
'\n%s\n',msg);
1730 answer = questdlg(msg,utilName,
'Use local installation',
'Go to website',
'Cancel',
'Cancel');
1731 drawnow; pause(0.01); % avoid MATLAB hang
1732 switch strtok(
char(answer))
1733 case 'Go', web(url,
'-browser');
1735 filter = {
'*.*',
'Executable files'};
1736 title = [
'Specify the ' utilName
' executable'];
1737 [fPath,fName,fExt] = uigetfile(filter,title);
1738 if ~ischar(fPath),
return, end
1739 fName = fullfile(fPath,[fName,fExt]);
1740 isOk = exist(fName,
'file') &&
user_string(
'ghostscript',fName);
1746function options = default_options()
1747 % Default options used by
savefig
1748 options =
struct(...
1751 'crop_amounts', nan(1,4), ... %
auto-crop all 4 image sides
1752 'transparent',
false, ...
1753 'renderer', 0, ... % 0:
default, 1: OpenGL, 2: ZBuffer, 3: Painters
1763 'clipboard',
false, ...
1764 'clipformat',
'image', ...
1765 'colourspace', 0, ... % 0: RGB/gray, 1: CMYK, 2: gray
1766 'append',
false, ...
1770 'bb_padding', 0, ...
1772 'resolution', [], ...
1773 'bookmark',
false, ...
1774 'closeFig',
false, ...
1776 'update',
false, ...
1777 'version',
false, ...
1778 'fontswap',
true, ...
1779 'font_space',
'', ...
1780 'linecaps',
false, ...
1781 'invert_hardcopy',
true, ...
1782 'format_options',
struct, ...
1783 'preserve_size',
false, ...
1784 'silent',
false, ...
1785 'notify',
false, ...
1786 'regexprep', [], ...
1787 'toolbar',
false, ...
1788 'menubar',
false, ...
1789 'contextmenu',
false, ...
1792 'fixed_size', [], ...
1793 'gs_options', {{}}, ...
1794 'propagatedOpts', {{}});
1797function [fig, options] =
parse_args(nout, fig, argNames, varargin)
1798 % Parse the input arguments
1800 % Convert strings => chars
1801 varargin = cellfun(@
str2char,varargin,
'un',
false);
1804 native =
false; % Set resolution to native of an image
1805 defaultOptions = default_options();
1806 options = defaultOptions;
1807 options.im = (nout == 1); % user requested imageData output
1808 options.alpha = (nout == 2); % user requested alpha output
1809 options.handleName =
''; %
default handle
name
1810 wasOutputRequested =
false;
1812 % Go through the other arguments
1814 for a = 1:nargin-3 % only process varargin, no other
parse_args() arguments
1816 skipNext = skipNext-1;
1819 thisArg = varargin{a};
1820 if isempty(thisArg) % skip empty args
1822 elseif ~ischar(thisArg)
1823 if ~all(ishandle(thisArg))
1824 error(
'savefig:InvalidHandle',
'invalid figure handle specified to savefig');
1827 options.handleName = argNames{a};
1829 else %
if ischar(thisArg) && ~isempty(thisArg)
1830 if thisArg(1) ==
'-'
1831 addToOptionsStr =
true;
1832 switch lower(thisArg(2:end))
1834 options.crop =
false;
1835 options.crop_amounts = [0,0,0,0];
1836 case {
'trans',
'transparent'}
1837 options.transparent =
true;
1839 options.renderer = 1;
1841 options.renderer = 2;
1843 options.renderer = 3;
1846 addToOptionsStr =
false;
1847 wasOutputRequested =
true;
1850 addToOptionsStr =
false;
1851 wasOutputRequested =
true;
1854 addToOptionsStr =
false;
1855 wasOutputRequested =
true;
1858 addToOptionsStr =
false;
1859 wasOutputRequested =
true;
1862 addToOptionsStr =
false;
1863 wasOutputRequested =
true;
1864 case {
'tif',
'tiff'}
1866 addToOptionsStr =
false;
1867 wasOutputRequested =
true;
1868 case {
'jpg',
'jpeg'}
1870 addToOptionsStr =
false;
1871 wasOutputRequested =
true;
1874 addToOptionsStr =
false;
1875 wasOutputRequested =
true;
1878 addToOptionsStr =
false;
1879 wasOutputRequested =
true;
1881 options.colourspace = 0;
1883 options.colourspace = 1;
1884 case {
'gray',
'grey'}
1885 options.colourspace = 2;
1886 case {
'a1',
'a2',
'a3',
'a4'}
1887 options.aa_factor = str2double(thisArg(3));
1889 options.append =
true;
1891 options.bookmark =
true;
1894 case {
'clipboard',
'clipboard:image'}
1895 options.clipboard =
true;
1896 options.clipformat =
'image';
1897 options.im =
true; %ensure that imageData is created
1898 options.alpha =
true;
1899 addToOptionsStr =
false;
1900 case 'clipboard:bitmap'
1901 options.clipboard =
true;
1902 options.clipformat =
'bitmap';
1903 addToOptionsStr =
false;
1904 case {
'clipboard:emf',
'clipboard:meta'}
1905 options.clipboard =
true;
1906 options.clipformat =
'meta';
1907 addToOptionsStr =
false;
1908 case 'clipboard:pdf'
1909 options.clipboard =
true;
1910 options.clipformat =
'pdf';
1911 addToOptionsStr =
false;
1913 warning(
'The update feature of the savefig package is deliberately disabled in the ParaMonte library.');
1914 %updateInstalledVersion();
1915 fig = -1; % silent bail-out
1916 addToOptionsStr =
false;
1918 options.version =
true;
1919 return % ignore any additional args
1921 options.fontswap =
false;
1923 options.font_space = varargin{a+1};
1926 options.linecaps =
true;
1928 options.invert_hardcopy =
false;
1929 case 'preserve_size'
1930 options.preserve_size =
true;
1932 % Issue #269: format-specific options
1933 inputOptions = varargin{a+1};
1934 %options.format_options = inputOptions;
1935 if isempty(inputOptions),
continue, end
1936 if iscell(inputOptions)
1937 fields = inputOptions(1:2:end)
';
1938 values = inputOptions(2:2:end)';
1939 options.format_options.all = cell2struct(values, fields);
1940 elseif isstruct(inputOptions)
1941 formats = fieldnames(inputOptions(1));
1942 for idx = 1 : numel(formats)
1943 optionsStruct = inputOptions.(formats{idx});
1944 %optionsCells = [fieldnames(optionsStruct) struct2cell(optionsStruct)]
';
1945 formatName = regexprep(lower(formats{idx}),{'tiff
','jpeg
'},{'tif
','jpg
'});
1946 options.format_options.(formatName) = optionsStruct; %=optionsCells(:)';
1949 warning(
'savefig:options',
'savefig -options argument is not in the expected format - ignored');
1953 options.silent =
true;
1955 options.notify =
true;
1957 options.regexprep = varargin(a+1:a+2);
1960 options.toolbar =
true;
1961 addToOptionsStr =
false;
1963 options.menubar =
true;
1964 addToOptionsStr =
false;
1966 options.contextmenu =
true;
1967 addToOptionsStr =
false;
1971 metadata = varargin{a+1};
1972 if isstruct(metadata)
1973 metadata = [fieldnames(metadata),struct2cell(metadata)]
';
1974 elseif ~iscell(metadata) || ~ischar(metadata{1}) || mod(length(metadata),2)==1
1975 error('savefig:BadOptionValue
','savefig metadata must be a
struct or cell-array of
name-value pairs');
1977 metadata = cellfun(@num2str,metadata(:)
','uniform
',0);
1978 str = sprintf(' /%s (%s)
', metadata{:});
1979 options.gs_options{end+1} = ['-c
"[' str ' /DOCINFO pdfmark"'];
1984 if strcmpi(thisArg(1:2),'-d
')
1985 thisArg(2) = 'd
'; % ensure lowercase 'd
'
1986 options.gs_options{end+1} = thisArg;
1987 elseif strcmpi(thisArg(1:2),'-c
')
1988 if strncmpi(thisArg,'-clipboard:
',11)
1990 error('savefig:BadOptionValue
','option
''%s
'' cannot be parsed: only image, bitmap, emf and pdf formats are supported
',thisArg);
1992 if numel(thisArg)==2
1994 vals = str2num(varargin{a+1}); %#ok<ST2NM>
1996 vals = str2num(thisArg(3:end)); %#ok<ST2NM>
2000 error('savefig:BadOptionValue
','option -c cannot be parsed: must be a 4-element numeric vector
');
2002 options.crop_amounts = vals;
2003 options.crop = true;
2004 elseif thisArg(1)=='-
' && any(thisArg(2)=='nNxXsS
') %issue #315
2005 if numel(thisArg)==2
2007 valsStr = varargin{a+1};
2009 valsStr = thisArg(3:end);
2011 numVals = sum(valsStr==',
');
2012 if any(valsStr=='-
')
2014 error('savefig:BadOptionValue
','option -%s must be a positive scalar value or 2-value vector
',thisArg(2));
2017 error('savefig:BadOptionValue
','option -%s must be a scalar value or 2-value vector
',thisArg(2));
2018 elseif numVals == 0 % -s100 => -s100,100
2019 valsStr = [valsStr ',
' valsStr]; %#ok<AGROW>
2021 valsStr = regexprep(valsStr,'([\d\.]*)%
','-$1
'); % -s95%,100 => [-95,100]
2022 vals = str2num(valsStr); %#ok<ST2NM>
2023 if any(vals < 0) && lower(thisArg(2)) ~= 's
'
2025 error('savefig:BadOptionValue
','option -%s cannot use percentage values
',thisArg(2));
2027 switch lower(thisArg(2))
2028 case 'n
', options.min_size = vals;
2029 case 'x
', options.max_size = vals;
2030 case 's
', options.fixed_size = vals;
2032 else % scalar parameter value
2033 val = str2double(regexpi(thisArg, '(?<=-([mrqp]))-?\d*.?\d+
', 'match
'));
2034 if isempty(val) || isnan(val)
2035 % Issue #51: improved processing of input args (accept space between param name & value)
2036 val = str2double(varargin{a+1});
2037 if isscalar(val) && ~isnan(val)
2041 if ~isscalar(val) || isnan(val)
2043 error('savefig:BadOptionValue
','option %s is not recognised or cannot be parsed
', thisArg);
2045 switch lower(thisArg(2))
2047 % Magnification may never be negative
2050 error('savefig:BadMagnification
','Bad magnification value: %g (must be positive)
', val);
2052 options.magnify = val;
2054 options.resolution = val;
2056 options.quality = max(val, 0);
2058 options.bb_padding = val;
2062 % We might have reached here by raising an intentional error
2063 if wasError % intentional raise
2065 else % unintentional
2066 error('savefig:BadOption
',['Unrecognized
savefig input option:
''' thisArg
'''']);
2071 options.propagatedOpts{end+1} = thisArg;
2073 options.propagatedOpts{end+1} = varargin{a+1};
2077 % test for case of figure name rather than export filename
2079 if isempty(options.handleName)
2081 if strncmpi(thisArg,'Figure',6)
2082 [~,~,~,~,e] = regexp(thisArg,'figure\s*(\d+)\s*(:\s*(.*))?
','ignorecase
');
2083 figNumber = str2double(e{1}{1});
2084 figName = regexprep(e{1}{2},':\s*
','');
2085 findProps = {'Number
',figNumber};
2086 if ~isempty(figName)
2087 findProps = [findProps,'Name
',figName]; %#ok<AGROW>
2090 findProps = {'Name
',thisArg};
2092 possibleFig = findall(0,'-depth
',1,'Type
','figure
',findProps{:});
2093 if ~isempty(possibleFig)
2094 fig = possibleFig(1); % return the 1st figure found
2095 if ~strcmpi(options.name, defaultOptions.name)
2096 continue % export fname was already specified
2098 isFigName = true; %use figure name as export fname
2102 % ignore - treat as export filename, not figure name
2105 % parse the input as a filename, alert if requested folder does not exist
2106 [p, options.name, ext] = fileparts(thisArg);
2107 if ~isempty(p) % export folder name/path was specified
2108 % Issue #221: alert if the requested folder does not exist
2110 options.name = fullfile(p, options.name); %without ext
2112 error('savefig:BadPath
','Folder %s does not exist, nor is it the
name of any active figure!
',p);
2114 % specified a figure name so ignore the bad folder part
2117 switch lower(ext(2:end))
2118 case {'tif
', 'tiff
','jpg
', 'jpeg
','png
','bmp
','eps
','emf
','pdf
','svg
','gif
'}
2119 options = setOptionsFormat(options, ext);
2120 wasOutputRequested = true;
2122 % If no open figure, then load the specified .fig file and continue
2123 figFilename = thisArg;
2125 fig = openfig(figFilename,'invisible
');
2127 options.closeFig = true;
2128 options.handleName = ['openfig(
''' figFilename
''')
'];
2130 % save the current figure as the specified .fig file and exit
2131 saveas(fig(1),figFilename);
2136 options.name = thisArg;
2137 wasOutputRequested = true;
2143 % Quick bail-out if no figure found
2144 if isempty(fig), return; end
2146 % Do border padding with repsect to a cropped image
2147 if options.bb_padding
2148 options.crop = true;
2151 % Set default anti-aliasing now we know the renderer
2152 try isAA = strcmp(get(ancestor(fig, 'figure
'), 'GraphicsSmoothing
'), 'on
'); catch, isAA = false; end
2154 if options.aa_factor > 1 && ~options.silent
2155 warning('savefig:AntiAliasing
','You requested anti-aliased
savefig output of a figure that is already anti-aliased - your -a option in
savefig is ignored.
')
2157 options.aa_factor = 1; % ignore -a option when the figure is already anti-aliased (HG2)
2158 elseif options.aa_factor == 0 % default
2159 %options.aa_factor = 1 + 2 * (~(using_hg2(fig) && isAA) | (options.renderer == 3));
2160 options.aa_factor = 1 + 2 * (~using_hg2(fig)); % =1 in HG2, =3 in HG1
2162 if options.aa_factor > 1 && ~isAA && using_hg2(fig) && ~options.silent
2163 warning('savefig:AntiAliasing
','You requested anti-aliased
savefig output of an aliased figure (
''GraphicsSmoothing
''=
''off
''). You will see better results
if you set your figure
''s GraphicsSmoothing property to
''on
'' before calling
savefig.
')
2166 % Use the figure's FileName property as the
default export filename
2167 if isempty(options.name)
2168 options.name = get(ancestor(fig(1),
'figure'),
'FileName'); %fix issue #372
2169 options.name = char(options.name); %fix issue #381
2170 options.name = regexprep(options.name,
'[*?"<>|]+',
'-'); %remove illegal filename chars, but not folder seperators!
2171 if isempty(options.name)
2172 % No FileName
property specified
for the figure, use
'export_fig_out'
2173 options.name =
'export_fig_out';
2174 elseif wasOutputRequested %
if no output requested, don
't force it!
2175 % Ensure the filepath is valid
2176 [p, options.name, ext] = fileparts(options.name);
2177 options = setOptionsFormat(options, ext);
2178 if ~isempty(p) % export folder name/path was specified
2180 options.name = fullfile(p, options.name); %without ext
2181 else % only warn, don't error
2182 warning(
'savefig:BadPath',
'Folder %s does not exist - exporting %s%s to current folder',p,options.name,ext);
2188 % Convert user dir
'~' to full path
2189 if numel(options.name) > 2 && options.name(1) ==
'~' && (options.name(2) ==
'/' || options.name(2) ==
'\')
2190 options.name = fullfile(
char(java.lang.System.getProperty(
'user.home')), options.name(2:end));
2193 % Compute the magnification and resolution
2194 if isempty(options.magnify)
2195 if isempty(options.resolution)
2196 options.magnify = 1;
2197 options.resolution = 864;
2199 options.magnify = options.resolution ./ get(0, 'ScreenPixelsPerInch');
2201 elseif isempty(options.resolution)
2202 options.resolution = 864;
2205 % Set the format to PNG, if no other format was specified but filename provided
2206 if ~isvector(options) && ~isbitmap(options) && ~options.svg && ~options.emf
2207 if wasOutputRequested
2212 % Check whether transparent background is wanted (old way)
2213 if isequal(get(ancestor(fig(1), 'figure'), 'Color'), 'none')
2214 options.transparent = true;
2217 % If requested, set the resolution to the native vertical resolution of the
2218 % first suitable image found
2220 if isbitmap(options)
2221 % Find a suitable image
2222 list = findall(fig, 'Type','image', 'Tag','export_fig_native');
2224 list = findall(fig, 'Type','image', 'Visible','on');
2227 % Check height is >= 2
2228 height = size(get(hIm, 'CData'), 1);
2232 % Account for the image filling only part of the axes, or vice versa
2233 yl = get(hIm, 'YData');
2235 yl = [yl(1)-0.5 yl(1)+height+0.5];
2237 yl = [min(yl), max(yl)]; % fix issue
#151 (case of yl containing more than 2 elements)
2241 yl = yl + [-0.5 0.5] * (diff(yl) / (height - 1));
2243 hAx = get(hIm,
'Parent');
2244 yl2 = get(hAx,
'YLim');
2245 % Find the pixel height of the axes
2246 oldUnits = get(hAx,
'Units');
2247 set(hAx,
'Units',
'pixels');
2248 pos = get(hAx,
'Position');
2249 set(hAx,
'Units', oldUnits);
2253 % Found a suitable image
2254 % Account for stretch-to-fill being disabled
2255 pbar = get(hAx, 'PlotBoxAspectRatio');
2256 pos = min(pos(4), pbar(2)*pos(3)/pbar(1));
2257 % Set the magnification to give native resolution
2258 options.magnify =
abs((height * diff(yl2)) / (pos * diff(yl))); % magnification must never be negative: issue
#103
2261 elseif options.resolution == 864 % don
't use -r864 in vector mode if user asked for -native
2262 options.resolution = []; % issue #241 (internal MATLAB bug produces black lines with -r864)
2266function options = setOptionsFormat(options, ext)
2267 switch lower(ext(2:end))
2268 case {'tif
', 'tiff
'}, options.tif = true;
2269 case {'jpg
', 'jpeg
'}, options.jpg = true;
2270 case 'png
', options.png = true;
2271 case 'bmp
', options.bmp = true;
2272 case 'eps
', options.eps = true;
2273 case 'emf
', options.emf = true;
2274 case 'pdf
', options.pdf = true;
2275 case 'svg
', options.svg = true;
2276 case 'gif
', options.gif = true;
2277 otherwise % do nothing
2281% Convert a possible string => char (issue #245)
2282function value = str2char(value)
2283 if isa(value,'string')
2284 value = char(value);
2288function A = downsize(A, factor)
2289 % Downsample an image
2290 if factor <= 1 || isempty(A) %issue #310
2295 % Faster, but requires image processing toolbox
2296 A = imresize(A, 1/factor, 'bilinear
');
2298 % No image processing toolbox - resize manually
2299 % Lowpass filter - use Gaussian as is separable, so faster
2300 % Compute the 1d Gaussian filter
2301 filt = (-factor-1:factor+1) / (factor * 0.6);
2302 filt = exp(-filt .* filt);
2303 % Normalize the filter
2304 filt = single(filt / sum(filt));
2306 padding = floor(numel(filt) / 2);
2307 if padding < 1 || isempty(A), return, end %issue #310
2308 onesPad = ones(1, padding);
2310 A2 = single(A([onesPad 1:end repmat(end,1,padding)], ...
2311 [onesPad 1:end repmat(end,1,padding)], a));
2312 A(:,:,a) = conv2(filt, filt', A2,
'valid');
2315 A = A(1+floor(mod(end-1, factor)/2):factor:end,1+floor(mod(end-1, factor)/2):factor:end,:);
2319function A = rgb2grey(A)
2320 A = cast(reshape(reshape(single(A), [], 3) * single([0.299; 0.587; 0.114]), size(A, 1), size(A, 2)),
class(A)); % #ok<ZEROLIKE>
2323function A = check_greyscale(A)
2324 % Check
if the image is greyscale
2325 if size(A, 3) == 3 && ...
2326 all(reshape(A(:,:,1) == A(:,:,2), [], 1)) && ...
2327 all(reshape(A(:,:,2) == A(:,:,3), [], 1))
2328 A = A(:,:,1); % Save only one channel for 8-bit output
2332function eps_remove_background(fname, count)
2333 % Remove the background of an eps file
2335 fh = fopen(fname, 'r+');
2337 error('
savefig:EPS:open','Cannot open file %s.', fname);
2339 % Read the file line by line
2344 break; % Quit, no rectangle found
2346 % Check if the line contains the background rectangle
2347 if isequal(regexp(l, ' *0 +0 +\d+ +\d+ +r[fe] *[\n\r]+', 'start'), 1)
2348 % Set the line to whitespace and quit
2349 l(1:regexp(l, '[\n\r]', 'start', 'once')-1) = ' ';
2350 fseek(fh, -numel(l), 0);
2360function b = isvector(options) % this only includes EPS-based vector formats (so not SVG,EMF)
2361 b = options.pdf || options.eps;
2364function b = isbitmap(options)
2365 b = options.png || options.tif || options.jpg || options.bmp || ...
2366 options.gif || options.im || options.alpha;
2369function [A, tcol, alpha] = getFigImage(fig, magnify, renderer, options, pos)
2370 if options.transparent
2371 % MATLAB "feature": figure size can change when changing color in -nodisplay mode
2372 % Note: figure background is set to off-white, not 'w', to handle common white elements (issue
#330)
2373 set(fig,
'Color',254/255*[1,1,1],
'Position',pos);
2374 % repaint figure, otherwise Java screencapture will see black bgcolor
2375 % Yair 19/12/21 - unnecessary: drawnow is called at top of
print2array
2378 % Print large
version to array
2380 % The following code might cause out-of-memory errors
2381 [A, tcol, alpha] =
print2array(fig, magnify, renderer);
2383 % This is more conservative in memory, but perhaps kills transparency(?)
2384 [A, tcol, alpha] =
print2array(fig, magnify/options.aa_factor, renderer,
'retry');
2386 % In transparent mode, set the bgcolor to white
2387 if options.transparent
2388 % Note: tcol should already be [255,255,255] here, but just in
case it
's not...
2389 tcol = uint8(254*[1,1,1]); %=off-white
2393function A = make_cell(A)
2399function add_bookmark(fname, bookmark_text)
2400 % Adds a bookmark to the temporary EPS file after %%EndPageSetup
2402 fh = fopen(fname, 'r
');
2404 error('savefig:bookmark:FileNotFound
','File %s not found.
', fname);
2407 fstrm = fread(fh, '*
char')';
2414 % Include standard pdfmark prolog to maximize compatibility
2415 fstrm = strrep(fstrm,
'%%BeginProlog', sprintf(
'%%%%BeginProlog\n/pdfmark where {pop} {userdict /pdfmark /cleartomark load put} ifelse'));
2417 fstrm = strrep(fstrm,
'%%EndPageSetup', sprintf(
'%%%%EndPageSetup\n[ /Title (%s) /OUT pdfmark',bookmark_text));
2419 % Write out the updated file
2420 fh = fopen(fname,
'w');
2422 error(
'savefig:bookmark:permission',
'Unable to open %s for writing.', fname);
2425 fwrite(fh, fstrm,
'char*1');
2433function set_manual_axes_modes(Hlims, ax)
2434 % Set the axes limits mode to manual
2435 set(Hlims, [ax
'LimMode'],
'manual');
2437 % Set the tick mode of linear axes to manual
2438 % Leave log axes alone as these are tricky
2439 M = get(Hlims, [ax
'Scale']);
2443 %idx = cellfun(@(c) strcmp(c,
'linear'), M);
2444 idx =
find(strcmp(M,
'linear'));
2445 %set(Hlims(idx), [ax
'TickMode'],
'manual'); % issue #187
2446 %set(Hlims(idx), [ax
'TickLabelMode'],
'manual'); %
this hides exponent label in HG2!
2447 for idx2 = 1 : numel(idx)
2449 % Fix
for issue #187 - only set manual ticks when no exponent is present
2450 hAxes = Hlims(idx(idx2));
2451 props = {[ax
'TickMode'],
'manual', [ax
'TickLabelMode'],
'manual'};
2452 tickVals = get(hAxes,[ax
'Tick']);
2453 tickStrs = get(hAxes,[ax
'TickLabel']);
2454 try % TickLabelRotation is available since R2021a
2455 propName = [ax,
'TickLabelRotationMode'];
2456 if ~isempty(get(hAxes,propName)) %
this will croak in R2020b-
2457 props = [props, propName,
'manual']; %#ok<AGROW>
2460 % ignore - probably R2020b or older
2462 try % Fix issue #236
2463 exponents = [hAxes.([ax
'Axis']).SecondaryLabel];
2465 exponents = [hAxes.([ax
'Ruler']).SecondaryLabel];
2467 if isempty([exponents.String])
2468 % Fix
for issue #205 - only set manual ticks when the Ticks number match the TickLabels number
2469 if numel(tickVals) == numel(tickStrs)
2470 set(hAxes, props{:}); % no exponent and matching ticks, so update both ticks and tick labels to manual
2471 drawnow % issue #385
2474 catch % probably HG1
2475 % Fix
for issue #220 - exponent is removed in HG1 when TickMode is
'manual' (internal MATLAB bug)
2476 if isequal(tickVals, str2num(tickStrs)
') %#ok<ST2NM>
2477 set(hAxes, props{:}); % revert back to old behavior
2483function change_rgb_to_cmyk(fname) % convert RGB => CMYK within an EPS file
2484 % Do post-processing on the eps file
2486 % Read the EPS file into memory
2487 fstrm = read_write_entire_textfile(fname);
2489 % Replace all gray-scale colors
2490 fstrm = regexprep(fstrm, '\n([\d.]+) +GC\n
', '\n0 0 0 ${num2str(1-str2num($1))} CC\n
');
2492 % Replace all RGB colors
2493 fstrm = regexprep(fstrm, '\n[0.]+ +[0.]+ +[0.]+ +RC\n
', '\n0 0 0 1 CC\n
'); % pure black
2494 fstrm = regexprep(fstrm, '\n([\d.]+) +([\d.]+) +([\d.]+) +RC\n
', '\n${sprintf(
''%.4g
'',[1-[str2num($1),str2num($2),str2num($3)]/max([str2num($1),str2num($2),str2num($3)]),1-max([str2num($1),str2num($2),str2num($3)])])} CC\n
');
2496 % Overwrite the file with the modified contents
2497 read_write_entire_textfile(fname, fstrm);
2499 % never mind - leave as is...
2503function [hBlackAxles, hBlackRulers] = fixBlackAxle(hAxes, axleName)
2506 for idx = 1 : numel(hAxes)
2508 axleColor = get(ax, axleName);
2509 if isequal(axleColor,[0,0,0]) || isequal(axleColor,'k
')
2510 hBlackAxles(end+1) = ax; %#ok<AGROW>
2511 try % Fix issue #306 - black yyaxis
2512 if strcmpi(axleName,'Color
'), continue, end %ruler, not axle
2513 rulerName = strrep(axleName,'Color
','Axis
');
2514 hRulers = get(ax, rulerName);
2515 newBlackRulers = fixBlackAxle(hRulers,'Color
');
2516 hBlackRulers = [hBlackRulers newBlackRulers]; %#ok<AGROW>
2521 set(hBlackAxles, axleName, [0,0,0.01]); % off-black
2524function hText = fixBlackText(hObject, propName)
2526 hText = get(hObject, propName);
2527 try hText = [hText{:}]; catch, end %issue #383
2528 for idx = numel(hText) : -1 : 1
2529 hThisText = hText(idx);
2530 try hThisText = hThisText{1}; catch, end
2531 color = get(hThisText,'Color
');
2532 if isequal(color,[0,0,0]) || isequal(color,'k
')
2533 set(hThisText, 'Color
', [0,0,0.01]); %off-black
2535 hText(idx) = []; % remove from list
2543% Issue #269: format-specific options
2544function [optionsCells, bitDepth] = getFormatOptions(options, formatName)
2547 optionsStruct = options.format_options.(lower(formatName));
2550 % Perhaps user specified the options in cell array format
2551 optionsStruct = options.format_options.all;
2553 % User did not specify any extra parameters for this format
2558 optionNames = fieldnames(optionsStruct);
2559 optionVals = struct2cell(optionsStruct);
2560 optionsCells = [optionNames, optionVals]';
2561 if nargout < 2,
return, end % bail out
if BitDepth is not required
2563 idx =
find(strcmpi(optionNames,
'BitDepth'), 1,
'last');
2565 bitDepth = optionVals{idx};
2568 % never mind - ignore
2572% Check
for newer
version (only once a day)
2573function isNewerVersionAvailable = checkForNewerVersion(currentVersion)
2574 persistent lastCheckTime lastVersion
2575 isNewerVersionAvailable =
false;
2576 return; % Amir Shahmoradi: This `
return` was added to suppress update checks by `
savefig` permanently.
2577 if isdeployed,
return, end
2578 if nargin < 1 || isempty(lastCheckTime) || now - lastCheckTime > 1
2579 url =
'https://raw.githubusercontent.com/altmany/savefig/master/savefig.m';
2582 [unused,unused,unused,unused,latestVerStrs] = regexp(str,
'\n[^:]+: \(([^)]+)\) ([^%]+)(?=\n)'); %#ok<ASGLU>
2583 latestVersion = str2double(latestVerStrs{end}{1});
2585 currentVersion = lastVersion;
2587 currentVersion = currentVersion + 1e3*eps;
2589 isNewerVersionAvailable = latestVersion > currentVersion;
2590 if isNewerVersionAvailable
2592 verStrs = strtrim(reshape([latestVerStrs{:}],2,[]));
2593 verNums = arrayfun(@(c)str2double(c{1}),verStrs(1,:));
2594 isValid = verNums > currentVersion;
2595 versionDesc = strjoin(flip(verStrs(2,isValid)),
';');
2597 % Something bad happened - only display the latest
version description
2598 versionDesc = latestVerStrs{1}{2};
2600 try versionDesc = strjoin(strrep(strcat(
' ***', strtrim(strsplit(versionDesc,
';'))),
'***',
'* '),
char(10));
catch, end %#ok<CHARTEN>
2601 msg = sprintf([
'You are using version %.2f of savefig. ' ...
2602 'A newer version (%g) is available, with the following improvements/fixes:\n' ...
2604 'A change-log of recent releases is available here; the complete change-log is included at the top of the savefig.m file.\n' ... % issue #322
2605 'You can download the new version from GitHub or MATLAB File Exchange, ' ...
2606 'or run savefig(''-update'') to install it directly.' ...
2607 ], currentVersion, latestVersion, versionDesc);
2608 msg =
hyperlink(
'https://github.com/altmany/savefig',
'GitHub', msg);
2609 msg =
hyperlink(
'https://www.mathworks.com/matlabcentral/fileexchange/23629-savefig',
'MATLAB File Exchange', msg);
2610 msg =
hyperlink(
'matlab:savefig(''-update'')',
'savefig(''-update'')', msg);
2611 msg =
hyperlink(
'https://github.com/altmany/savefig/releases',
'available here', msg);
2612 msg =
hyperlink(
'https://github.com/altmany/savefig/blob/master/savefig.m#L300',
'savefig.m file', msg);
2613 warning(
'savefig:version',msg);
2618 lastCheckTime = now;
2619 lastVersion = currentVersion;
2624function updateInstalledVersion()
2626 zipFileName =
'https://github.com/altmany/savefig/archive/master.zip';
2627 fprintf(
'Downloading latest version of %s from %s...\n', mfilename, zipFileName);
2628 folderName = fileparts(
which(mfilename(
'fullpath')));
2629 targetFileName = fullfile(folderName, datestr(now,
'yyyy-mm-dd.zip'));
2630 folder = folderName;
2631 if ispc % winopen only works on Windows
2633 folder =
hyperlink([
'matlab:winopen(''' folderName
''')
'], folderName);
2634 catch % hyperlink.m is not properly installed
2638 urlwrite(zipFileName,targetFileName); %#ok<URLWR>
2640 error('savefig:update:download
','Error downloading %s into %s: %s\n
',zipFileName,targetFileName,err.message);
2643 % Unzip the downloaded zip file in the savefig folder
2644 fprintf('Extracting %s...\n
', targetFileName);
2646 unzip(targetFileName,folderName);
2647 % Fix issue #302 - zip file uses an internal folder savefig-master
2648 subFolder = fullfile(folderName,'savefig-master
');
2649 try movefile(fullfile(subFolder,'*.*
'),folderName, 'f
'); catch, end %All OSes
2650 try movefile(fullfile(subFolder,'*
'), folderName, 'f
'); catch, end %MacOS/Unix
2651 try movefile(fullfile(subFolder,'.*
'), folderName, 'f
'); catch, end %MacOS/Unix
2652 try rmdir(subFolder); catch, end
2654 error('savefig:update:unzip
','Error unzipping %s: %s\n
',targetFileName,err.message);
2657 % Notify the user and rehash
2658 fprintf('Successfully installed the latest %s
version in %s\n
', mfilename, folder);
2659 clear functions %#ok<CLFUNC>
2663% Read a file from the web
2664function str = readURL(url)
2666 str = char(webread(url));
2667 catch err %if isempty(which('webread
'))
2668 if isempty(strfind(err.message,'404'))
2669 v =
version; %
'9.6.0.1072779 (R2019a)'
2670 if v(1) >=
'8' %
'8.0 (R2012b)' https:
2671 str = urlread(url,
'Timeout',5); %#ok<URLRD>
2673 str = urlread(url); %#ok<URLRD> % R2012a or older (no Timeout parameter)
2679 if size(str,1) > 1 % ensure a row-wise
string
2684% Display a promo message in the MATLAB console
2685function displayPromoMsg(msg, url)
2687 msg = strrep(msg,'<$>
',url);
2688 link = ['<a
href=
"' url];
2689 msg = regexprep(msg,url,[link '">$0</a>
']);
2690 %msg = regexprep(msg,{'consulting
','training
'},[link '/$0
">$0</a>']);
2691 %warning('savefig:promo',msg);
2692 disp(['[' 8 msg ']' 8]);
2695% Cross-check existance of other programs
2696function programsCrossCheck()
2697 if isdeployed, return, end % don't check in deployed mode
2700 hasTaskList = false;
2701 if ispc && ~exist('IQML','file')
2702 hasIQ = exist('C:/Progra~1/DTN/IQFeed','dir') || ...
2703 exist('C:/Progra~2/DTN/IQFeed','dir');
2705 [status,tasksStr] = system('tasklist'); %#ok<ASGLU>
2706 tasksStr = lower(tasksStr);
2707 hasIQ = ~isempty(strfind(tasksStr,'iqconnect')) || ...
2708 ~isempty(strfind(tasksStr,'iqlink')); %#ok<STREMP>
2712 displayPromoMsg('To connect MATLAB to IQFeed, try the IQML connector <$>', 'https://UndocumentedMatlab.com/IQML');
2717 if ~exist('IBMatlab','file')
2719 possibleFolders = {'C:/Jts','C:/Programs/Jts','C:/Progra~1/Jts','C:/Progra~2/Jts','~/IBJts','~/IBJts/IBJts'};
2720 for folderIdx = 1 : length(possibleFolders)
2721 if exist(possibleFolders{folderIdx},'dir')
2729 [status,tasksStr] = system('tasklist'); %#ok<ASGLU>
2731 [status,tasksStr] = system('ps -e'); %#ok<ASGLU>
2733 tasksStr = lower(tasksStr);
2735 hasIB = ~isempty(strfind(tasksStr,'tws')) || ...
2736 ~isempty(strfind(tasksStr,'ibgateway')); %#ok<STREMP>
2739 displayPromoMsg('To connect MATLAB to IB try the IB-MATLAB connector <$>', 'https://UndocumentedMatlab.com/IB-MATLAB');
2743 % never mind - ignore error
2747% Hint to users to use exportgraphics/copygraphics in certain cases
2748function alertForExportOrCopygraphics(options)
2749 %matlabVerNum = str2num(regexprep(version,'(\d+\.\d+).*','$1'));
2750 if isdeployed, return, end % don't check in deployed mode
2752 % Bail out on R2019b- (copygraphics/exportgraphics not available/reliable)
2753 if verLessThan('matlab','9.8') % 9.8 = R2020a
2757 isNoRendererSpecified = options.renderer == 0;
2758 isPainters = options.renderer == 3;
2759 noResolutionSpecified = isempty(options.resolution) || isequal(options.resolution,864);
2761 % First check for copygraphics compatibility (export to clipboard)
2763 if options.clipboard
2764 if options.transparent % -transparent was requested
2765 if isPainters || isNoRendererSpecified % painters or default renderer
2766 if noResolutionSpecified
2767 params = '''BackgroundColor'',''none'',''ContentType'',''vector'',';
2768 else % exception: no message
2771 else % opengl/zbuffer renderer
2772 if options.invert_hardcopy % default
2773 params = '''BackgroundColor'',''white'',';
2774 else % -noinvert was requested
2775 params = '''BackgroundColor'',''current'','; % 'none' is 'white' when ContentType='image'
2777 params = [params '''ContentType'',''image'','];
2778 if ~noResolutionSpecified
2779 params = [params '''Resolution'',' num2str(options.resolution) ','];
2781 % don't add a resolution param
2784 else % no -transparent
2785 if options.invert_hardcopy % default
2786 params = '''BackgroundColor'',''white'',';
2787 else % -noinvert was requested
2788 params = '''BackgroundColor'',''current'',';
2790 if isPainters % painters (but not default!) renderer
2791 if noResolutionSpecified
2792 params = [params '''ContentType'',''vector'','];
2793 else % exception: no message
2796 else % opengl/zbuffer/default renderer
2797 params = [params '''ContentType'',''image'','];
2798 if ~noResolutionSpecified
2799 params = [params '''Resolution'',' num2str(options.resolution) ','];
2801 % don't add a resolution param
2806 % If non-RGB colorspace was requested on R2021a+
2807 if ~verLessThan('matlab','9.10') % 9.10 = R2021a
2808 if options.colourspace == 2 % gray
2809 params = [params '''Colorspace'',''gray'','];
2813 displayMsg(params, 'copygraphics', 'clipboard', '');
2815 % Next check for exportgraphics compatibility (export to file)
2816 % Note: not <else>, since -clipboard can be combined with file export
2818 if ~options.clipboard
2819 if options.transparent % -transparent was requested
2820 if isvector(options) % vector output
2821 if isPainters || isNoRendererSpecified % painters or default renderer
2822 if noResolutionSpecified
2823 params = '''BackgroundColor'',''none'',''ContentType'',''vector'',';
2824 else % exception: no message
2827 else % opengl/zbuffer renderer
2828 params = '''BackgroundColor'',''none'',''ContentType'',''vector'',';
2830 else % non-vector output
2833 else % no -transparent
2834 if options.invert_hardcopy % default
2835 params = '''BackgroundColor'',''white'',';
2836 else % -noinvert was requested
2837 params = '''BackgroundColor'',''current'',';
2839 if isvector(options) % vector output
2840 if isPainters || isNoRendererSpecified % painters or default renderer
2841 if noResolutionSpecified
2842 params = [params '''ContentType'',''vector'','];
2843 else % exception: no message
2846 else % opengl/zbuffer renderer
2847 if noResolutionSpecified
2848 params = [params '''ContentType'',''image'','];
2849 else % exception: no message
2853 else % non-vector output
2854 if isPainters % painters (but not default!) renderer
2855 % exception: no message
2857 else % opengl/zbuffer/default renderer
2858 if ~noResolutionSpecified
2859 params = [params '''Resolution'',' num2str(options.resolution) ','];
2865 % If non-RGB colorspace was requested on R2021a+
2866 if ~verLessThan('matlab','9.10') % 9.10 = R2021a
2867 if options.colourspace == 2 % gray
2868 params = [params '''Colorspace'',''gray'','];
2869 elseif options.colourspace == 1 && options.eps % cmyk (eps only)
2870 params = [params '''Colorspace'',''cmyk'','];
2874 filenameParam = 'filename,'; %=[options.name ','];
2875 displayMsg(params, 'exportgraphics', 'file', filenameParam);
2877 % Ignore errors - do not stop savefig processing
2880 % Utility function to display an alert message
2881 function displayMsg(params, funcName, type, filenameParam)
2882 if length(params) > 1
2883 % strip default param values from the message
2884 params = strrep(params, '''BackgroundColor'',''white'',', '');
2885 % strip the trailing ,
2886 if ~isempty(params) && params(end)==',', params(end)=''; end
2887 % if this message was not already displayed
2888 try prevParams = getpref('savefig',funcName); catch, prevParams = ''; end
2889 if ~strcmpi(params, prevParams)
2890 % display the message (TODO: perhaps replace warning() with fprintf()?)
2891 if ~isempty([filenameParam params])
2892 filenameParam = [',' filenameParam];
2894 if ~isempty(filenameParam) && filenameParam(end)==',' && isempty(params)
2895 filenameParam(end) = '';
2897 handleName = options.handleName;
2898 if isempty(options.handleName) % handle was either not specified, or via gca()/gcf() etc. [i.e. not by variable]
2899 handleName = 'hFigure';
2901 msg = ['In MATLAB R2020a+ you can also use ' funcName '(' handleName filenameParam params ') for simple ' type ' export'];
2902 if ~isempty(strfind(params,'''vector''')) %#ok<STREMP>
2903 msg = [msg ', which could also improve image vectorization, solving rasterization/pixelization problems.'];
2905 oldWarn = warning('on','verbose');
2906 warning(['savefig:' funcName], msg);
2908 setpref('savefig',funcName,params);
2915function flag = existFile(filename)
2917 % isfile() is faster than exist(), but does not report files on path
2918 flag = isfile(filename);
2920 flag = exist(filename,'file') ~= 0;
2924% Add interactive export button to the figure's toolbar
2925function addToolbarButton(hFig, options)
2926 % Ensure we have a valid toolbar handle
2927 if isempty(hFig) || ~ishandle(hFig)
2931 error('savefig:noFigure','not a valid GUI handle');
2934 hToolbar = findall(hFig,'type','uifigure','-or','tag','uitoolbar','-or','tag','FigureToolBar','-depth',1);
2935 % Don't create the figure toolbar - perhaps there's a custom user toolbar
2936 % If there isn't any toolbar, uisplittool() below will create a new one
2937 if isempty(hToolbar)
2939 set(hFig,'ToolBar','figure');
2940 hToolbar = findall(hFig,'tag','uitoolbar','-or','tag','FigureToolBar','-depth',1);
2941 if isempty(hToolbar)
2943 warning('savefig:noToolbar','cannot add toolbar button to the specified figure');
2948 hToolbar = hToolbar(1); % just in case there are several toolbars... - use only the first
2951 % Bail out silently if the savefig button already exists
2952 hButton = findall(hToolbar, 'Tag','savefig');
2953 if ~isempty(hButton)
2957 % Prepare the camera icon
2958 icon = ['3333333333333333'; ...
2959 '3333300000033333'; ...
2960 '3333065555603333'; ...
2961 '3000000000000003'; ...
2962 '3022222222222203'; ...
2963 '3022222222222203'; ...
2964 '3022220000222203'; ...
2965 '3022203113022203'; ...
2966 '3022201111022203'; ...
2967 '3022204444022203'; ...
2968 '3022220000222203'; ...
2969 '3022222222222203'; ...
2970 '3022222222222203'; ...
2971 '3000000000000003'; ...
2972 '3333333333333333'; ...
2973 '3333333333333333'];
2974 cm = [ 0 0 0; ... % black
2975 0 0.60 1; ... % light blue
2976 0.53 0.53 0.53; ... % light gray
2977 NaN NaN NaN; ... % transparent
2978 0 0.73 0; ... % light green
2979 0.27 0.27 0.27; ... % gray
2980 0.13 0.13 0.13]; % dark gray
2981 cdata = ind2rgb(uint8(icon-'0'),cm);
2983 % If the button does not already exit
2984 tooltip = 'Export this figure to a pdf or image file';
2986 % Add the button with the icon to the figure's toolbar
2987 props = {'CData',cdata, 'Tag','savefig', ...
2988 'Tooltip',tooltip, 'ClickedCallback',@(h,e)interactiveExport(hFig,options)};
2989 if ~isempty(hToolbar)
2990 props = {props{:}, 'Parent',hToolbar}; %#ok<CCAT> %[props,...] cause a runtime-error! (internal MATLAB bug)
2993 hButton = []; % just in case we croak below
2995 % Create a new split-button with the open-file button's data
2996 oldWarn = warning('off','MATLAB:uisplittool:DeprecatedFunction');
2997 hButton = uisplittool(props{:});
3000 % Add the split-button's menu items
3001 drawnow; pause(0.01); % allow the buttom time to render
3002 jButton = get(hButton,'JavaContainer'); %#ok<JAVCT>
3003 jButtonMenu = jButton.getMenuComponent;
3005 tooltip = [tooltip ' (specify filename/format)'];
3006 try jButtonMenu.setToolTipText(tooltip); catch, end
3007 try jButton.getComponentPeer.getComponent(1).setToolTipText(tooltip); catch, end
3009 [folder,defaultFname] = fileparts(get(hFig,'FileName'));
3010 if ~isempty(folder) && exist(folder,'dir')
3011 folder = regexprep(folder,'[/\]$','');
3015 if isempty(defaultFname), defaultFname = get(hFig,'Name'); end
3016 defaultFname = regexprep(defaultFname,'[*?"<>|:/\\]
','-
'); %remove illegal filename chars
3017 if isempty(defaultFname), defaultFname = 'figure
'; end
3018 defaultFname = fullfile(folder,defaultFname);
3019 imFormats = {'pdf
','eps
','svg
','png
','jpg
','tif
','gif
','bmp
'};
3020 if ispc, imFormats{end+1} = 'emf
'; end
3021 for idx = 1 : numel(imFormats)
3022 thisFormat = imFormats{idx};
3023 filename = [defaultFname '.
' thisFormat];
3024 label = [upper(thisFormat) ' image file (
' filename ')
'];
3025 jMenuItem = handle(jButtonMenu.add(label),'CallbackProperties
');
3026 callback = @(h,e) savefig(hFig, filename, options.propagatedOpts{:});
3027 set(jMenuItem,'ActionPerformedCallback
',callback);
3029 jButtonMenu.addSeparator();
3030 if ispc % winopen only works on Windows
3031 label = ['Open export folder:
' folder];
3032 jMenuItem = handle(jButtonMenu.add(label),'CallbackProperties
');
3033 set(jMenuItem,'ActionPerformedCallback
',@(h,e)winopen(folder));
3034 jButtonMenu.addSeparator();
3036 cbFormats = {'image
','bitmap
','meta
','pdf
'};
3037 for idx = 1 : numel(cbFormats)
3038 thisFormat = cbFormats{idx};
3039 exFormat = ['-clipboard:
' thisFormat];
3040 label = ['Clipboard (
' thisFormat ' format)
'];
3041 jMenuItem = handle(jButtonMenu.add(label),'CallbackProperties
');
3042 callback = @(h,e) savefig(hFig, exFormat, options.propagatedOpts{:});
3043 set(jMenuItem,'ActionPerformedCallback
',callback);
3045 jButtonMenu.addSeparator();
3046 jMenuItem = handle(jButtonMenu.add('Select file
name, location and format
'),'CallbackProperties
');
3047 set(jMenuItem,'ActionPerformedCallback
',@(h,e)interactiveExport(hFig,options));
3048 catch % revert to a simple documented toolbar pushbutton
3050 if isempty(hButton) %avoid duplicate toolbar buttons (keep the splittool)
3051 hButton = uipushtool(props{:}); %#ok<NASGU>
3056% Add interactive export menu to the figure's menubar
3057function addMenubarMenu(hFig, options)
3058 % Ensure we have a valid figure handle
3059 if isempty(hFig) || ~ishandle(hFig)
3063 error(
'savefig:noFigure',
'not a valid GUI handle');
3066 %set(hFig,
'MenuBar',
'figure'); % Don
't create the default figure menubar!
3068 % Bail out silently if the savefig menu already exists
3069 hMainMenu = findall(hFig, '-depth
',1, 'type
','uimenu
', 'Tag
','savefig');
3070 if ~isempty(hMainMenu)
3074 % Add the savefig menu to the figure's menubar
3075 hMainMenu = uimenu(hFig,
'Text',
'E&xport',
'Tag',
'savefig');
3076 addMenuItems(hMainMenu, hFig, options);
3079% Add
savefig menu item to a parent menu
3080function addMenuItems(hMainMenu, hFig, options)
3081 [folder,defaultFname] = fileparts(get(hFig,
'FileName'));
3082 if ~isempty(folder) && exist(folder,
'dir')
3083 folder = regexprep(folder,'[/\]$','');
3087 if isempty(defaultFname), defaultFname = get(hFig,'Name'); end
3088 defaultFname = regexprep(defaultFname,'[*?"<>|:/\\]','-'); %remove illegal filename chars
3089 if isempty(defaultFname), defaultFname = 'figure'; end
3090 defaultFname = fullfile(folder,defaultFname);
3091 imFormats = {
'pdf',
'eps',
'svg',
'png',
'jpg',
'tif',
'gif',
'bmp'};
3092 if ispc, imFormats{end+1} =
'emf'; end
3093 for idx = 1 : numel(imFormats)
3094 thisFormat = imFormats{idx};
3095 filename = [defaultFname
'.' thisFormat];
3096 label = [upper(thisFormat)
' image file (' filename
')'];
3097 callback = @(h,e)
savefig(hFig, filename, options.propagatedOpts{:});
3098 uimenu(hMainMenu,
'Text',label,
'MenuSelectedFcn',callback);
3100 if ispc % winopen only works on Windows
3101 uimenu(hMainMenu,
'Text',[
'Open export folder: ' folder],
'Separator',
'on', ...
3102 'MenuSelectedFcn',@(h,e)winopen(folder));
3104 cbFormats = {
'image',
'bitmap',
'meta',
'pdf'};
3105 for idx = 1 : numel(cbFormats)
3106 thisFormat = cbFormats{idx};
3107 exFormat = [
'-clipboard:' thisFormat];
3108 label = [
'Clipboard (' thisFormat
' format)'];
3109 sep =
'off';
if idx==1, sep =
'on'; end
3110 callback = @(h,e)
savefig(hFig, exFormat, options.propagatedOpts{:});
3111 uimenu(hMainMenu,
'Text',label,
'Separator',sep, ...
3112 'MenuSelectedFcn',callback);
3114 uimenu(hMainMenu,
'Text',
'Select file name, location and format',
'Separator',
'on', ...
3115 'MenuSelectedFcn',@(h,e)interactiveExport(hFig,options));
3118% Add interactive export context-menu to the specified figure
3119function addContextMenu(hFig, options)
3120 % Ensure we have a valid figure handle
3121 if isempty(hFig) || ~ishandle(hFig)
3125 error(
'savefig:noHandle',
'not a valid GUI handle');
3129 % Get the figure
's current context-menu (if defined)
3130 % Note: The UIContextMenu property name changed sometime in the late 2010s
3132 propName = 'ContextMenu
';
3133 cm = get(hFig,propName);
3135 propName = 'UIContextMenu
';
3136 cm = get(hFig,propName);
3139 % If no context menu is defined, attach the basic savefig one
3141 % Get the standard savefig context menu for this figure
3142 std_cm = findall(hFig, '-depth
',1, 'type
','uicontextmenu
', 'Tag
','savefig');
3144 % Basic savefig context-menu not yet defined - create it
3145 std_cm = uicontextmenu(hFig,'Tag
','savefig');
3146 hMenu = uimenu(std_cm, 'Text
','Export
', 'Tag
','savefig');
3147 addMenuItems(hMenu, hFig, options);
3149 %Attach the standard savefig context menu to this figure
3150 set(hFig,propName,std_cm);
3151 else % a context-menu is already defined for this figure
3152 % Ensure that the context-menu doesn't already have an
savefig sub-menu
3153 hMenu = findall(cm,
'tag',
'savefig');
3155 % Attach the
savefig sub-menu to the figure
's context-menu
3156 hMenu = uimenu(cm, 'Text
','Export
', 'Tag
','savefig');
3157 addMenuItems(hMenu, hFig, options);
3162% Callback functions for toolbar/menubar actions
3163function interactiveExport(hObject, options)
3164 % Get the exported figure handle
3167 hFig = ancestor(hObject, 'figure
');
3170 return % bail out silently if no figure is available
3173 % Display a Save-as dialog to let the user select the export name & type
3174 [folder,defaultFname] = fileparts(get(hFig,'FileName
'));
3175 if ~isempty(folder) && exist(folder,'dir
')
3176 folder = regexprep(folder,'[/\]$
','');
3180 if isempty(defaultFname), defaultFname = get(hFig,'Name
'); end
3181 defaultFname = regexprep(defaultFname,'[*?
"<>|:/\\]+','-'); %remove illegal filename chars
3182 if isempty(defaultFname), defaultFname = 'figure'; end
3183 defaultFname = fullfile(folder,defaultFname);
3184 %formats = imformats;
3185 formats = {'pdf','eps','svg','png','jpg','tif','gif','bmp'};
3186 if ispc, formats{end+1} = 'emf'; end
3187 formats = [formats,'clipboard:image','clipboard:bitmap','clipboard:meta','clipboard:pdf'];
3188 for idx = 1 : numel(formats)
3189 thisFormat = formats{idx};
3190 ext = sprintf('*.%s',thisFormat);
3191 if ~any(thisFormat==':') % image file format
3192 description = [upper(thisFormat) ' image file (' ext ')'];
3193 format(idx,1:2) = {ext, description}; %#ok<AGROW>
3194 else % clipboard format
3195 description = [strrep(thisFormat,':',' (') ' format *.)'];
3196 format(idx,1:2) = {'*.*', description}; %#ok<AGROW>
3200 [filename,pathname,idx] = uiputfile(format,'Save figure export as',defaultFname);
3201 drawnow; pause(0.01); % prevent a MATLAB hang
3202 if ~isequal(filename,0)
3203 thisFormat = formats{idx};
3204 if ~any(thisFormat==':') % export to image file
3205 filename = fullfile(pathname,filename);
3206 savefig(hFig, filename, options.propagatedOpts{:});
3207 else % export to clipboard
3208 savefig(hFig, ['-' thisFormat], options.propagatedOpts{:});
3211 % User canceled the dialog - bail out silently
function name(in vendor)
Return the MPI library name as used in naming the ParaMonte MATLAB shared library directories.
function list()
Return a list of MATLAB strings containing the names of OS platforms supported by the ParaMonte MATLA...
function index(in array)
Given array(1 : n), return the array index indx(1 : n) such that array(indx) is in ascending order.
function version(in silent)
Return a scalar MATLAB string containing the latest available ParaMonte MATLAB version newer than the...
function abs(in path, in style)
Return the Get absolute canonical path of a file or folder.
function str2char(in value)
This is the base class for generating subclass of MATLAB handle superclass whose annoying methods are...
function copy(in from, in to, in field, in exclude)
Copy the contents of the struct/object from to the struct/object to recursively and without destroyin...
function crop_borders(in A, in bcol, in padding, in crop_amounts)
function find(in vendor)
Return a list of scalar MATLAB strings containing the paths to all detected mpiexec binaries installe...
function ghostscript(in cmd)
function href(in url)
Return an HTML-style decoration of the input URL if the ParaMonte MATLAB library is used in GUI,...
function hyperlink(in url, in label, in msg)
function parse_args(in A, in varargin)
function im2gif(in A, in varargin)
function info()
Return a MATLAB string and the corresponding cache file path containing the current system informatio...
function install(in vendor, in isyes)
Install or provide instructions to install the MPI library from the requested input MPI vendor.
function isolate_axes(in ah, in vis)
function map()
Return a scalar MATLAB logical that is true if and only if the current installation of MATLAB contain...
function print2array(in fig, in res, in renderer, in gs_options)
function print2eps(in name, in fig, in export_options, in varargin)
function read_write_entire_textfile(in fname, in fstrm)
function release(in type)
Return a scalar MATLAB string containing the MATLAB release version, year, or season as requested.
function savefig(in varargin)
Export figures in a publication-quality format.
function show(in obj, in name, in hidden)
Display the components of an input MATLAB variable on MATLAB Console recursively.
function user_string(in string_name, in string)
function using_hg2(in fig)
function which(in vendor)
Return the a MATLAB string containing the path to the first mpiexec executable binary found in system...