|
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 |