Switch to unified view

a b/util/export_fig/print2eps.m
1
function print2eps(name, fig, export_options, varargin)
2
%PRINT2EPS  Prints figures to eps with improved line styles
3
%
4
% Examples:
5
%   print2eps filename
6
%   print2eps(filename, fig_handle)
7
%   print2eps(filename, fig_handle, export_options)
8
%   print2eps(filename, fig_handle, export_options, print_options)
9
%
10
% This function saves a figure as an eps file, with two improvements over
11
% MATLAB's print command. First, it improves the line style, making dashed
12
% lines more like those on screen and giving grid lines a dotted line style.
13
% Secondly, it substitutes original font names back into the eps file,
14
% where these have been changed by MATLAB, for up to 11 different fonts.
15
%
16
%IN:
17
%   filename - string containing the name (optionally including full or
18
%              relative path) of the file the figure is to be saved as. A
19
%              ".eps" extension is added if not there already. If a path is
20
%              not specified, the figure is saved in the current directory.
21
%   fig_handle - The handle of the figure to be saved. Default: gcf().
22
%   export_options - array or struct of optional scalar values:
23
%       bb_padding - Scalar value of amount of padding to add to border around
24
%                    the cropped image, in points (if >1) or percent (if <1).
25
%                    Can be negative as well as positive; Default: 0
26
%       crop       - Cropping flag. Deafult: 0
27
%       fontswap   - Whether to swap non-default fonts in figure. Default: true
28
%       renderer   - Renderer used to generate bounding-box. Default: 'opengl'
29
%                    (available only via the struct alternative)
30
%       crop_amounts - 4-element vector of crop amounts: [top,right,bottom,left]
31
%                    (available only via the struct alternative)
32
%   print_options - Additional parameter strings to be passed to the print command
33
34
%{
35
% Copyright (C) Oliver Woodford 2008-2014, Yair Altman 2015-
36
37
% The idea of editing the EPS file to change line styles comes from Jiro
38
% Doke's FIXPSLINESTYLE (fex id: 17928)
39
% The idea of changing dash length with line width came from comments on
40
% fex id: 5743, but the implementation is mine :)
41
%}
42
%{
43
% 14/11/11: Fix a MATLAB bug rendering black or white text incorrectly.
44
%           Thanks to Mathieu Morlighem for reporting the issue and
45
%           obtaining a fix from TMW.
46
% 08/12/11: Added ability to correct fonts. Several people have requested
47
%           this at one time or another, and also pointed me to printeps
48
%           (fex id: 7501), so thank you to them. My implementation (which
49
%           was not inspired by printeps - I'd already had the idea for my
50
%           approach) goes slightly further in that it allows multiple
51
%           fonts to be swapped.
52
% 14/12/11: Fix bug affecting font names containing spaces. Thanks to David
53
%           Szwer for reporting the issue.
54
% 25/01/12: Add a font not to be swapped. Thanks to Anna Rafferty and Adam
55
%           Jackson for reporting the issue. Also fix a bug whereby using a
56
%           font alias can lead to another font being swapped in.
57
% 10/04/12: Make the font swapping case insensitive.
58
% 26/10/12: Set PaperOrientation to portrait. Thanks to Michael Watts for
59
%           reporting the issue.
60
% 26/10/12: Fix issue to do with swapping fonts changing other fonts and
61
%           sizes we don't want, due to listeners. Thanks to Malcolm Hudson
62
%           for reporting the issue.
63
% 22/03/13: Extend font swapping to axes labels. Thanks to Rasmus Ischebeck
64
%           for reporting the issue.
65
% 23/07/13: Bug fix to font swapping. Thanks to George for reporting the
66
%           issue.
67
% 13/08/13: Fix MATLAB feature of not exporting white lines correctly.
68
%           Thanks to Sebastian Hesslinger for reporting it.
69
% 24/02/15: Fix for Matlab R2014b bug (issue #31): LineWidths<0.75 are not
70
%           set in the EPS (default line width is used)
71
% 25/02/15: Fixed issue #32: BoundingBox problem caused uncropped EPS/PDF files
72
% 05/03/15: Fixed issue #43: Inability to perform EPS file post-processing
73
% 06/03/15: Improved image padding & cropping thanks to Oscar Hartogensis
74
% 21/03/15: Fixed edge-case of missing handles having a 'FontName' property
75
% 26/03/15: Attempt to fix issue #45: white lines in subplots do not print correctly
76
% 27/03/15: Attempt to fix issue #44: white artifact lines appearing in patch exports
77
% 30/03/15: Fixed issue #52: improved performance on HG2 (R2014b+)
78
% 09/04/15: Comment blocks consolidation and minor code cleanup (no real code change)
79
% 12/04/15: Fixed issue #56: bad cropping
80
% 14/04/15: Workaround for issue #45: lines in image subplots are exported in invalid color
81
% 07/07/15: Added option to avoid font-swapping in EPS/PDF
82
% 07/07/15: Fixed issue #83: use numeric handles in HG1
83
% 22/07/15: Fixed issue #91 (thanks to Carlos Moffat)
84
% 28/09/15: Fixed issue #108 (thanks to JacobD10)
85
% 01/11/15: Fixed issue #112: optional renderer for bounding-box computation (thanks to Jesús Pestana Puerta)
86
% 21/02/16: Enabled specifying non-automated crop amounts
87
% 22/02/16: Better support + backward compatibility for transparency (issue #108)
88
% 10/06/16: Fixed issue #159: text handles get cleared by Matlab in the print() command
89
% 12/06/16: Improved the fix for issue #159 (in the previous commit)
90
% 12/06/16: Fixed issue #158: transparent patch color in PDF/EPS
91
%}
92
93
    options = {'-loose'};
94
    if nargin > 3
95
        options = [options varargin];
96
    elseif nargin < 3
97
        export_options = 0;
98
        if nargin < 2
99
            fig = gcf();
100
        end
101
    end
102
103
    % Retrieve padding, crop & font-swap values
104
    crop_amounts = nan(1,4);  % auto-crop all 4 sides by default
105
    if isstruct(export_options)
106
        try fontswap     = export_options.fontswap;     catch, fontswap = true;     end
107
        try bb_crop      = export_options.crop;         catch, bb_crop = 0;         end
108
        try crop_amounts = export_options.crop_amounts; catch,                      end
109
        try bb_padding   = export_options.bb_padding;   catch, bb_padding = 0;      end
110
        try renderer     = export_options.rendererStr;  catch, renderer = 'opengl'; end  % fix for issue #110
111
        if renderer(1)~='-',  renderer = ['-' renderer];  end
112
    else
113
        if numel(export_options) > 2  % font-swapping
114
            fontswap = export_options(3);
115
        else
116
            fontswap = true;
117
        end
118
        if numel(export_options) > 1  % cropping
119
            bb_crop = export_options(2);
120
        else
121
            bb_crop = 0;  % scalar value, so use default bb_crop value of 0
122
        end
123
        if numel(export_options) > 0  % padding
124
            bb_padding = export_options(1);
125
        else
126
            bb_padding = 0;
127
        end
128
        renderer = '-opengl';
129
    end
130
131
    % Construct the filename
132
    if numel(name) < 5 || ~strcmpi(name(end-3:end), '.eps')
133
        name = [name '.eps']; % Add the missing extension
134
    end
135
136
    % Set paper size
137
    old_pos_mode = get(fig, 'PaperPositionMode');
138
    old_orientation = get(fig, 'PaperOrientation');
139
    set(fig, 'PaperPositionMode', 'auto', 'PaperOrientation', 'portrait');
140
141
    % Find all the used fonts in the figure
142
    font_handles = findall(fig, '-property', 'FontName');
143
    fonts = get(font_handles, 'FontName');
144
    if isempty(fonts)
145
        fonts = {};
146
    elseif ~iscell(fonts)
147
        fonts = {fonts};
148
    end
149
150
    % Map supported font aliases onto the correct name
151
    fontsl = lower(fonts);
152
    for a = 1:numel(fonts)
153
        f = fontsl{a};
154
        f(f==' ') = [];
155
        switch f
156
            case {'times', 'timesnewroman', 'times-roman'}
157
                fontsl{a} = 'times-roman';
158
            case {'arial', 'helvetica'}
159
                fontsl{a} = 'helvetica';
160
            case {'newcenturyschoolbook', 'newcenturyschlbk'}
161
                fontsl{a} = 'newcenturyschlbk';
162
            otherwise
163
        end
164
    end
165
    fontslu = unique(fontsl);
166
167
    % Determine the font swap table
168
    if fontswap
169
        matlab_fonts = {'Helvetica', 'Times-Roman', 'Palatino', 'Bookman', 'Helvetica-Narrow', 'Symbol', ...
170
                        'AvantGarde', 'NewCenturySchlbk', 'Courier', 'ZapfChancery', 'ZapfDingbats'};
171
        matlab_fontsl = lower(matlab_fonts);
172
        require_swap = find(~ismember(fontslu, matlab_fontsl));
173
        unused_fonts = find(~ismember(matlab_fontsl, fontslu));
174
        font_swap = cell(3, min(numel(require_swap), numel(unused_fonts)));
175
        fonts_new = fonts;
176
        for a = 1:size(font_swap, 2)
177
            font_swap{1,a} = find(strcmp(fontslu{require_swap(a)}, fontsl));
178
            font_swap{2,a} = matlab_fonts{unused_fonts(a)};
179
            font_swap{3,a} = fonts{font_swap{1,a}(1)};
180
            fonts_new(font_swap{1,a}) = font_swap(2,a);
181
        end
182
    else
183
        font_swap = [];
184
    end
185
186
    % Swap the fonts
187
    if ~isempty(font_swap)
188
        fonts_size = get(font_handles, 'FontSize');
189
        if iscell(fonts_size)
190
            fonts_size = cell2mat(fonts_size);
191
        end
192
        M = false(size(font_handles));
193
194
        % Loop because some changes may not stick first time, due to listeners
195
        c = 0;
196
        update = zeros(1000, 1);
197
        for b = 1:10 % Limit number of loops to avoid infinite loop case
198
            for a = 1:numel(M)
199
                M(a) = ~isequal(get(font_handles(a), 'FontName'), fonts_new{a}) || ~isequal(get(font_handles(a), 'FontSize'), fonts_size(a));
200
                if M(a)
201
                    set(font_handles(a), 'FontName', fonts_new{a}, 'FontSize', fonts_size(a));
202
                    c = c + 1;
203
                    update(c) = a;
204
                end
205
            end
206
            if ~any(M)
207
                break;
208
            end
209
        end
210
211
        % Compute the order to revert fonts later, without the need of a loop
212
        [update, M] = unique(update(1:c));
213
        [M, M] = sort(M);
214
        update = reshape(update(M), 1, []);
215
    end
216
217
    % MATLAB bug fix - black and white text can come out inverted sometimes
218
    % Find the white and black text
219
    black_text_handles = findall(fig, 'Type', 'text', 'Color', [0 0 0]);
220
    white_text_handles = findall(fig, 'Type', 'text', 'Color', [1 1 1]);
221
    % Set the font colors slightly off their correct values
222
    set(black_text_handles, 'Color', [0 0 0] + eps);
223
    set(white_text_handles, 'Color', [1 1 1] - eps);
224
225
    % MATLAB bug fix - white lines can come out funny sometimes
226
    % Find the white lines
227
    white_line_handles = findall(fig, 'Type', 'line', 'Color', [1 1 1]);
228
    % Set the line color slightly off white
229
    set(white_line_handles, 'Color', [1 1 1] - 0.00001);
230
231
    % Workaround for issue #45: lines in image subplots are exported in invalid color
232
    % In this case the -depsc driver solves the problem, but then all the other workarounds
233
    % below (for all the other issues) will fail, so it's better to let the user decide by
234
    % just issuing a warning and accepting the '-depsc' input parameter
235
    epsLevel2 = ~any(strcmpi(options,'-depsc'));
236
    if epsLevel2
237
        % Use -depsc2 (EPS color level-2) if -depsc (EPS color level-3) was not specifically requested
238
        options{end+1} = '-depsc2';
239
        % Issue a warning if multiple images & lines were found in the figure, and HG1 with painters renderer is used
240
        isPainters = any(strcmpi(options,'-painters'));
241
        if isPainters && ~using_hg2 && numel(findall(fig,'Type','image'))>1 && ~isempty(findall(fig,'Type','line'))
242
            warning('YMA:export_fig:issue45', ...
243
                    ['Multiple images & lines detected. In such cases, the lines might \n' ...
244
                     'appear with an invalid color due to an internal MATLAB bug (fixed in R2014b). \n' ...
245
                     'Possible workaround: add a ''-depsc'' or ''-opengl'' parameter to the export_fig command.']);
246
        end
247
    end
248
249
    % Fix issue #83: use numeric handles in HG1
250
    if ~using_hg2(fig),  fig = double(fig);  end
251
252
    % Workaround for when transparency is lost through conversion fig>EPS>PDF (issue #108)
253
    % Replace transparent patch RGB values with an ID value (rare chance that ID color is being used already)
254
    if using_hg2
255
        origAlphaColors = eps_maintainAlpha(fig);
256
    end
257
258
    % Print to eps file
259
    print(fig, options{:}, name);
260
261
    % Do post-processing on the eps file
262
    try
263
        % Read the EPS file into memory
264
        fstrm = read_write_entire_textfile(name);
265
    catch
266
        fstrm = '';
267
    end
268
269
    % Restore colors for transparent patches/lines and apply the
270
    % setopacityalpha setting in the EPS file (issue #108)
271
    if using_hg2
272
        [~,fstrm,foundFlags] = eps_maintainAlpha(fig, fstrm, origAlphaColors);
273
274
        % If some of the transparencies were not found in the EPS file, then rerun the
275
        % export with only the found transparencies modified (backward compatibility)
276
        if ~isempty(fstrm) && ~all(foundFlags)
277
            foundIdx = find(foundFlags);
278
            for objIdx = 1 : sum(foundFlags)
279
                colorsIdx = foundIdx(objIdx);
280
                colorsData = origAlphaColors{colorsIdx};
281
                hObj     = colorsData{1};
282
                propName = colorsData{2};
283
                newColor = colorsData{4};
284
                hObj.(propName).ColorData = newColor;
285
            end
286
            delete(name);
287
            print(fig, options{:}, name);
288
            fstrm = read_write_entire_textfile(name);
289
            [~,fstrm] = eps_maintainAlpha(fig, fstrm, origAlphaColors(foundFlags));
290
        end
291
    end
292
293
    % Fix for Matlab R2014b bug (issue #31): LineWidths<0.75 are not set in the EPS (default line width is used)
294
    try
295
        if ~isempty(fstrm) && using_hg2(fig)
296
            % Convert miter joins to line joins
297
            %fstrm = regexprep(fstrm, '\n10.0 ML\n', '\n1 LJ\n');
298
            % This is faster (the original regexprep could take many seconds when the axes contains many lines):
299
            fstrm = strrep(fstrm, sprintf('\n10.0 ML\n'), sprintf('\n1 LJ\n'));
300
301
            % 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)
302
            %   hAxes=gca; hAxes.YGridHandle.LineWidth, hAxes.YRuler.Axle.LineWidth
303
            %fstrm = regexprep(fstrm, '(GC\n2 setlinecap\n1 LJ)\nN', '$1\n0.667 LW\nN');
304
            % This is faster:
305
            fstrm = strrep(fstrm, sprintf('GC\n2 setlinecap\n1 LJ\nN'), sprintf('GC\n2 setlinecap\n1 LJ\n0.667 LW\nN'));
306
307
            % This is more accurate but *MUCH* slower (issue #52)
308
            %{
309
            % Modify all thin lines in the figure to have 10x LineWidths
310
            hLines = findall(fig,'Type','line');
311
            hThinLines = [];
312
            for lineIdx = 1 : numel(hLines)
313
                thisLine = hLines(lineIdx);
314
                if thisLine.LineWidth < 0.75 && strcmpi(thisLine.Visible,'on')
315
                    hThinLines(end+1) = thisLine; %#ok<AGROW>
316
                    thisLine.LineWidth = thisLine.LineWidth * 10;
317
                end
318
            end
319
320
            % If any thin lines were found
321
            if ~isempty(hThinLines)
322
                % Prepare an EPS with large-enough line widths
323
                print(fig, options{:}, name);
324
                % Restore the original LineWidths in the figure
325
                for lineIdx = 1 : numel(hThinLines)
326
                    thisLine = handle(hThinLines(lineIdx));
327
                    thisLine.LineWidth = thisLine.LineWidth / 10;
328
                end
329
330
                % Compare the original and the new EPS files and correct the original stream's LineWidths
331
                fstrm_new = read_write_entire_textfile(name);
332
                idx = 500;  % skip heading with its possibly-different timestamp
333
                markerStr = sprintf('10.0 ML\nN');
334
                markerLen = length(markerStr);
335
                while ~isempty(idx) && idx < length(fstrm)
336
                    lastIdx = min(length(fstrm), length(fstrm_new));
337
                    delta = fstrm(idx+1:lastIdx) - fstrm_new(idx+1:lastIdx);
338
                    idx = idx + find(delta,1);
339
                    if ~isempty(idx) && ...
340
                            isequal(fstrm(idx-markerLen+1:idx), markerStr) && ...
341
                            ~isempty(regexp(fstrm_new(idx-markerLen+1:idx+12),'10.0 ML\n[\d\.]+ LW\nN')) %#ok<RGXP1>
342
                        value = str2double(regexprep(fstrm_new(idx:idx+12),' .*',''));
343
                        if isnan(value), break; end  % something's wrong... - bail out
344
                        newStr = sprintf('%0.3f LW\n',value/10);
345
                        fstrm = [fstrm(1:idx-1) newStr fstrm(idx:end)];
346
                        idx = idx + 12;
347
                    else
348
                        break;
349
                    end
350
                end
351
            end
352
            %}
353
354
            % This is much faster although less accurate: fix all non-gray lines to have a LineWidth of 0.75 (=1 LW)
355
            % Note: This will give incorrect LineWidth of 075 for lines having LineWidth<0.75, as well as for non-gray grid-lines (if present)
356
            %       However, in practice these edge-cases are very rare indeed, and the difference in LineWidth should not be noticeable
357
            %fstrm = regexprep(fstrm, '([CR]C\n2 setlinecap\n1 LJ)\nN', '$1\n1 LW\nN');
358
            % This is faster (the original regexprep could take many seconds when the axes contains many lines):
359
            fstrm = strrep(fstrm, sprintf('\n2 setlinecap\n1 LJ\nN'), sprintf('\n2 setlinecap\n1 LJ\n1 LW\nN'));
360
        end
361
    catch err
362
        fprintf(2, 'Error fixing LineWidths in EPS file: %s\n at %s:%d\n', err.message, err.stack(1).file, err.stack(1).line);
363
    end
364
365
    % Reset the font and line colors
366
    try
367
        set(black_text_handles, 'Color', [0 0 0]);
368
        set(white_text_handles, 'Color', [1 1 1]);
369
    catch
370
        % Fix issue #159: redo findall() '*text_handles'
371
        black_text_handles = findall(fig, 'Type', 'text', 'Color', [0 0 0]+eps);
372
        white_text_handles = findall(fig, 'Type', 'text', 'Color', [1 1 1]-eps);
373
        set(black_text_handles, 'Color', [0 0 0]);
374
        set(white_text_handles, 'Color', [1 1 1]);
375
    end
376
    set(white_line_handles, 'Color', [1 1 1]);
377
378
    % Reset paper size
379
    set(fig, 'PaperPositionMode', old_pos_mode, 'PaperOrientation', old_orientation);
380
381
    % Reset the font names in the figure
382
    if ~isempty(font_swap)
383
        for a = update
384
            set(font_handles(a), 'FontName', fonts{a}, 'FontSize', fonts_size(a));
385
        end
386
    end
387
388
    % Bail out if EPS post-processing is not possible
389
    if isempty(fstrm)
390
        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.');
391
        return
392
    end
393
394
    % Replace the font names
395
    if ~isempty(font_swap)
396
        for a = 1:size(font_swap, 2)
397
            %fstrm = regexprep(fstrm, [font_swap{1,a} '-?[a-zA-Z]*\>'], font_swap{3,a}(~isspace(font_swap{3,a})));
398
            fstrm = regexprep(fstrm, font_swap{2,a}, font_swap{3,a}(~isspace(font_swap{3,a})));
399
        end
400
    end
401
402
    % Move the bounding box to the top of the file (HG2 only), or fix the line styles (HG1 only)
403
    if using_hg2(fig)
404
        % Move the bounding box to the top of the file (HG2 only)
405
        [s, e] = regexp(fstrm, '%%BoundingBox: [^%]*%%');
406
        if numel(s) == 2
407
            fstrm = fstrm([1:s(1)-1 s(2):e(2)-2 e(1)-1:s(2)-1 e(2)-1:end]);
408
        end
409
    else
410
        % Fix the line styles (HG1 only)
411
        fstrm = fix_lines(fstrm);
412
    end
413
414
    % Apply the bounding box padding & cropping, replacing Matlab's print()'s bounding box
415
    if bb_crop
416
        % Calculate a new bounding box based on a bitmap print using crop_border.m
417
        % 1. Determine the Matlab BoundingBox and PageBoundingBox
418
        [s,e] = regexp(fstrm, '%%BoundingBox: [^%]*%%'); % location BB in eps file
419
        if numel(s)==2, s=s(2); e=e(2); end
420
        aa = fstrm(s+15:e-3); % dimensions bb - STEP1
421
        bb_matlab = cell2mat(textscan(aa,'%f32%f32%f32%f32'));  % dimensions bb - STEP2
422
423
        [s,e] = regexp(fstrm, '%%PageBoundingBox: [^%]*%%'); % location bb in eps file
424
        if numel(s)==2, s=s(2); e=e(2); end
425
        aa = fstrm(s+19:e-3); % dimensions bb - STEP1
426
        pagebb_matlab = cell2mat(textscan(aa,'%f32%f32%f32%f32'));  % dimensions bb - STEP2
427
428
        % 2. Create a bitmap image and use crop_borders to create the relative
429
        %    bb with respect to the PageBoundingBox
430
        [A, bcol] = print2array(fig, 1, renderer);
431
        [aa, aa, aa, bb_rel] = crop_borders(A, bcol, bb_padding, crop_amounts);
432
433
        % 3. Calculate the new Bounding Box
434
        pagew = pagebb_matlab(3)-pagebb_matlab(1);
435
        pageh = pagebb_matlab(4)-pagebb_matlab(2);
436
        %bb_new = [pagebb_matlab(1)+pagew*bb_rel(1) pagebb_matlab(2)+pageh*bb_rel(2) ...
437
        %          pagebb_matlab(1)+pagew*bb_rel(3) pagebb_matlab(2)+pageh*bb_rel(4)];
438
        bb_new = pagebb_matlab([1,2,1,2]) + [pagew,pageh,pagew,pageh].*bb_rel;  % clearer
439
        bb_offset = (bb_new-bb_matlab) + [-1,-1,1,1];  % 1px margin so that cropping is not TOO tight
440
441
        % Apply the bounding box padding
442
        if bb_padding
443
            if abs(bb_padding)<1
444
                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
445
            end
446
            add_padding = @(n1, n2, n3, n4) sprintf(' %d', str2double({n1, n2, n3, n4}) + [-bb_padding -bb_padding bb_padding bb_padding] + bb_offset);
447
        else
448
            add_padding = @(n1, n2, n3, n4) sprintf(' %d', str2double({n1, n2, n3, n4}) + bb_offset); % fix small but noticeable bounding box shift
449
        end
450
        fstrm = regexprep(fstrm, '%%BoundingBox:[ ]+([-]?\d+)[ ]+([-]?\d+)[ ]+([-]?\d+)[ ]+([-]?\d+)', '%%BoundingBox:${add_padding($1, $2, $3, $4)}');
451
    end
452
453
    % Fix issue #44: white artifact lines appearing in patch exports
454
    % Note: the problem is due to the fact that Matlab's print() function exports patches
455
    %       as a combination of filled triangles, and a white line appears where the triangles touch
456
    % In the workaround below, we will modify such dual-triangles into a filled rectangle.
457
    % We are careful to only modify regexps that exactly match specific patterns - it's better to not
458
    % correct some white-line artifacts than to change the geometry of a patch, or to corrupt the EPS.
459
    %   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'
460
    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');
461
    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');
462
    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');
463
    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');
464
465
    % Write out the fixed eps file
466
    read_write_entire_textfile(name, fstrm);
467
end
468
469
function [StoredColors, fstrm, foundFlags] = eps_maintainAlpha(fig, fstrm, StoredColors)
470
    if nargin == 1  % in: convert transparency in Matlab figure into unique RGB colors
471
        hObjs = findall(fig); %findobj(fig,'Type','Area');
472
        StoredColors = {};
473
        propNames = {'Face','Edge'};
474
        for objIdx = 1:length(hObjs)
475
            hObj = hObjs(objIdx);
476
            for propIdx = 1 : numel(propNames)
477
                try
478
                    propName = propNames{propIdx};
479
                    if strcmp(hObj.(propName).ColorType, 'truecoloralpha')
480
                        nColors = length(StoredColors);
481
                        oldColor = hObj.(propName).ColorData;
482
                        newColor = uint8([101; 102+floor(nColors/255); mod(nColors,255); 255]);
483
                        StoredColors{end+1} = {hObj, propName, oldColor, newColor};
484
                        hObj.(propName).ColorData = newColor;
485
                    end
486
                catch
487
                    % Never mind - ignore (either doesn't have the property or cannot change it)
488
                end
489
            end
490
        end
491
    else  % restore transparency in Matlab figure by converting back from the unique RGBs
492
        %Find the transparent patches
493
        wasError = false;
494
        nColors = length(StoredColors);
495
        foundFlags = false(1,nColors);
496
        for objIdx = 1 : nColors
497
            colorsData = StoredColors{objIdx};
498
            hObj      = colorsData{1};
499
            propName  = colorsData{2};
500
            origColor = colorsData{3};
501
            newColor  = colorsData{4};
502
            try
503
                %Restore the EPS files patch color
504
                colorID   = num2str(round(double(newColor(1:3)') /255,3),'%.3g %.3g %.3g'); %ID for searching
505
                origRGB   = num2str(round(double(origColor(1:3)')/255,3),'%.3g %.3g %.3g'); %Replace with original color
506
                origAlpha = num2str(round(double(origColor(end)) /255,3),'%.3g'); %Convert alpha value for EPS
507
508
                %Find and replace the RGBA values within the EPS text fstrm
509
                if strcmpi(propName,'Face')
510
                    oldStr = sprintf(['\n' colorID ' RC\nN\n']);
511
                    newStr = sprintf(['\n' origRGB ' RC\n' origAlpha ' .setopacityalpha true\nN\n']);
512
                else  %'Edge'
513
                    oldStr = sprintf(['\n' colorID ' RC\n1 LJ\n']);
514
                    newStr = sprintf(['\n' origRGB ' RC\n' origAlpha ' .setopacityalpha true\n']);
515
                end
516
                foundFlags(objIdx) = ~isempty(strfind(fstrm, oldStr));
517
                fstrm = strrep(fstrm, oldStr, newStr);
518
519
                %Restore the figure object's original color
520
                hObj.(propName).ColorData = origColor;
521
            catch err
522
                % something is wrong - cannot restore transparent color...
523
                if ~wasError
524
                    fprintf(2, 'Error maintaining transparency in EPS file: %s\n at %s:%d\n', err.message, err.stack(1).file, err.stack(1).line);
525
                    wasError = true;
526
                end
527
            end
528
        end
529
    end
530
end