Robochameleon  v1.0
print2eps.m
1 %PRINT2EPS Prints figures to eps with improved line styles
2 %
3 % Examples:
4 % print2eps filename
5 % print2eps(filename, fig_handle)
6 % print2eps(filename, fig_handle, options)
7 %
8 % This function saves a figure as an eps file, with two improvements over
9 % MATLAB's print command. First, it improves the line style, making dashed
10 % lines more like those on screen and giving grid lines their own dotted
11 % style. Secondly, it substitutes original font names back into the eps
12 % file, where these have been changed by MATLAB, for up to 11 different
13 % fonts.
14 %
15 %IN:
16 % filename - string containing the name (optionally including full or
17 % relative path) of the file the figure is to be saved as. A
18 % ".eps" extension is added if not there already. If a path is
19 % not specified, the figure is saved in the current directory.
20 % fig_handle - The handle of the figure to be saved. Default: gcf.
21 % options - Additional parameter strings to be passed to print.
22 
23 % Copyright (C) Oliver Woodford 2008-2014
24 
25 % The idea of editing the EPS file to change line styles comes from Jiro
26 % Doke's FIXPSLINESTYLE (fex id: 17928)
27 % The idea of changing dash length with line width came from comments on
28 % fex id: 5743, but the implementation is mine :)
29 
30 % 14/11/2011: Fix a MATLAB bug rendering black or white text incorrectly.
31 % Thanks to Mathieu Morlighem for reporting the issue and
32 % obtaining a fix from TMW.
33 % 08/12/11: Added ability to correct fonts. Several people have requested
34 % this at one time or another, and also pointed me to printeps
35 % (fex id: 7501), so thank you to them. My implementation (which
36 % was not inspired by printeps - I'd already had the idea for my
37 % approach) goes slightly further in that it allows multiple
38 % fonts to be swapped.
39 % 14/12/11: Fix bug affecting font names containing spaces. Thanks to David
40 % Szwer for reporting the issue.
41 % 25/01/12: Add a font not to be swapped. Thanks to Anna Rafferty and Adam
42 % Jackson for reporting the issue. Also fix a bug whereby using a
43 % font alias can lead to another font being swapped in.
44 % 10/04/12: Make the font swapping case insensitive.
45 % 26/10/12: Set PaperOrientation to portrait. Thanks to Michael Watts for
46 % reporting the issue.
47 % 26/10/12: Fix issue to do with swapping fonts changing other fonts and
48 % sizes we don't want, due to listeners. Thanks to Malcolm Hudson
49 % for reporting the issue.
50 % 22/03/13: Extend font swapping to axes labels. Thanks to Rasmus Ischebeck
51 % for reporting the issue.
52 % 23/07/13: Bug fix to font swapping. Thank to George for reporting the
53 % issue.
54 % 13/08/13: Fix MATLAB feature of not exporting white lines correctly.
55 % Thanks to Sebastian Heßlinger for reporting it.
56 
57 function print2eps(name, fig, varargin)
58 options = {'-depsc2'};
59 if nargin < 2
60  fig = gcf;
61 elseif nargin > 2
62  options = [options varargin];
63 end
64 % Construct the filename
65 if numel(name) < 5 || ~strcmpi(name(end-3:end), '.eps')
66  name = [name '.eps']; % Add the missing extension
67 end
68 % Set paper size
69 old_pos_mode = get(fig, 'PaperPositionMode');
70 old_orientation = get(fig, 'PaperOrientation');
71 set(fig, 'PaperPositionMode', 'auto', 'PaperOrientation', 'portrait');
72 % Find all the used fonts in the figure
73 font_handles = findall(fig, '-property', 'FontName');
74 fonts = get(font_handles, 'FontName');
75 if ~iscell(fonts)
76  fonts = {fonts};
77 end
78 % Map supported font aliases onto the correct name
79 fontsl = lower(fonts);
80 for a = 1:numel(fonts)
81  f = fontsl{a};
82  f(f==' ') = [];
83  switch f
84  case {'times', 'timesnewroman', 'times-roman'}
85  fontsl{a} = 'times-roman';
86  case {'arial', 'helvetica'}
87  fontsl{a} = 'helvetica';
88  case {'newcenturyschoolbook', 'newcenturyschlbk'}
89  fontsl{a} = 'newcenturyschlbk';
90  otherwise
91  end
92 end
93 fontslu = unique(fontsl);
94 % Determine the font swap table
95 matlab_fonts = {'Helvetica', 'Times-Roman', 'Palatino', 'Bookman', 'Helvetica-Narrow', 'Symbol', ...
96  'AvantGarde', 'NewCenturySchlbk', 'Courier', 'ZapfChancery', 'ZapfDingbats'};
97 matlab_fontsl = lower(matlab_fonts);
98 require_swap = find(~ismember(fontslu, matlab_fontsl));
99 unused_fonts = find(~ismember(matlab_fontsl, fontslu));
100 font_swap = cell(3, min(numel(require_swap), numel(unused_fonts)));
101 fonts_new = fonts;
102 for a = 1:size(font_swap, 2)
103  font_swap{1,a} = find(strcmp(fontslu{require_swap(a)}, fontsl));
104  font_swap{2,a} = matlab_fonts{unused_fonts(a)};
105  font_swap{3,a} = fonts{font_swap{1,a}(1)};
106  fonts_new(font_swap{1,a}) = {font_swap{2,a}};
107 end
108 % Swap the fonts
109 if ~isempty(font_swap)
110  fonts_size = get(font_handles, 'FontSize');
111  if iscell(fonts_size)
112  fonts_size = cell2mat(fonts_size);
113  end
114  M = false(size(font_handles));
115  % Loop because some changes may not stick first time, due to listeners
116  c = 0;
117  update = zeros(1000, 1);
118  for b = 1:10 % Limit number of loops to avoid infinite loop case
119  for a = 1:numel(M)
120  M(a) = ~isequal(get(font_handles(a), 'FontName'), fonts_new{a}) || ~isequal(get(font_handles(a), 'FontSize'), fonts_size(a));
121  if M(a)
122  set(font_handles(a), 'FontName', fonts_new{a}, 'FontSize', fonts_size(a));
123  c = c + 1;
124  update(c) = a;
125  end
126  end
127  if ~any(M)
128  break;
129  end
130  end
131  % Compute the order to revert fonts later, without the need of a loop
132  [update, M] = unique(update(1:c));
133  [M, M] = sort(M);
134  update = reshape(update(M), 1, []);
135 end
136 % MATLAB bug fix - black and white text can come out inverted sometimes
137 % Find the white and black text
138 white_text_handles = findobj(fig, 'Type', 'text');
139 M = get(white_text_handles, 'Color');
140 if iscell(M)
141  M = cell2mat(M);
142 end
143 M = sum(M, 2);
144 black_text_handles = white_text_handles(M == 0);
145 white_text_handles = white_text_handles(M == 3);
146 % Set the font colors slightly off their correct values
147 set(black_text_handles, 'Color', [0 0 0] + eps);
148 set(white_text_handles, 'Color', [1 1 1] - eps);
149 % MATLAB bug fix - white lines can come out funny sometimes
150 % Find the white lines
151 white_line_handles = findobj(fig, 'Type', 'line');
152 M = get(white_line_handles, 'Color');
153 if iscell(M)
154  M = cell2mat(M);
155 end
156 white_line_handles = white_line_handles(sum(M, 2) == 3);
157 % Set the line color slightly off white
158 set(white_line_handles, 'Color', [1 1 1] - 0.00001);
159 % Print to eps file
160 print(fig, options{:}, name);
161 % Reset the font and line colors
162 set(black_text_handles, 'Color', [0 0 0]);
163 set(white_text_handles, 'Color', [1 1 1]);
164 set(white_line_handles, 'Color', [1 1 1]);
165 % Reset paper size
166 set(fig, 'PaperPositionMode', old_pos_mode, 'PaperOrientation', old_orientation);
167 % Correct the fonts
168 if ~isempty(font_swap)
169  % Reset the font names in the figure
170  for a = update
171  set(font_handles(a), 'FontName', fonts{a}, 'FontSize', fonts_size(a));
172  end
173  % Replace the font names in the eps file
174  font_swap = font_swap(2:3,:);
175  try
176  swap_fonts(name, font_swap{:});
177  catch
178  warning('swap_fonts() failed. This is usually because the figure contains a large number of patch objects. Consider exporting to a bitmap format in this case.');
179  return
180  end
181 end
182 if using_hg2(fig)
183  % Move the bounding box to the top of the file
184  try
185  move_bb(name);
186  catch
187  warning('move_bb() failed. This is usually because the figure contains a large number of patch objects. Consider exporting to a bitmap format in this case.');
188  end
189 else
190  % Fix the line styles
191  try
192  fix_lines(name);
193  catch
194  warning('fix_lines() failed. This is usually because the figure contains a large number of patch objects. Consider exporting to a bitmap format in this case.');
195  end
196 end
197 
198 function swap_fonts(fname, varargin)
199 % Read in the file
200 fh = fopen(fname, 'r');
201 if fh == -1
202  error('File %s not found.', fname);
203 end
204 try
205  fstrm = fread(fh, '*char')';
206 catch ex
207  fclose(fh);
208  rethrow(ex);
209 end
210 fclose(fh);
211 
212 % Replace the font names
213 for a = 1:2:numel(varargin)
214  %fstrm = regexprep(fstrm, [varargin{a} '-?[a-zA-Z]*\>'], varargin{a+1}(~isspace(varargin{a+1})));
215  fstrm = regexprep(fstrm, varargin{a}, varargin{a+1}(~isspace(varargin{a+1})));
216 end
217 
218 % Write out the updated file
219 fh = fopen(fname, 'w');
220 if fh == -1
221  error('Unable to open %s for writing.', fname2);
222 end
223 try
224  fwrite(fh, fstrm, 'char*1');
225 catch ex
226  fclose(fh);
227  rethrow(ex);
228 end
229 fclose(fh);
230 
231 function move_bb(fname)
232 % Read in the file
233 fh = fopen(fname, 'r');
234 if fh == -1
235  error('File %s not found.', fname);
236 end
237 try
238  fstrm = fread(fh, '*char')';
239 catch ex
240  fclose(fh);
241  rethrow(ex);
242 end
243 fclose(fh);
244 
245 % Find the bounding box
246 [s, e] = regexp(fstrm, '%%BoundingBox: [\w\s()]*%%');
247 if numel(s) == 2
248  fstrm = fstrm([1:s(1)-1 s(2):e(2)-2 e(1)-1:s(2)-1 e(2)-1:end]);
249 end
250 
251 % Write out the updated file
252 fh = fopen(fname, 'w');
253 if fh == -1
254  error('Unable to open %s for writing.', fname2);
255 end
256 try
257  fwrite(fh, fstrm, 'char*1');
258 catch ex
259  fclose(fh);
260  rethrow(ex);
261 end
262 fclose(fh);