Ignore:
Timestamp:
12/08/19 19:39:55 (5 years ago)
Author:
Mathieu Morlighem
Message:

CHG: upgrading exprt_fig

File:
1 edited

Legend:

Unmodified
Added
Removed
  • issm/trunk-jpl/externalpackages/export_fig/export_fig.m

    r21671 r24460  
    1 function [imageData, alpha] = export_fig(varargin)
     1function [imageData, alpha] = export_fig(varargin) %#ok<*STRCL1>
    22%EXPORT_FIG  Exports figures in a publication-quality format
    33%
     
    2525%   export_fig ... -update
    2626%   export_fig ... -nofontswap
     27%   export_fig ... -font_space <char>
    2728%   export_fig ... -linecaps
     29%   export_fig ... -noinvert
     30%   export_fig ... -preserve_size
     31%   export_fig ... -options <optionsStruct>
    2832%   export_fig(..., handle)
    2933%
     
    3842%   - Render images at native resolution (optional for bitmap formats)
    3943%   - Transparent background supported (pdf, eps, png, tif)
    40 %   - Semi-transparent patch objects supported (png & tif only)
    41 %   - RGB, CMYK or grayscale output (CMYK only with pdf, eps, tiff)
     44%   - Semi-transparent patch objects supported (png, tif)
     45%   - RGB, CMYK or grayscale output (CMYK only with pdf, eps, tif)
    4246%   - Variable image compression, including lossless (pdf, eps, jpg)
    43 %   - Optionally append to file (pdf, tiff)
    44 %   - Vector formats: pdf, eps
    45 %   - Bitmap formats: png, tiff, jpg, bmp, export to workspace
    46 %   - Rounded line-caps (optional; pdf & eps only)
     47%   - Optional rounded line-caps (pdf, eps)
     48%   - Optionally append to file (pdf, tif)
     49%   - Vector formats: pdf, eps, svg
     50%   - Bitmap formats: png, tif, jpg, bmp, export to workspace
    4751%
    4852% This function is especially suited to exporting figures for use in
     
    5862% background; only TIF & PNG formats support transparency of patch objects.
    5963%
    60 % The choice of renderer (opengl, zbuffer or painters) has a large impact
    61 % on the quality of output. The default value (opengl for bitmaps, painters
    62 % for vector formats) generally gives good results, but if you aren't
    63 % satisfied then try another renderer.  Notes: 1) For vector formats (EPS,
    64 % PDF), only painters generates vector graphics. 2) For bitmaps, only
    65 % opengl can render transparent patch objects correctly. 3) For bitmaps,
    66 % only painters will correctly scale line dash and dot lengths when
    67 % magnifying or anti-aliasing. 4) Fonts may be substitued with Courier when
    68 % using painters.
     64% The choice of renderer (opengl/zbuffer/painters) has a large impact on the
     65% output quality. The default value (opengl for bitmaps, painters for vector
     66% formats) generally gives good results, but if you aren't satisfied
     67% then try another renderer.  Notes:
     68%   1) For vector formats (EPS,PDF), only painters generates vector graphics
     69%   2) For bitmap formats, only opengl correctly renders transparent patches
     70%   3) For bitmap formats, only painters correctly scales line dash and dot
     71%      lengths when magnifying or anti-aliasing
     72%   4) Fonts may be substitued with Courier when using painters
    6973%
    7074% When exporting to vector format (PDF & EPS) and bitmap format using the
     
    7276% on your system. You can download this from:
    7377%   http://www.ghostscript.com
    74 % When exporting to eps it additionally requires pdftops, from the Xpdf
    75 % suite of functions. You can download this from:
    76 %   http://www.foolabs.com/xpdf
     78% When exporting to EPS it additionally requires pdftops, from the Xpdf
     79% suite of functions. You can download this from: http://xpdfreader.com
     80%
     81% SVG output uses the fig2svg (https://github.com/kupiqu/fig2svg) or plot2svg
     82% (https://github.com/jschwizer99/plot2svg) utilities, or Matlab's built-in
     83% SVG export if neither of these utilities are available on Matlab's path.
     84% Note: cropping/padding are not supported in export_fig's SVG output.
    7785%
    7886% Inputs:
    7987%   filename - string containing the name (optionally including full or
    80 %              relative path) of the file the figure is to be saved as. If
    81 %              a path is not specified, the figure is saved in the current
    82 %              directory. If no name and no output arguments are specified,
    83 %              the default name, 'export_fig_out', is used. If neither a
    84 %              file extension nor a format are specified, a ".png" is added
    85 %              and the figure saved in that format.
    86 %   -format1, -format2, etc. - strings containing the extensions of the
    87 %                              file formats the figure is to be saved as.
    88 %                              Valid options are: '-pdf', '-eps', '-png',
    89 %                              '-tif', '-jpg' and '-bmp'. All combinations
    90 %                              of formats are valid.
    91 %   -nocrop - option indicating that the borders of the output are not to
    92 %             be cropped.
     88%             relative path) of the file the figure is to be saved as. If
     89%             a path is not specified, the figure is saved in the current
     90%             directory. If no name and no output arguments are specified,
     91%             the default name, 'export_fig_out', is used. If neither a
     92%             file extension nor a format are specified, a ".png" is added
     93%             and the figure saved in that format.
     94%   -<format> - string(s) containing the output file extension(s). Options:
     95%             '-pdf', '-eps', '-svg', '-png', '-tif', '-jpg' and '-bmp'.
     96%             Multiple formats can be specified, without restriction.
     97%             For example: export_fig('-jpg', '-pdf', '-png', ...)
     98%             Either '-tif','-tiff' can be specified, and either '-jpg','-jpeg'.
     99%   -nocrop - option indicating that empty margins should not be cropped.
    93100%   -c[<val>,<val>,<val>,<val>] - option indicating crop amounts. Must be
    94101%             a 4-element vector of numeric values: [top,right,bottom,left]
    95102%             where NaN/Inf indicate auto-cropping, 0 means no cropping,
    96103%             and any other value mean cropping in pixel amounts.
    97 %   -transparent - option indicating that the figure background is to be
    98 %                  made transparent (png, pdf, tif and eps output only).
     104%   -transparent - option indicating that the figure background is to be made
     105%             transparent (PNG,PDF,TIF,EPS formats only). Implies -noinvert.
    99106%   -m<val> - option where val indicates the factor to magnify the
    100107%             on-screen figure pixel dimensions by when generating bitmap
     
    116123%             effects on image quality (disable with the -a1 option).
    117124%   -a1, -a2, -a3, -a4 - option indicating the amount of anti-aliasing to
    118 %                        use for bitmap outputs. '-a1' means no anti-
    119 %                        aliasing; '-a4' is the maximum amount (default).
     125%             use for bitmap outputs. '-a1' means no anti-aliasing;
     126%             '-a4' is the maximum amount (default).
    120127%   -<renderer> - option to force a particular renderer (painters, opengl or
    121 %                 zbuffer). Default value: opengl for bitmap formats or
    122 %                 figures with patches and/or transparent annotations;
    123 %                 painters for vector formats without patches/transparencies.
     128%             zbuffer). Default value: opengl for bitmap formats or
     129%             figures with patches and/or transparent annotations;
     130%             painters for vector formats without patches/transparencies.
    124131%   -<colorspace> - option indicating which colorspace color figures should
    125 %                   be saved in: RGB (default), CMYK or gray. CMYK is only
    126 %                   supported in pdf, eps and tiff output.
    127 %   -q<val> - option to vary bitmap image quality (in pdf, eps and jpg
    128 %             files only).  Larger val, in the range 0-100, gives higher
    129 %             quality/lower compression. val > 100 gives lossless
    130 %             compression. Default: '-q95' for jpg, ghostscript prepress
    131 %             default for pdf & eps. Note: lossless compression can
    132 %             sometimes give a smaller file size than the default lossy
    133 %             compression, depending on the type of images.
     132%             be saved in: RGB (default), CMYK or gray. Usage example: '-gray'.
     133%             Note: CMYK is only supported in PDF, EPS and TIF formats.
     134%   -q<val> - option to vary bitmap image quality (PDF, EPS, JPG formats only).
     135%             A larger val, in the range 0-100, produces higher quality and
     136%             lower compression. val > 100 results in lossless compression.
     137%             Default: '-q95' for JPG, ghostscript prepress default for PDF,EPS.
     138%             Note: lossless compression can sometimes give a smaller file size
     139%             than the default lossy compression, depending on the image type.
    134140%   -p<val> - option to pad a border of width val to exported files, where
    135141%             val is either a relative size with respect to cropped image
     
    139145%             If used, the -nocrop flag will be ignored, i.e. the image will
    140146%             always be cropped and then padded. Default: 0 (i.e. no padding).
    141 %   -append - option indicating that if the file (pdfs only) already
    142 %             exists, the figure is to be appended as a new page, instead
    143 %             of being overwritten (default).
     147%   -append - option indicating that if the file already exists the figure is to
     148%             be appended as a new page, instead of being overwritten (default).
     149%             PDF & TIF output formats only.
    144150%   -bookmark - option to indicate that a bookmark with the name of the
    145 %               figure is to be created in the output file (pdf only).
     151%             figure is to be created in the output file (PDF format only).
    146152%   -clipboard - option to save output as an image on the system clipboard.
    147 %                Note: background transparency is not preserved in clipboard
     153%             Note: background transparency is not preserved in clipboard
    148154%   -d<gs_option> - option to indicate a ghostscript setting. For example,
    149 %                   -dMaxBitmap=0 or -dNoOutputFonts (Ghostscript 9.15+).
     155%             -dMaxBitmap=0 or -dNoOutputFonts (Ghostscript 9.15+).
    150156%   -depsc -  option to use EPS level-3 rather than the default level-2 print
    151157%             device. This solves some bugs with Matlab's default -depsc2 device
     
    155161%             done in vector formats (only): 11 standard Matlab fonts are
    156162%             replaced by the original figure fonts. This option prevents this.
     163%   -font_space <char> - option to set a spacer character for font-names that
     164%             contain spaces, used by EPS/PDF. Default: ''
    157165%   -linecaps - option to create rounded line-caps (vector formats only).
     166%   -noinvert - option to avoid setting figure's InvertHardcopy property to
     167%             'off' during output (this solves some problems of empty outputs).
     168%   -preserve_size - option to preserve the figure's PaperSize property in output
     169%             file (PDF/EPS formats only; default is to not preserve it).
     170%   -options <optionsStruct> - format-specific parameters as defined in Matlab's
     171%             documentation of the imwrite function, contained in a struct under
     172%             the format name. For example to specify the JPG Comment parameter,
     173%             pass a struct such as this: options.JPG.Comment='abc'. Similarly,
     174%             options.PNG.BitDepth=4. Valid only for PNG,TIF,JPG output formats.
    158175%   handle -  The handle of the figure, axes or uipanels (can be an array of
    159 %             handles, but the objects must be in the same figure) to be
    160 %             saved. Default: gcf.
     176%             handles, but the objects must be in the same figure) which is
     177%             to be saved. Default: gcf (handle of current figure).
    161178%
    162179% Outputs:
     
    248265% 22/03/17: Fixed issue #187: only set manual ticks when no exponent is present
    249266% 09/04/17: Added -linecaps option (idea by Baron Finer, issue #192)
     267% 15/09/17: Fixed issue #205: incorrect tick-labels when Ticks number don't match the TickLabels number
     268% 15/09/17: Fixed issue #210: initialize alpha map to ones instead of zeros when -transparent is not used
     269% 18/09/17: Added -font_space option to replace font-name spaces in EPS/PDF (workaround for issue #194)
     270% 18/09/17: Added -noinvert option to solve some export problems with some graphic cards (workaround for issue #197)
     271% 08/11/17: Fixed issue #220: axes exponent is removed in HG1 when TickMode is 'manual' (internal Matlab bug)
     272% 08/11/17: Fixed issue #221: alert if the requested folder does not exist
     273% 19/11/17: Workaround for issue #207: alert when trying to use transparent bgcolor with -opengl
     274% 29/11/17: Workaround for issue #206: warn if exporting PDF/EPS for a figure that contains an image
     275% 11/12/17: Fixed issue #230: use OpenGL renderer when exported image contains transparency (also see issue #206)
     276% 30/01/18: Updated SVG message to point to https://github.com/kupiqu/plot2svg and display user-selected filename if available
     277% 27/02/18: Fixed issue #236: axes exponent cropped from output if on right-hand axes
     278% 29/05/18: Fixed issue #245: process "string" inputs just like 'char' inputs
     279% 13/08/18: Fixed issue #249: correct black axes color to off-black to avoid extra cropping with -transparent
     280% 27/08/18: Added a possible file-open reason in EPS/PDF write-error message (suggested by "craq" on FEX page)
     281% 22/09/18: Xpdf website changed to xpdfreader.com
     282% 23/09/18: Fixed issue #243: only set non-bold font (workaround for issue #69) in R2015b or earlier; warn if changing font
     283% 23/09/18: Workaround for issue #241: don't use -r864 in EPS/PDF outputs when -native is requested (solves black lines problem)
     284% 18/11/18: Issue #261: Added informative alert when trying to export a uifigure (which is not currently supported)
     285% 13/12/18: Issue #261: Fixed last commit for cases of specifying axes/panel handle as input, rather than a figure handle
     286% 13/01/19: Issue #72: Added basic SVG output support
     287% 04/02/19: Workaround for issues #207 and #267: -transparent implies -noinvert
     288% 08/03/19: Issue #269: Added ability to specify format-specific options for PNG,TIF,JPG outputs; fixed help section
     289% 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)
     290% 12/06/19: Issue #277: Enabled preservation of figure's PaperSize in output PDF/EPS file
     291% 06/08/19: Remove warning message about obsolete JavaFrame in R2019b
     292% 30/10/19: Fixed issue #261: added support for exporting uifigures and uiaxes (thanks to idea by @MarvinILA)
    250293%}
    251294
     
    266309    % Ensure that we have a figure handle
    267310    if isequal(fig,-1)
    268         return;  % silent bail-out
     311        return  % silent bail-out
    269312    elseif isempty(fig)
    270313        error('No figure found');
     314    else
     315        oldWarn = warning('off','MATLAB:HandleGraphics:ObsoletedProperty:JavaFrame');
     316        warning off MATLAB:ui:javaframe:PropertyToBeRemoved
     317        uifig = handle(ancestor(fig,'figure'));
     318        try jf = get(uifig,'JavaFrame'); catch, jf=1; end
     319        warning(oldWarn);
     320        if isempty(jf)  % this is a uifigure
     321            %error('Figures created using the uifigure command or App Designer are not supported by export_fig. See <a href="https://github.com/altmany/export_fig/issues/261">issue #261</a> for details.');
     322            if numel(fig) > 1
     323                error('export_fig:uifigure:multipleHandles', 'export_fig only supports exporting a single uifigure handle at a time; array of handles is not currently supported.')
     324            elseif ~any(strcmpi(fig.Type,{'figure','axes'}))
     325                error('export_fig:uifigure:notFigureOrAxes', 'export_fig only supports exporting a uifigure or uiaxes handle; other handles of a uifigure are not currently supported.')
     326            end
     327            % fig is either a uifigure or uiaxes handle
     328            isUiaxes = strcmpi(fig.Type,'axes');
     329            if isUiaxes
     330                % Label the specified axes so that we can find it in the legacy figure
     331                oldUserData = fig.UserData;
     332                tempStr = tempname;
     333                fig.UserData = tempStr;
     334            end
     335            try
     336                % Create an invisible legacy figure at the same position/size as the uifigure
     337                hNewFig = figure('Units',uifig.Units, 'Position',uifig.Position, 'MenuBar','none', 'ToolBar','none', 'Visible','off');
     338                % Copy the uifigure contents onto the new invisible legacy figure
     339                try
     340                    hChildren = allchild(uifig); %=uifig.Children;
     341                    copyobj(hChildren,hNewFig);
     342                catch
     343                    warning('export_fig:uifigure:controls', 'Some uifigure controls cannot be exported by export_fig and will not appear in the generated output.');
     344                end
     345                try fig.UserData = oldUserData; catch, end  % restore axes UserData, if modified above
     346                % Replace the uihandle in the input args with the legacy handle
     347                if isUiaxes  % uiaxes
     348                    % Locate the corresponding axes handle in the new legacy figure
     349                    hAxes = findall(hNewFig,'type','axes','UserData',tempStr);
     350                    if isempty(hAxes) % should never happen, check just in case
     351                        hNewHandle = hNewFig;  % export the figure instead of the axes
     352                    else
     353                        hNewHandle = hAxes;  % new axes handle found: use it instead of the uiaxes
     354                    end
     355                else  % uifigure
     356                    hNewHandle = hNewFig;
     357                end
     358                varargin(cellfun(@(c)isequal(c,fig),varargin)) = {hNewHandle};
     359                % Rerun export_fig on the legacy figure (with the replaced handle)
     360                [imageData, alpha] = export_fig(varargin{:});
     361                % Delete the temp legacy figure and bail out
     362                try delete(hNewFig); catch, end
     363                return
     364            catch err
     365                % Clean up the temp legacy figure and report the error
     366                try delete(hNewFig); catch, end
     367                rethrow(err)
     368            end
     369        end
    271370    end
    272371
     
    337436            % Set the FontWeight of axes labels/titles to 'normal'
    338437            % Fix issue #69: set non-bold font only if the string contains symbols (\beta etc.)
    339             texLabels = findall(fig, 'type','text', 'FontWeight','bold');
    340             symbolIdx = ~cellfun('isempty',strfind({texLabels.String},'\'));
    341             set(texLabels(symbolIdx), 'FontWeight','normal');
     438            % Issue #243: only set non-bold font (workaround for issue #69) in R2015b or earlier
     439            try isPreR2016a = verLessThan('matlab','8.7'); catch, isPreR2016a = true; end
     440            if isPreR2016a
     441                texLabels = findall(fig, 'type','text', 'FontWeight','bold');
     442                symbolIdx = ~cellfun('isempty',strfind({texLabels.String},'\'));
     443                if ~isempty(symbolIdx)
     444                    set(texLabels(symbolIdx), 'FontWeight','normal');
     445                    warning('export_fig:BoldTexLabels', 'Bold labels with Tex symbols converted into non-bold in export_fig (fix for issue #69)');
     446                end
     447            end
    342448        end
    343449    catch
     
    365471
    366472    % Set to print exactly what is there
    367     set(fig, 'InvertHardcopy', 'off');
     473    if options.invert_hardcopy
     474        try set(fig, 'InvertHardcopy', 'off'); catch, end  % fail silently in uifigures
     475    end
     476
    368477    % Set the renderer
    369478    switch options.renderer
     
    377486            renderer = '-opengl'; % Default for bitmaps
    378487    end
     488
     489    hImages = findall(fig,'type','image');
    379490
    380491    % Handle transparent patches
     
    390501        elseif ~options.png && ~options.tif  % issue #168
    391502            warning('export_fig:transparency', '%s\nTo export the transparency correctly, try using the ScreenCapture utility on the Matlab File Exchange: http://bit.ly/1QFrBip', msg);
     503        end
     504    elseif ~isempty(hImages)
     505        % Fix for issue #230: use OpenGL renderer when exported image contains transparency
     506        for idx = 1 : numel(hImages)
     507            cdata = get(hImages(idx),'CData');
     508            if any(isnan(cdata(:)))
     509                hasTransparency = true;
     510                break
     511            end
    392512        end
    393513    end
     
    428548                set(hCB(yCol==0), 'YColor', [0 0 0]);
    429549                set(hCB(xCol==0), 'XColor', [0 0 0]);
     550                % Correct black axes color to off-black (issue #249)
     551                hAxes = findall(fig, 'Type','axes');
     552                hXs = fixBlackAxle(hAxes, 'XColor');
     553                hYs = fixBlackAxle(hAxes, 'YColor');
     554                hZs = fixBlackAxle(hAxes, 'ZColor');
    430555
    431556                % The following code might cause out-of-memory errors
     
    445570                set(hCB(yCol==3), 'YColor', [1 1 1]);
    446571                set(hCB(xCol==3), 'XColor', [1 1 1]);
     572                % Revert the black axes colors
     573                set(hXs, 'XColor', [0,0,0]);
     574                set(hYs, 'YColor', [0,0,0]);
     575                set(hZs, 'ZColor', [0,0,0]);
    447576
    448577                % The following code might cause out-of-memory errors
     
    487616                    res = options.magnify * get(0, 'ScreenPixelsPerInch') / 25.4e-3;
    488617                    % Save the png
    489                     imwrite(A, [options.name '.png'], 'Alpha', double(alpha), 'ResolutionUnit', 'meter', 'XResolution', res, 'YResolution', res);
     618                    [format_options, bitDepth] = getFormatOptions(options, 'png');  %Issue #269
     619                    if ~isempty(bitDepth) && bitDepth < 16 && size(A,3) == 3
     620                        % BitDepth specification requires using a color-map
     621                        [A, map] = rgb2ind(A, 256);
     622                        imwrite(A, map, [options.name '.png'], 'Alpha',double(alpha), 'ResolutionUnit','meter', 'XResolution',res, 'YResolution',res, format_options{:});
     623                    else
     624                        imwrite(A, [options.name '.png'], 'Alpha',double(alpha), 'ResolutionUnit','meter', 'XResolution',res, 'YResolution',res, format_options{:});
     625                    end
    490626                    % Clear the png bit
    491627                    options.png = false;
     
    544680                if options.alpha
    545681                    imageData = A;
    546                     alpha = zeros(size(A, 1), size(A, 2), 'single');
     682                    alpha = ones(size(A, 1), size(A, 2), 'single');
    547683                end
    548684            end
     
    550686            if options.png
    551687                res = options.magnify * get(0, 'ScreenPixelsPerInch') / 25.4e-3;
    552                 imwrite(A, [options.name '.png'], 'ResolutionUnit', 'meter', 'XResolution', res, 'YResolution', res);
     688                [format_options, bitDepth] = getFormatOptions(options, 'png');  %Issue #269
     689                if ~isempty(bitDepth) && bitDepth < 16 && size(A,3) == 3
     690                    % BitDepth specification requires using a color-map
     691                    [A, map] = rgb2ind(A, 256);
     692                    imwrite(A, map, [options.name '.png'], 'ResolutionUnit','meter', 'XResolution',res, 'YResolution',res, format_options{:});
     693                else
     694                    imwrite(A, [options.name '.png'], 'ResolutionUnit','meter', 'XResolution',res, 'YResolution',res, format_options{:});
     695                end
    553696            end
    554697            if options.bmp
     
    561704                    quality = 95;
    562705                end
     706                format_options = getFormatOptions(options, 'jpg');  %Issue #269
    563707                if quality > 100
    564                     imwrite(A, [options.name '.jpg'], 'Mode', 'lossless');
     708                    imwrite(A, [options.name '.jpg'], 'Mode','lossless', format_options{:});
    565709                else
    566                     imwrite(A, [options.name '.jpg'], 'Quality', quality);
     710                    imwrite(A, [options.name '.jpg'], 'Quality',quality, format_options{:});
    567711                end
    568712            end
     
    580724                end
    581725                append_mode = {'overwrite', 'append'};
    582                 imwrite(A, [options.name '.tif'], 'Resolution', options.magnify*get(0, 'ScreenPixelsPerInch'), 'WriteMode', append_mode{options.append+1});
     726                format_options = getFormatOptions(options, 'tif');  %Issue #269
     727                imwrite(A, [options.name '.tif'], 'Resolution',options.magnify*get(0,'ScreenPixelsPerInch'), 'WriteMode',append_mode{options.append+1}, format_options{:});
    583728            end
    584729        end
     
    590735                if hasTransparency || hasPatches
    591736                    % This is *MUCH* slower, but more accurate for patches and transparent annotations (issue #39)
    592                     renderer = '-painters'; %ISSM fix
     737                    renderer = '-opengl';
    593738                else
    594739                    renderer = '-painters';
     
    624769            end
    625770            % Generate the options for print
    626             p2eArgs = {renderer, sprintf('-r%d', options.resolution)};
     771            printArgs = {renderer};
     772            if ~isempty(options.resolution)  % issue #241
     773                printArgs{end+1} = sprintf('-r%d', options.resolution);
     774            end
    627775            if options.colourspace == 1  % CMYK
    628776                % Issue #33: due to internal bugs in Matlab's print() function, we can't use its -cmyk option
    629                 %p2eArgs{end+1} = '-cmyk';
     777                %printArgs{end+1} = '-cmyk';
    630778            end
    631779            if ~options.crop
    632780                % Issue #56: due to internal bugs in Matlab's print() function, we can't use its internal cropping mechanism,
    633781                % therefore we always use '-loose' (in print2eps.m) and do our own cropping (in crop_borders)
    634                 %p2eArgs{end+1} = '-loose';
     782                %printArgs{end+1} = '-loose';
    635783            end
    636784            if any(strcmpi(varargin,'-depsc'))
    637785                % Issue #45: lines in image subplots are exported in invalid color.
    638786                % The workaround is to use the -depsc parameter instead of the default -depsc2
    639                 p2eArgs{end+1} = '-depsc';
     787                printArgs{end+1} = '-depsc';
    640788            end
    641789            try
     790                % Remove background if requested (issue #207)
     791                originalBgColor = get(fig, 'Color');
     792                [hXs, hYs, hZs] = deal([]);
     793                if options.transparent %&& ~isequal(get(fig, 'Color'), 'none')
     794                    if options.renderer == 1  % OpenGL
     795                        warning('export_fig:openglTransparentBG', '-opengl sometimes fails to produce transparent backgrounds; in such a case, try to use -painters instead');
     796                    end
     797
     798                    % Fix for issue #207, #267 (corrected)
     799                    set(fig,'Color','none');
     800
     801                    % Correct black axes color to off-black (issue #249)
     802                    hAxes = findall(fig, 'Type','axes');
     803                    hXs = fixBlackAxle(hAxes, 'XColor');
     804                    hYs = fixBlackAxle(hAxes, 'YColor');
     805                    hZs = fixBlackAxle(hAxes, 'ZColor');
     806                end
    642807                % Generate an eps
    643                 print2eps(tmp_nam, fig, options, p2eArgs{:});
     808                print2eps(tmp_nam, fig, options, printArgs{:});
     809                % {
    644810                % Remove the background, if desired
    645                 if options.transparent && ~isequal(get(fig, 'Color'), 'none')
     811                if options.transparent %&& ~isequal(get(fig, 'Color'), 'none')
    646812                    eps_remove_background(tmp_nam, 1 + using_hg2(fig));
    647                 end
     813
     814                    % Revert the black axes colors
     815                    set(hXs, 'XColor', [0,0,0]);
     816                    set(hYs, 'YColor', [0,0,0]);
     817                    set(hZs, 'ZColor', [0,0,0]);
     818                end
     819                %}
     820                % Restore the figure's previous background color (if modified)
     821                try set(fig,'Color',originalBgColor); drawnow; catch, end
    648822                % Fix colorspace to CMYK, if requested (workaround for issue #33)
    649823                if options.colourspace == 1  % CMYK
     
    671845                    % Alert in case of error creating output PDF/EPS file (issue #179)
    672846                    if exist(pdf_nam_tmp, 'file')
    673                         error(['Could not create ' pdf_nam ' - perhaps the folder does not exist, or you do not have write permissions']);
     847                        errMsg = ['Could not create ' pdf_nam ' - perhaps the folder does not exist, or you do not have write permissions, or the file is open in another application'];
     848                        error(errMsg);
    674849                    else
    675850                        error('Could not generate the intermediary EPS file.');
     
    677852                end
    678853            catch ex
     854                % Restore the figure's previous background color (in case it was not already restored)
     855                try set(fig,'Color',originalBgColor); drawnow; catch, end
    679856                % Delete the eps
    680857                delete(tmp_nam);
     858                % Rethrow the EPS/PDF-generation error
    681859                rethrow(ex);
    682860            end
     
    718896                end
    719897            end
     898            % Issue #206: warn if the figure contains an image
     899            if ~isempty(hImages) && strcmpi(renderer,'-opengl')  % see addendum to issue #206
     900                warnMsg = ['exporting images to PDF/EPS may result in blurry images on some viewers. ' ...
     901                           'If so, try to change viewer, or increase the image''s CData resolution, or use -opengl renderer, or export via the print function. ' ...
     902                           'See <a href="matlab:web(''https://github.com/altmany/export_fig/issues/206'',''-browser'');">issue #206</a> for details.'];
     903                warning('export_fig:pdf_eps:blurry_image', warnMsg);
     904            end
     905        end
     906
     907        % SVG format
     908        if options.svg
     909            oldUnits = get(fig,'Units');
     910            filename = [options.name '.svg'];
     911            % Adapted from Dan Joshea's https://github.com/djoshea/matlab-save-figure :
     912            try %if verLessThan('matlab', '8.4')
     913                % Try using the fig2svg/plot2svg utilities
     914                try
     915                    fig2svg(filename, fig);  %https://github.com/kupiqu/fig2svg
     916                catch
     917                    plot2svg(filename, fig); %https://github.com/jschwizer99/plot2svg
     918                    warning('export_fig:SVG:plot2svg', 'export_fig used the plot2svg utility for SVG output. Better results may be gotten via the fig2svg utility (https://github.com/kupiqu/fig2svg).');
     919                end
     920            catch %else  % (neither fig2svg nor plot2svg are available)
     921                % Try Matlab's built-in svg engine (from Batik Graphics2D for java)
     922                try
     923                    set(fig,'Units','pixels');   % All data in the svg-file is saved in pixels
     924                    printArgs = {renderer};
     925                    if ~isempty(options.resolution)
     926                        printArgs{end+1} = sprintf('-r%d', options.resolution);
     927                    end
     928                    print(fig, '-dsvg', printArgs{:}, filename);
     929                    warning('export_fig:SVG:print', 'export_fig used Matlab''s built-in SVG output engine. Better results may be gotten via the fig2svg utility (https://github.com/kupiqu/fig2svg).');
     930                catch err  % built-in print() failed - maybe an old Matlab release (no -dsvg)
     931                    set(fig,'Units',oldUnits);
     932                    filename = strrep(filename,'export_fig_out','filename');
     933                    msg = ['SVG output is not supported for your figure: ' err.message '\n' ...
     934                        'Try one of the following alternatives:\n' ...
     935                        '  1. saveas(gcf,''' filename ''')\n' ...
     936                        '  2. fig2svg utility: https://github.com/kupiqu/fig2svg\n' ...  % Note: replaced defunct https://github.com/jschwizer99/plot2svg with up-to-date fork on https://github.com/kupiqu/fig2svg
     937                        '  3. export_fig to EPS/PDF, then convert to SVG using non-Matlab tools\n'];
     938                    error(sprintf(msg)); %#ok<SPERR>
     939                end
     940            end
     941            % SVG output was successful if we reached this point
     942            % Restore original figure units
     943            set(fig,'Units',oldUnits);
     944            % Add warning about unsupported export_fig options with SVG output
     945            if any(~isnan(options.crop_amounts)) || any(options.bb_padding)
     946                warning('export_fig:SVG:options', 'export_fig''s SVG output does not [currently] support cropping/padding.');
     947            end
    720948        end
    721949
     
    726954        else
    727955            % Reset the hardcopy mode
    728             set(fig, 'InvertHardcopy', old_mode);
     956            try set(fig, 'InvertHardcopy', old_mode); catch, end  % fail silently in uifigures
    729957            % Reset the axes limit and tick modes
    730958            for a = 1:numel(Hlims)
     
    7751003                error(javachk('awt', 'export_fig -clipboard output'));
    7761004            catch
    777                 warning('export_fig -clipboard output failed: requires Java to work');
     1005                warning('export_fig:clipboardJava', 'export_fig -clipboard output failed: requires Java to work');
    7781006                return;
    7791007            end
     
    8211049                cb.setContents(imSelection, []);
    8221050            catch
    823                 warning('export_fig -clipboard output failed: %s', lasterr); %#ok<LERR>
     1051                warning('export_fig:clipboardFailed', 'export_fig -clipboard output failed: %s', lasterr); %#ok<LERR>
    8241052            end
    8251053        end
     
    8351063            fprintf(2, 'Please ensure:\n');
    8361064            fprintf(2, '  that you are using the <a href="https://github.com/altmany/export_fig/archive/master.zip">latest version</a> of export_fig\n');
    837             if ismac
    838                 fprintf(2, '  and that you have <a href="http://pages.uoregon.edu/koch">Ghostscript</a> installed\n');
    839             else
    840                 fprintf(2, '  and that you have <a href="http://www.ghostscript.com">Ghostscript</a> installed\n');
     1065            if isvector(options)
     1066                if ismac
     1067                    fprintf(2, '  and that you have <a href="http://pages.uoregon.edu/koch">Ghostscript</a> installed\n');
     1068                else
     1069                    fprintf(2, '  and that you have <a href="http://www.ghostscript.com">Ghostscript</a> installed\n');
     1070                end
    8411071            end
    8421072            try
    8431073                if options.eps
    844                     fprintf(2, '  and that you have <a href="http://www.foolabs.com/xpdf">pdftops</a> installed\n');
     1074                    fprintf(2, '  and that you have <a href="http://xpdfreader.com/download.html">pdftops</a> installed\n');
    8451075                end
    8461076            catch
     
    8661096    % Default options used by export_fig
    8671097    options = struct(...
    868         'name',         'export_fig_out', ...
    869         'crop',         true, ...
    870         'crop_amounts', nan(1,4), ...  % auto-crop all 4 image sides
    871         'transparent',  false, ...
    872         'renderer',     0, ...         % 0: default, 1: OpenGL, 2: ZBuffer, 3: Painters
    873         'pdf',          false, ...
    874         'eps',          false, ...
    875         'png',          false, ...
    876         'tif',          false, ...
    877         'jpg',          false, ...
    878         'bmp',          false, ...
    879         'clipboard',    false, ...
    880         'colourspace',  0, ...         % 0: RGB/gray, 1: CMYK, 2: gray
    881         'append',       false, ...
    882         'im',           false, ...
    883         'alpha',        false, ...
    884         'aa_factor',    0, ...
    885         'bb_padding',   0, ...
    886         'magnify',      [], ...
    887         'resolution',   [], ...
    888         'bookmark',     false, ...
    889         'closeFig',     false, ...
    890         'quality',      [], ...
    891         'update',       false, ...
    892         'fontswap',     true, ...
    893         'linecaps',     false, ...
    894         'gs_options',   {{}});
     1098        'name',            'export_fig_out', ...
     1099        'crop',            true, ...
     1100        'crop_amounts',    nan(1,4), ...  % auto-crop all 4 image sides
     1101        'transparent',     false, ...
     1102        'renderer',        0, ...         % 0: default, 1: OpenGL, 2: ZBuffer, 3: Painters
     1103        'pdf',             false, ...
     1104        'eps',             false, ...
     1105        'svg',             false, ...
     1106        'png',             false, ...
     1107        'tif',             false, ...
     1108        'jpg',             false, ...
     1109        'bmp',             false, ...
     1110        'clipboard',       false, ...
     1111        'colourspace',     0, ...         % 0: RGB/gray, 1: CMYK, 2: gray
     1112        'append',          false, ...
     1113        'im',              false, ...
     1114        'alpha',           false, ...
     1115        'aa_factor',       0, ...
     1116        'bb_padding',      0, ...
     1117        'magnify',         [], ...
     1118        'resolution',      [], ...
     1119        'bookmark',        false, ...
     1120        'closeFig',        false, ...
     1121        'quality',         [], ...
     1122        'update',          false, ...
     1123        'fontswap',        true, ...
     1124        'font_space',      '', ...
     1125        'linecaps',        false, ...
     1126        'invert_hardcopy', true, ...
     1127        'format_options',  struct, ...
     1128        'preserve_size',   false, ...
     1129        'gs_options',      {{}});
    8951130end
    8961131
    8971132function [fig, options] = parse_args(nout, fig, varargin)
    8981133    % Parse the input arguments
     1134
     1135    % Convert strings => chars
     1136    varargin = cellfun(@str2char,varargin,'un',false);
    8991137
    9001138    % Set the defaults
     
    9311169                    case 'eps'
    9321170                        options.eps = true;
     1171                    case 'svg'
     1172                        options.svg = true;
    9331173                    case 'png'
    9341174                        options.png = true;
     
    9571197                        options.im = true;
    9581198                        options.alpha = true;
    959                     case 'svg'
    960                         msg = ['SVG output is not supported by export_fig. Use one of the following alternatives:\n' ...
    961                                '  1. saveas(gcf,''filename.svg'')\n' ...
    962                                '  2. plot2svg utility: http://github.com/jschwizer99/plot2svg\n' ...
    963                                '  3. export_fig to EPS/PDF, then convert to SVG using generic (non-Matlab) tools\n'];
    964                         error(sprintf(msg)); %#ok<SPERR>
    9651199                    case 'update'
    9661200                        % Download the latest version of export_fig into the export_fig folder
     
    9691203                            folderName = fileparts(which(mfilename('fullpath')));
    9701204                            targetFileName = fullfile(folderName, datestr(now,'yyyy-mm-dd.zip'));
    971                             urlwrite(zipFileName,targetFileName);
     1205                            urlwrite(zipFileName,targetFileName); %#ok<URLWR>
    9721206                        catch
    9731207                            error('Could not download %s into %s\n',zipFileName,targetFileName);
     
    9821216                    case 'nofontswap'
    9831217                        options.fontswap = false;
     1218                    case 'font_space'
     1219                        options.font_space = varargin{a+1};
     1220                        skipNext = true;
    9841221                    case 'linecaps'
    9851222                        options.linecaps = true;
     1223                    case 'noinvert'
     1224                        options.invert_hardcopy = false;
     1225                    case 'preserve_size'
     1226                        options.preserve_size = true;
     1227                    case 'options'
     1228                        % Issue #269: format-specific options
     1229                        inputOptions = varargin{a+1};
     1230                        %options.format_options  = inputOptions;
     1231                        if isempty(inputOptions), continue, end
     1232                        formats = fieldnames(inputOptions(1));
     1233                        for idx = 1 : numel(formats)
     1234                            optionsStruct = inputOptions.(formats{idx});
     1235                            %optionsCells = [fieldnames(optionsStruct) struct2cell(optionsStruct)]';
     1236                            formatName = regexprep(lower(formats{idx}),{'tiff','jpeg'},{'tif','jpg'});
     1237                            options.format_options.(formatName) = optionsStruct; %=optionsCells(:)';
     1238                        end
     1239                        skipNext = true;
    9861240                    otherwise
    9871241                        try
     
    10441298                [p, options.name, ext] = fileparts(varargin{a});
    10451299                if ~isempty(p)
     1300                    % Issue #221: alert if the requested folder does not exist
     1301                    if ~exist(p,'dir'),  error(['Folder ' p ' does not exist!']);  end
    10461302                    options.name = [p filesep options.name];
    10471303                end
     
    10721328                        end
    10731329                    case '.svg'
    1074                         msg = ['SVG output is not supported by export_fig. Use one of the following alternatives:\n' ...
    1075                                '  1. saveas(gcf,''filename.svg'')\n' ...
    1076                                '  2. plot2svg utility: http://github.com/jschwizer99/plot2svg\n' ...
    1077                                '  3. export_fig to EPS/PDF, then convert to SVG using generic (non-Matlab) tools\n'];
    1078                         error(sprintf(msg)); %#ok<SPERR>
     1330                        options.svg = true;
    10791331                    otherwise
    10801332                        options.name = varargin{a};
     
    11271379    % If requested, set the resolution to the native vertical resolution of the
    11281380    % first suitable image found
    1129     if native && isbitmap(options)
    1130         % Find a suitable image
    1131         list = findall(fig, 'Type','image', 'Tag','export_fig_native');
    1132         if isempty(list)
    1133             list = findall(fig, 'Type','image', 'Visible','on');
    1134         end
    1135         for hIm = list(:)'
    1136             % Check height is >= 2
    1137             height = size(get(hIm, 'CData'), 1);
    1138             if height < 2
    1139                 continue
    1140             end
    1141             % Account for the image filling only part of the axes, or vice versa
    1142             yl = get(hIm, 'YData');
    1143             if isscalar(yl)
    1144                 yl = [yl(1)-0.5 yl(1)+height+0.5];
    1145             else
    1146                 yl = [min(yl), max(yl)];  % fix issue #151 (case of yl containing more than 2 elements)
    1147                 if ~diff(yl)
     1381    if native
     1382        if isbitmap(options)
     1383            % Find a suitable image
     1384            list = findall(fig, 'Type','image', 'Tag','export_fig_native');
     1385            if isempty(list)
     1386                list = findall(fig, 'Type','image', 'Visible','on');
     1387            end
     1388            for hIm = list(:)'
     1389                % Check height is >= 2
     1390                height = size(get(hIm, 'CData'), 1);
     1391                if height < 2
    11481392                    continue
    11491393                end
    1150                 yl = yl + [-0.5 0.5] * (diff(yl) / (height - 1));
    1151             end
    1152             hAx = get(hIm, 'Parent');
    1153             yl2 = get(hAx, 'YLim');
    1154             % Find the pixel height of the axes
    1155             oldUnits = get(hAx, 'Units');
    1156             set(hAx, 'Units', 'pixels');
    1157             pos = get(hAx, 'Position');
    1158             set(hAx, 'Units', oldUnits);
    1159             if ~pos(4)
    1160                 continue
    1161             end
    1162             % Found a suitable image
    1163             % Account for stretch-to-fill being disabled
    1164             pbar = get(hAx, 'PlotBoxAspectRatio');
    1165             pos = min(pos(4), pbar(2)*pos(3)/pbar(1));
    1166             % Set the magnification to give native resolution
    1167             options.magnify = abs((height * diff(yl2)) / (pos * diff(yl)));  % magnification must never be negative: issue #103
    1168             break
    1169         end
     1394                % Account for the image filling only part of the axes, or vice versa
     1395                yl = get(hIm, 'YData');
     1396                if isscalar(yl)
     1397                    yl = [yl(1)-0.5 yl(1)+height+0.5];
     1398                else
     1399                    yl = [min(yl), max(yl)];  % fix issue #151 (case of yl containing more than 2 elements)
     1400                    if ~diff(yl)
     1401                        continue
     1402                    end
     1403                    yl = yl + [-0.5 0.5] * (diff(yl) / (height - 1));
     1404                end
     1405                hAx = get(hIm, 'Parent');
     1406                yl2 = get(hAx, 'YLim');
     1407                % Find the pixel height of the axes
     1408                oldUnits = get(hAx, 'Units');
     1409                set(hAx, 'Units', 'pixels');
     1410                pos = get(hAx, 'Position');
     1411                set(hAx, 'Units', oldUnits);
     1412                if ~pos(4)
     1413                    continue
     1414                end
     1415                % Found a suitable image
     1416                % Account for stretch-to-fill being disabled
     1417                pbar = get(hAx, 'PlotBoxAspectRatio');
     1418                pos = min(pos(4), pbar(2)*pos(3)/pbar(1));
     1419                % Set the magnification to give native resolution
     1420                options.magnify = abs((height * diff(yl2)) / (pos * diff(yl)));  % magnification must never be negative: issue #103
     1421                break
     1422            end
     1423        elseif options.resolution == 864  % don't use -r864 in vector mode if user asked for -native
     1424            options.resolution = []; % issue #241 (internal Matlab bug produces black lines with -r864)
     1425        end
     1426    end
     1427end
     1428
     1429% Convert a possible string => char (issue #245)
     1430function value = str2char(value)
     1431    if isa(value,'string')
     1432        value = char(value);
    11701433    end
    11711434end
     
    13041567            hAxes = Hlims(idx(idx2));
    13051568            props = {[ax 'TickMode'],'manual', [ax 'TickLabelMode'],'manual'};
    1306             if isempty(strtrim(hAxes.([ax 'Ruler']).SecondaryLabel.String))
    1307                 set(hAxes, props{:});  % no exponent, so update moth ticks and tick labels to manual
     1569            tickVals = get(hAxes,[ax 'Tick']);
     1570            tickStrs = get(hAxes,[ax 'TickLabel']);
     1571            try % Fix issue #236
     1572                exponents = [hAxes.([ax 'Axis']).SecondaryLabel];
     1573            catch
     1574                exponents = [hAxes.([ax 'Ruler']).SecondaryLabel];
     1575            end
     1576            if isempty([exponents.String])
     1577                % Fix for issue #205 - only set manual ticks when the Ticks number match the TickLabels number
     1578                if numel(tickVals) == numel(tickStrs)
     1579                    set(hAxes, props{:});  % no exponent and matching ticks, so update both ticks and tick labels to manual
     1580                end
    13081581            end
    13091582        catch  % probably HG1
    1310             set(hAxes, props{:});  % revert back to old behavior
     1583            % Fix for issue #220 - exponent is removed in HG1 when TickMode is 'manual' (internal Matlab bug)
     1584            if isequal(tickVals, str2num(tickStrs)') %#ok<ST2NM>
     1585                set(hAxes, props{:});  % revert back to old behavior
     1586            end
    13111587        end
    13121588    end
     
    13321608    end
    13331609end
     1610
     1611function hBlackAxles = fixBlackAxle(hAxes, axleName)
     1612    hBlackAxles = [];
     1613    for idx = 1 : numel(hAxes)
     1614        ax = hAxes(idx);
     1615        axleColor = get(ax, axleName);
     1616        if isequal(axleColor,[0,0,0]) || isequal(axleColor,'k')
     1617            hBlackAxles(end+1) = ax; %#ok<AGROW>
     1618        end
     1619    end
     1620    set(hBlackAxles, axleName, [0,0,0.01]);  % off-black
     1621end
     1622
     1623% Issue #269: format-specific options
     1624function [optionsCells, bitDepth] = getFormatOptions(options, formatName)
     1625    bitDepth = [];
     1626    try
     1627        optionsStruct = options.format_options.(lower(formatName));
     1628    catch
     1629        % User did not specify any extra parameters for this format
     1630        optionsCells = {};
     1631        return
     1632    end
     1633    optionNames = fieldnames(optionsStruct);
     1634    optionVals  = struct2cell(optionsStruct);
     1635    optionsCells = [optionNames, optionVals]';
     1636    if nargout < 2, return, end  % bail out if BitDepth is not required
     1637    try
     1638        idx = find(strcmpi(optionNames,'BitDepth'), 1, 'last');
     1639        if ~isempty(idx)
     1640            bitDepth = optionVals{idx};
     1641        end
     1642    catch
     1643        % never mind - ignore
     1644    end
     1645end
Note: See TracChangeset for help on using the changeset viewer.