Home > data-vis > supporting_functions > export_fig > print2eps.m

print2eps

PURPOSE ^

PRINT2EPS Prints figures to eps with improved line styles

SYNOPSIS ^

function print2eps(name, fig, export_options, varargin)

DESCRIPTION ^

PRINT2EPS  Prints figures to eps with improved line styles

 Examples:
   print2eps filename
   print2eps(filename, fig_handle)
   print2eps(filename, fig_handle, export_options)
   print2eps(filename, fig_handle, export_options, print_options)

 This function saves a figure as an eps file, with two improvements over
 MATLAB's print command. First, it improves the line style, making dashed
 lines more like those on screen and giving grid lines a dotted line style.
 Secondly, it substitutes original font names back into the eps file,
 where these have been changed by MATLAB, for up to 11 different fonts.

IN:
   filename - string containing the name (optionally including full or
              relative path) of the file the figure is to be saved as. A
              ".eps" extension is added if not there already. If a path is
              not specified, the figure is saved in the current directory.
   fig_handle - The handle of the figure to be saved. Default: gcf().
   export_options - array or struct of optional scalar values:
       bb_padding - Scalar value of amount of padding to add to border around
                    the cropped image, in points (if >1) or percent (if <1).
                    Can be negative as well as positive; Default: 0
       crop       - Cropping flag. Deafult: 0
       fontswap   - Whether to swap non-default fonts in figure. Default: true
       renderer   - Renderer used to generate bounding-box. Default: 'opengl'
       crop_amounts - 4-element vector of crop amounts: [top,right,bottom,left]
                    (available only via the struct alternative)
   print_options - Additional parameter strings to be passed to the print command

CROSS-REFERENCE INFORMATION ^

This function calls: This function is called by:

SUBFUNCTIONS ^

SOURCE CODE ^

0001 function print2eps(name, fig, export_options, varargin)
0002 %PRINT2EPS  Prints figures to eps with improved line styles
0003 %
0004 % Examples:
0005 %   print2eps filename
0006 %   print2eps(filename, fig_handle)
0007 %   print2eps(filename, fig_handle, export_options)
0008 %   print2eps(filename, fig_handle, export_options, print_options)
0009 %
0010 % This function saves a figure as an eps file, with two improvements over
0011 % MATLAB's print command. First, it improves the line style, making dashed
0012 % lines more like those on screen and giving grid lines a dotted line style.
0013 % Secondly, it substitutes original font names back into the eps file,
0014 % where these have been changed by MATLAB, for up to 11 different fonts.
0015 %
0016 %IN:
0017 %   filename - string containing the name (optionally including full or
0018 %              relative path) of the file the figure is to be saved as. A
0019 %              ".eps" extension is added if not there already. If a path is
0020 %              not specified, the figure is saved in the current directory.
0021 %   fig_handle - The handle of the figure to be saved. Default: gcf().
0022 %   export_options - array or struct of optional scalar values:
0023 %       bb_padding - Scalar value of amount of padding to add to border around
0024 %                    the cropped image, in points (if >1) or percent (if <1).
0025 %                    Can be negative as well as positive; Default: 0
0026 %       crop       - Cropping flag. Deafult: 0
0027 %       fontswap   - Whether to swap non-default fonts in figure. Default: true
0028 %       renderer   - Renderer used to generate bounding-box. Default: 'opengl'
0029 %       crop_amounts - 4-element vector of crop amounts: [top,right,bottom,left]
0030 %                    (available only via the struct alternative)
0031 %   print_options - Additional parameter strings to be passed to the print command
0032 
0033 %{
0034 % Copyright (C) Oliver Woodford 2008-2014, Yair Altman 2015-
0035 
0036 % The idea of editing the EPS file to change line styles comes from Jiro
0037 % Doke's FIXPSLINESTYLE (fex id: 17928)
0038 % The idea of changing dash length with line width came from comments on
0039 % fex id: 5743, but the implementation is mine :)
0040 %}
0041 %{
0042 % 14/11/11: Fix a MATLAB bug rendering black or white text incorrectly.
0043 %           Thanks to Mathieu Morlighem for reporting the issue and
0044 %           obtaining a fix from TMW.
0045 % 08/12/11: Added ability to correct fonts. Several people have requested
0046 %           this at one time or another, and also pointed me to printeps
0047 %           (fex id: 7501), so thank you to them. My implementation (which
0048 %           was not inspired by printeps - I'd already had the idea for my
0049 %           approach) goes slightly further in that it allows multiple
0050 %           fonts to be swapped.
0051 % 14/12/11: Fix bug affecting font names containing spaces. Thanks to David
0052 %           Szwer for reporting the issue.
0053 % 25/01/12: Add a font not to be swapped. Thanks to Anna Rafferty and Adam
0054 %           Jackson for reporting the issue. Also fix a bug whereby using a
0055 %           font alias can lead to another font being swapped in.
0056 % 10/04/12: Make the font swapping case insensitive.
0057 % 26/10/12: Set PaperOrientation to portrait. Thanks to Michael Watts for
0058 %           reporting the issue.
0059 % 26/10/12: Fix issue to do with swapping fonts changing other fonts and
0060 %           sizes we don't want, due to listeners. Thanks to Malcolm Hudson
0061 %           for reporting the issue.
0062 % 22/03/13: Extend font swapping to axes labels. Thanks to Rasmus Ischebeck
0063 %           for reporting the issue.
0064 % 23/07/13: Bug fix to font swapping. Thanks to George for reporting the
0065 %           issue.
0066 % 13/08/13: Fix MATLAB feature of not exporting white lines correctly.
0067 %           Thanks to Sebastian Hesslinger for reporting it.
0068 % 24/02/15: Fix for Matlab R2014b bug (issue #31): LineWidths<0.75 are not
0069 %           set in the EPS (default line width is used)
0070 % 25/02/15: Fixed issue #32: BoundingBox problem caused uncropped EPS/PDF files
0071 % 05/03/15: Fixed issue #43: Inability to perform EPS file post-processing
0072 % 06/03/15: Improved image padding & cropping thanks to Oscar Hartogensis
0073 % 21/03/15: Fixed edge-case of missing handles having a 'FontName' property
0074 % 26/03/15: Attempt to fix issue #45: white lines in subplots do not print correctly
0075 % 27/03/15: Attempt to fix issue #44: white artifact lines appearing in patch exports
0076 % 30/03/15: Fixed issue #52: improved performance on HG2 (R2014b+)
0077 % 09/04/15: Comment blocks consolidation and minor code cleanup (no real code change)
0078 % 12/04/15: Fixed issue #56: bad cropping
0079 % 14/04/15: Workaround for issue #45: lines in image subplots are exported in invalid color
0080 % 07/07/15: Added option to avoid font-swapping in EPS/PDF
0081 % 07/07/15: Fixed issue #83: use numeric handles in HG1
0082 % 22/07/15: Fixed issue #91 (thanks to Carlos Moffat)
0083 % 28/09/15: Fixed issue #108 (thanks to JacobD10)
0084 % 01/11/15: Fixed issue #112: optional renderer for bounding-box computation (thanks to Jesús Pestana Puerta)
0085 % 21/02/16: Enabled specifying non-automated crop amounts
0086 % 22/02/16: Better support + backward compatibility for transparency (issue #108)
0087 % 10/06/16: Fixed issue #159: text handles get cleared by Matlab in the print() command
0088 % 12/06/16: Improved the fix for issue #159 (in the previous commit)
0089 % 12/06/16: Fixed issue #158: transparent patch color in PDF/EPS
0090 %}
0091 
0092     options = {'-loose'};
0093     if nargin > 3
0094         options = [options varargin];
0095     elseif nargin < 3
0096         export_options = 0;
0097         if nargin < 2
0098             fig = gcf();
0099         end
0100     end
0101 
0102     % Retrieve padding, crop & font-swap values
0103     crop_amounts = nan(1,4);  % auto-crop all 4 sides by default
0104     if isstruct(export_options)
0105         try fontswap     = export_options.fontswap;     catch, fontswap = true;     end
0106         try bb_crop      = export_options.crop;         catch, bb_crop = 0;         end
0107         try crop_amounts = export_options.crop_amounts; catch,                      end
0108         try bb_padding   = export_options.bb_padding;   catch, bb_padding = 0;      end
0109         try renderer     = export_options.rendererStr;  catch, renderer = 'opengl'; end  % fix for issue #110
0110         if renderer(1)~='-',  renderer = ['-' renderer];  end
0111     else
0112         if numel(export_options) > 2  % font-swapping
0113             fontswap = export_options(3);
0114         else
0115             fontswap = true;
0116         end
0117         if numel(export_options) > 1  % cropping
0118             bb_crop = export_options(2);
0119         else
0120             bb_crop = 0;  % scalar value, so use default bb_crop value of 0
0121         end
0122         if numel(export_options) > 0  % padding
0123             bb_padding = export_options(1);
0124         else
0125             bb_padding = 0;
0126         end
0127         renderer = '-opengl';
0128     end
0129 
0130     % Construct the filename
0131     if numel(name) < 5 || ~strcmpi(name(end-3:end), '.eps')
0132         name = [name '.eps']; % Add the missing extension
0133     end
0134 
0135     % Set paper size
0136     old_pos_mode = get(fig, 'PaperPositionMode');
0137     old_orientation = get(fig, 'PaperOrientation');
0138     set(fig, 'PaperPositionMode', 'auto', 'PaperOrientation', 'portrait');
0139 
0140     % Find all the used fonts in the figure
0141     font_handles = findall(fig, '-property', 'FontName');
0142     fonts = get(font_handles, 'FontName');
0143     if isempty(fonts)
0144         fonts = {};
0145     elseif ~iscell(fonts)
0146         fonts = {fonts};
0147     end
0148 
0149     % Map supported font aliases onto the correct name
0150     fontsl = lower(fonts);
0151     for a = 1:numel(fonts)
0152         f = fontsl{a};
0153         f(f==' ') = [];
0154         switch f
0155             case {'times', 'timesnewroman', 'times-roman'}
0156                 fontsl{a} = 'times-roman';
0157             case {'arial', 'helvetica'}
0158                 fontsl{a} = 'helvetica';
0159             case {'newcenturyschoolbook', 'newcenturyschlbk'}
0160                 fontsl{a} = 'newcenturyschlbk';
0161             otherwise
0162         end
0163     end
0164     fontslu = unique(fontsl);
0165 
0166     % Determine the font swap table
0167     if fontswap
0168         matlab_fonts = {'Helvetica', 'Times-Roman', 'Palatino', 'Bookman', 'Helvetica-Narrow', 'Symbol', ...
0169                         'AvantGarde', 'NewCenturySchlbk', 'Courier', 'ZapfChancery', 'ZapfDingbats'};
0170         matlab_fontsl = lower(matlab_fonts);
0171         require_swap = find(~ismember(fontslu, matlab_fontsl));
0172         unused_fonts = find(~ismember(matlab_fontsl, fontslu));
0173         font_swap = cell(3, min(numel(require_swap), numel(unused_fonts)));
0174         fonts_new = fonts;
0175         for a = 1:size(font_swap, 2)
0176             font_swap{1,a} = find(strcmp(fontslu{require_swap(a)}, fontsl));
0177             font_swap{2,a} = matlab_fonts{unused_fonts(a)};
0178             font_swap{3,a} = fonts{font_swap{1,a}(1)};
0179             fonts_new(font_swap{1,a}) = font_swap(2,a);
0180         end
0181     else
0182         font_swap = [];
0183     end
0184 
0185     % Swap the fonts
0186     if ~isempty(font_swap)
0187         fonts_size = get(font_handles, 'FontSize');
0188         if iscell(fonts_size)
0189             fonts_size = cell2mat(fonts_size);
0190         end
0191         M = false(size(font_handles));
0192 
0193         % Loop because some changes may not stick first time, due to listeners
0194         c = 0;
0195         update = zeros(1000, 1);
0196         for b = 1:10 % Limit number of loops to avoid infinite loop case
0197             for a = 1:numel(M)
0198                 M(a) = ~isequal(get(font_handles(a), 'FontName'), fonts_new{a}) || ~isequal(get(font_handles(a), 'FontSize'), fonts_size(a));
0199                 if M(a)
0200                     set(font_handles(a), 'FontName', fonts_new{a}, 'FontSize', fonts_size(a));
0201                     c = c + 1;
0202                     update(c) = a;
0203                 end
0204             end
0205             if ~any(M)
0206                 break;
0207             end
0208         end
0209 
0210         % Compute the order to revert fonts later, without the need of a loop
0211         [update, M] = unique(update(1:c));
0212         [M, M] = sort(M);
0213         update = reshape(update(M), 1, []);
0214     end
0215 
0216     % MATLAB bug fix - black and white text can come out inverted sometimes
0217     % Find the white and black text
0218     black_text_handles = findall(fig, 'Type', 'text', 'Color', [0 0 0]);
0219     white_text_handles = findall(fig, 'Type', 'text', 'Color', [1 1 1]);
0220     % Set the font colors slightly off their correct values
0221     set(black_text_handles, 'Color', [0 0 0] + eps);
0222     set(white_text_handles, 'Color', [1 1 1] - eps);
0223 
0224     % MATLAB bug fix - white lines can come out funny sometimes
0225     % Find the white lines
0226     white_line_handles = findall(fig, 'Type', 'line', 'Color', [1 1 1]);
0227     % Set the line color slightly off white
0228     set(white_line_handles, 'Color', [1 1 1] - 0.00001);
0229 
0230     % Workaround for issue #45: lines in image subplots are exported in invalid color
0231     % In this case the -depsc driver solves the problem, but then all the other workarounds
0232     % below (for all the other issues) will fail, so it's better to let the user decide by
0233     % just issuing a warning and accepting the '-depsc' input parameter
0234     epsLevel2 = ~any(strcmpi(options,'-depsc'));
0235     if epsLevel2
0236         % Use -depsc2 (EPS color level-2) if -depsc (EPS color level-3) was not specifically requested
0237         options{end+1} = '-depsc2';
0238         % Issue a warning if multiple images & lines were found in the figure, and HG1 with painters renderer is used
0239         isPainters = any(strcmpi(options,'-painters'));
0240         if isPainters && ~using_hg2 && numel(findall(fig,'Type','image'))>1 && ~isempty(findall(fig,'Type','line'))
0241             warning('YMA:export_fig:issue45', ...
0242                     ['Multiple images & lines detected. In such cases, the lines might \n' ...
0243                      'appear with an invalid color due to an internal MATLAB bug (fixed in R2014b). \n' ...
0244                      'Possible workaround: add a ''-depsc'' or ''-opengl'' parameter to the export_fig command.']);
0245         end
0246     end
0247 
0248     % Fix issue #83: use numeric handles in HG1
0249     if ~using_hg2(fig),  fig = double(fig);  end
0250 
0251     % Workaround for when transparency is lost through conversion fig>EPS>PDF (issue #108)
0252     % Replace transparent patch RGB values with an ID value (rare chance that ID color is being used already)
0253     if using_hg2
0254         origAlphaColors = eps_maintainAlpha(fig);
0255     end
0256 
0257     % Print to eps file
0258     print(fig, options{:}, name);
0259 
0260     % Do post-processing on the eps file
0261     try
0262         % Read the EPS file into memory
0263         fstrm = read_write_entire_textfile(name);
0264     catch
0265         fstrm = '';
0266     end
0267 
0268     % Restore colors for transparent patches/lines and apply the
0269     % setopacityalpha setting in the EPS file (issue #108)
0270     if using_hg2
0271         [~,fstrm,foundFlags] = eps_maintainAlpha(fig, fstrm, origAlphaColors);
0272 
0273         % If some of the transparencies were not found in the EPS file, then rerun the
0274         % export with only the found transparencies modified (backward compatibility)
0275         if ~isempty(fstrm) && ~all(foundFlags)
0276             foundIdx = find(foundFlags);
0277             for objIdx = 1 : sum(foundFlags)
0278                 colorsIdx = foundIdx(objIdx);
0279                 colorsData = origAlphaColors{colorsIdx};
0280                 hObj     = colorsData{1};
0281                 propName = colorsData{2};
0282                 newColor = colorsData{4};
0283                 hObj.(propName).ColorData = newColor;
0284             end
0285             delete(name);
0286             print(fig, options{:}, name);
0287             fstrm = read_write_entire_textfile(name);
0288             [~,fstrm] = eps_maintainAlpha(fig, fstrm, origAlphaColors(foundFlags));
0289         end
0290     end
0291 
0292     % Fix for Matlab R2014b bug (issue #31): LineWidths<0.75 are not set in the EPS (default line width is used)
0293     try
0294         if ~isempty(fstrm) && using_hg2(fig)
0295             % Convert miter joins to line joins
0296             %fstrm = regexprep(fstrm, '\n10.0 ML\n', '\n1 LJ\n');
0297             % This is faster (the original regexprep could take many seconds when the axes contains many lines):
0298             fstrm = strrep(fstrm, sprintf('\n10.0 ML\n'), sprintf('\n1 LJ\n'));
0299 
0300             % In HG2, grid lines and axes Ruler Axles have a default LineWidth of 0.5 => replace en-bulk (assume that 1.0 LineWidth = 1.333 LW)
0301             %   hAxes=gca; hAxes.YGridHandle.LineWidth, hAxes.YRuler.Axle.LineWidth
0302             %fstrm = regexprep(fstrm, '(GC\n2 setlinecap\n1 LJ)\nN', '$1\n0.667 LW\nN');
0303             % This is faster:
0304             fstrm = strrep(fstrm, sprintf('GC\n2 setlinecap\n1 LJ\nN'), sprintf('GC\n2 setlinecap\n1 LJ\n0.667 LW\nN'));
0305 
0306             % This is more accurate but *MUCH* slower (issue #52)
0307             %{
0308             % Modify all thin lines in the figure to have 10x LineWidths
0309             hLines = findall(fig,'Type','line');
0310             hThinLines = [];
0311             for lineIdx = 1 : numel(hLines)
0312                 thisLine = hLines(lineIdx);
0313                 if thisLine.LineWidth < 0.75 && strcmpi(thisLine.Visible,'on')
0314                     hThinLines(end+1) = thisLine; %#ok<AGROW>
0315                     thisLine.LineWidth = thisLine.LineWidth * 10;
0316                 end
0317             end
0318 
0319             % If any thin lines were found
0320             if ~isempty(hThinLines)
0321                 % Prepare an EPS with large-enough line widths
0322                 print(fig, options{:}, name);
0323                 % Restore the original LineWidths in the figure
0324                 for lineIdx = 1 : numel(hThinLines)
0325                     thisLine = handle(hThinLines(lineIdx));
0326                     thisLine.LineWidth = thisLine.LineWidth / 10;
0327                 end
0328 
0329                 % Compare the original and the new EPS files and correct the original stream's LineWidths
0330                 fstrm_new = read_write_entire_textfile(name);
0331                 idx = 500;  % skip heading with its possibly-different timestamp
0332                 markerStr = sprintf('10.0 ML\nN');
0333                 markerLen = length(markerStr);
0334                 while ~isempty(idx) && idx < length(fstrm)
0335                     lastIdx = min(length(fstrm), length(fstrm_new));
0336                     delta = fstrm(idx+1:lastIdx) - fstrm_new(idx+1:lastIdx);
0337                     idx = idx + find(delta,1);
0338                     if ~isempty(idx) && ...
0339                             isequal(fstrm(idx-markerLen+1:idx), markerStr) && ...
0340                             ~isempty(regexp(fstrm_new(idx-markerLen+1:idx+12),'10.0 ML\n[\d\.]+ LW\nN')) %#ok<RGXP1>
0341                         value = str2double(regexprep(fstrm_new(idx:idx+12),' .*',''));
0342                         if isnan(value), break; end  % something's wrong... - bail out
0343                         newStr = sprintf('%0.3f LW\n',value/10);
0344                         fstrm = [fstrm(1:idx-1) newStr fstrm(idx:end)];
0345                         idx = idx + 12;
0346                     else
0347                         break;
0348                     end
0349                 end
0350             end
0351             %}
0352 
0353             % This is much faster although less accurate: fix all non-gray lines to have a LineWidth of 0.75 (=1 LW)
0354             % Note: This will give incorrect LineWidth of 075 for lines having LineWidth<0.75, as well as for non-gray grid-lines (if present)
0355             %       However, in practice these edge-cases are very rare indeed, and the difference in LineWidth should not be noticeable
0356             %fstrm = regexprep(fstrm, '([CR]C\n2 setlinecap\n1 LJ)\nN', '$1\n1 LW\nN');
0357             % This is faster (the original regexprep could take many seconds when the axes contains many lines):
0358             fstrm = strrep(fstrm, sprintf('\n2 setlinecap\n1 LJ\nN'), sprintf('\n2 setlinecap\n1 LJ\n1 LW\nN'));
0359         end
0360     catch err
0361         fprintf(2, 'Error fixing LineWidths in EPS file: %s\n at %s:%d\n', err.message, err.stack(1).file, err.stack(1).line);
0362     end
0363 
0364     % Reset the font and line colors
0365     try
0366         set(black_text_handles, 'Color', [0 0 0]);
0367         set(white_text_handles, 'Color', [1 1 1]);
0368     catch
0369         % Fix issue #159: redo findall() '*text_handles'
0370         black_text_handles = findall(fig, 'Type', 'text', 'Color', [0 0 0]+eps);
0371         white_text_handles = findall(fig, 'Type', 'text', 'Color', [1 1 1]-eps);
0372         set(black_text_handles, 'Color', [0 0 0]);
0373         set(white_text_handles, 'Color', [1 1 1]);
0374     end
0375     set(white_line_handles, 'Color', [1 1 1]);
0376 
0377     % Reset paper size
0378     set(fig, 'PaperPositionMode', old_pos_mode, 'PaperOrientation', old_orientation);
0379 
0380     % Reset the font names in the figure
0381     if ~isempty(font_swap)
0382         for a = update
0383             set(font_handles(a), 'FontName', fonts{a}, 'FontSize', fonts_size(a));
0384         end
0385     end
0386 
0387     % Bail out if EPS post-processing is not possible
0388     if isempty(fstrm)
0389         warning('Loading EPS file failed, so unable to perform post-processing. This is usually because the figure contains a large number of patch objects. Consider exporting to a bitmap format in this case.');
0390         return
0391     end
0392 
0393     % Replace the font names
0394     if ~isempty(font_swap)
0395         for a = 1:size(font_swap, 2)
0396             %fstrm = regexprep(fstrm, [font_swap{1,a} '-?[a-zA-Z]*\>'], font_swap{3,a}(~isspace(font_swap{3,a})));
0397             fstrm = regexprep(fstrm, font_swap{2,a}, font_swap{3,a}(~isspace(font_swap{3,a})));
0398         end
0399     end
0400 
0401     % Move the bounding box to the top of the file (HG2 only), or fix the line styles (HG1 only)
0402     if using_hg2(fig)
0403         % Move the bounding box to the top of the file (HG2 only)
0404         [s, e] = regexp(fstrm, '%%BoundingBox: [^%]*%%');
0405         if numel(s) == 2
0406             fstrm = fstrm([1:s(1)-1 s(2):e(2)-2 e(1)-1:s(2)-1 e(2)-1:end]);
0407         end
0408     else
0409         % Fix the line styles (HG1 only)
0410         fstrm = fix_lines(fstrm);
0411     end
0412 
0413     % Apply the bounding box padding & cropping, replacing Matlab's print()'s bounding box
0414     if bb_crop
0415         % Calculate a new bounding box based on a bitmap print using crop_border.m
0416         % 1. Determine the Matlab BoundingBox and PageBoundingBox
0417         [s,e] = regexp(fstrm, '%%BoundingBox: [^%]*%%'); % location BB in eps file
0418         if numel(s)==2, s=s(2); e=e(2); end
0419         aa = fstrm(s+15:e-3); % dimensions bb - STEP1
0420         bb_matlab = cell2mat(textscan(aa,'%f32%f32%f32%f32'));  % dimensions bb - STEP2
0421 
0422         [s,e] = regexp(fstrm, '%%PageBoundingBox: [^%]*%%'); % location bb in eps file
0423         if numel(s)==2, s=s(2); e=e(2); end
0424         aa = fstrm(s+19:e-3); % dimensions bb - STEP1
0425         pagebb_matlab = cell2mat(textscan(aa,'%f32%f32%f32%f32'));  % dimensions bb - STEP2
0426 
0427         % 2. Create a bitmap image and use crop_borders to create the relative
0428         %    bb with respect to the PageBoundingBox
0429         [A, bcol] = print2array(fig, 1, renderer);
0430         [aa, aa, aa, bb_rel] = crop_borders(A, bcol, bb_padding, crop_amounts);
0431 
0432         % 3. Calculate the new Bounding Box
0433         pagew = pagebb_matlab(3)-pagebb_matlab(1);
0434         pageh = pagebb_matlab(4)-pagebb_matlab(2);
0435         %bb_new = [pagebb_matlab(1)+pagew*bb_rel(1) pagebb_matlab(2)+pageh*bb_rel(2) ...
0436         %          pagebb_matlab(1)+pagew*bb_rel(3) pagebb_matlab(2)+pageh*bb_rel(4)];
0437         bb_new = pagebb_matlab([1,2,1,2]) + [pagew,pageh,pagew,pageh].*bb_rel;  % clearer
0438         bb_offset = (bb_new-bb_matlab) + [-1,-1,1,1];  % 1px margin so that cropping is not TOO tight
0439 
0440         % Apply the bounding box padding
0441         if bb_padding
0442             if abs(bb_padding)<1
0443                 bb_padding = round((mean([bb_new(3)-bb_new(1) bb_new(4)-bb_new(2)])*bb_padding)/0.5)*0.5; % ADJUST BB_PADDING
0444             end
0445             add_padding = @(n1, n2, n3, n4) sprintf(' %d', str2double({n1, n2, n3, n4}) + [-bb_padding -bb_padding bb_padding bb_padding] + bb_offset);
0446         else
0447             add_padding = @(n1, n2, n3, n4) sprintf(' %d', str2double({n1, n2, n3, n4}) + bb_offset); % fix small but noticeable bounding box shift
0448         end
0449         fstrm = regexprep(fstrm, '%%BoundingBox:[ ]+([-]?\d+)[ ]+([-]?\d+)[ ]+([-]?\d+)[ ]+([-]?\d+)', '%%BoundingBox:${add_padding($1, $2, $3, $4)}');
0450     end
0451 
0452     % Fix issue #44: white artifact lines appearing in patch exports
0453     % Note: the problem is due to the fact that Matlab's print() function exports patches
0454     %       as a combination of filled triangles, and a white line appears where the triangles touch
0455     % In the workaround below, we will modify such dual-triangles into a filled rectangle.
0456     % We are careful to only modify regexps that exactly match specific patterns - it's better to not
0457     % correct some white-line artifacts than to change the geometry of a patch, or to corrupt the EPS.
0458     %   e.g.: '0 -450 937 0 0 450 3 MP PP 937 0 0 -450 0 450 3 MP PP' => '0 -450 937 0 0 450 0 0 4 MP'
0459     fstrm = regexprep(fstrm, '\n([-\d.]+ [-\d.]+) ([-\d.]+ [-\d.]+) ([-\d.]+ [-\d.]+) 3 MP\nPP\n\2 \1 \3 3 MP\nPP\n','\n$1 $2 $3 0 0 4 MP\nPP\n');
0460     fstrm = regexprep(fstrm, '\n([-\d.]+ [-\d.]+) ([-\d.]+ [-\d.]+) ([-\d.]+ [-\d.]+) 3 MP\nPP\n\2 \3 \1 3 MP\nPP\n','\n$1 $2 $3 0 0 4 MP\nPP\n');
0461     fstrm = regexprep(fstrm, '\n([-\d.]+ [-\d.]+) ([-\d.]+ [-\d.]+) ([-\d.]+ [-\d.]+) 3 MP\nPP\n\3 \1 \2 3 MP\nPP\n','\n$1 $2 $3 0 0 4 MP\nPP\n');
0462     fstrm = regexprep(fstrm, '\n([-\d.]+ [-\d.]+) ([-\d.]+ [-\d.]+) ([-\d.]+ [-\d.]+) 3 MP\nPP\n\3 \2 \1 3 MP\nPP\n','\n$1 $2 $3 0 0 4 MP\nPP\n');
0463 
0464     % Write out the fixed eps file
0465     read_write_entire_textfile(name, fstrm);
0466 end
0467 
0468 function [StoredColors, fstrm, foundFlags] = eps_maintainAlpha(fig, fstrm, StoredColors)
0469     if nargin == 1  % in: convert transparency in Matlab figure into unique RGB colors
0470         hObjs = findall(fig); %findobj(fig,'Type','Area');
0471         StoredColors = {};
0472         propNames = {'Face','Edge'};
0473         for objIdx = 1:length(hObjs)
0474             hObj = hObjs(objIdx);
0475             for propIdx = 1 : numel(propNames)
0476                 try
0477                     propName = propNames{propIdx};
0478                     if strcmp(hObj.(propName).ColorType, 'truecoloralpha')
0479                         nColors = length(StoredColors);
0480                         oldColor = hObj.(propName).ColorData;
0481                         newColor = uint8([101; 102+floor(nColors/255); mod(nColors,255); 255]);
0482                         StoredColors{end+1} = {hObj, propName, oldColor, newColor};
0483                         hObj.(propName).ColorData = newColor;
0484                     end
0485                 catch
0486                     % Never mind - ignore (either doesn't have the property or cannot change it)
0487                 end
0488             end
0489         end
0490     else  % restore transparency in Matlab figure by converting back from the unique RGBs
0491         %Find the transparent patches
0492         wasError = false;
0493         nColors = length(StoredColors);
0494         foundFlags = false(1,nColors);
0495         for objIdx = 1 : nColors
0496             colorsData = StoredColors{objIdx};
0497             hObj      = colorsData{1};
0498             propName  = colorsData{2};
0499             origColor = colorsData{3};
0500             newColor  = colorsData{4};
0501             try
0502                 %Restore the EPS files patch color
0503                 colorID   = num2str(round(double(newColor(1:3)') /255,3),'%.3g %.3g %.3g'); %ID for searching
0504                 origRGB   = num2str(round(double(origColor(1:3)')/255,3),'%.3g %.3g %.3g'); %Replace with original color
0505                 origAlpha = num2str(round(double(origColor(end)) /255,3),'%.3g'); %Convert alpha value for EPS
0506 
0507                 %Find and replace the RGBA values within the EPS text fstrm
0508                 if strcmpi(propName,'Face')
0509                     oldStr = sprintf(['\n' colorID ' RC\nN\n']);
0510                     newStr = sprintf(['\n' origRGB ' RC\n' origAlpha ' .setopacityalpha true\nN\n']);
0511                 else  %'Edge'
0512                     oldStr = sprintf(['\n' colorID ' RC\n1 LJ\n']);
0513                     newStr = sprintf(['\n' origRGB ' RC\n' origAlpha ' .setopacityalpha true\n']);
0514                 end
0515                 foundFlags(objIdx) = ~isempty(strfind(fstrm, oldStr));
0516                 fstrm = strrep(fstrm, oldStr, newStr);
0517 
0518                 %Restore the figure object's original color
0519                 hObj.(propName).ColorData = origColor;
0520             catch err
0521                 % something is wrong - cannot restore transparent color...
0522                 if ~wasError
0523                     fprintf(2, 'Error maintaining transparency in EPS file: %s\n at %s:%d\n', err.message, err.stack(1).file, err.stack(1).line);
0524                     wasError = true;
0525                 end
0526             end
0527         end
0528     end
0529 end

Generated on Tue 23-May-2017 03:00:58 by m2html © 2005