Robochameleon  v1.0
exportfig.m
1 function exportfig(varargin)
2 %EXPORTFIG Export a figure to Encapsulated Postscript.
3 % EXPORTFIG(H, FILENAME) writes the figure H to FILENAME. H is
4 % a figure handle and FILENAME is a string that specifies the
5 % name of the output file.
6 %
7 % EXPORTFIG(...,PARAM1,VAL1,PARAM2,VAL2,...) specifies
8 % parameters that control various characteristics of the output
9 % file.
10 %
11 % Format Paramter:
12 % 'Format' one of the strings 'eps','eps2','jpeg','png','preview'
13 % specifies the output format. Defaults to 'eps'.
14 % The output format 'preview' does not generate an output
15 % file but instead creates a new figure window with a
16 % preview of the exported figure. In this case the
17 % FILENAME parameter is ignored.
18 %
19 % 'Preview' one of the strings 'none', 'tiff'
20 % specifies a preview for EPS files. Defaults to 'none'.
21 %
22 % Size Parameters:
23 % 'Width' a positive scalar
24 % specifies the width in the figure's PaperUnits
25 % 'Height' a positive scalar
26 % specifies the height in the figure's PaperUnits
27 %
28 % Specifying only one dimension sets the other dimension
29 % so that the exported aspect ratio is the same as the
30 % figure's current aspect ratio.
31 % If neither dimension is specified the size defaults to
32 % the width and height from the figure's PaperPosition.
33 %
34 % Rendering Parameters:
35 % 'Color' one of the strings 'bw', 'gray', 'cmyk'
36 % 'bw' specifies that lines and text are exported in
37 % black and all other objects in grayscale
38 % 'gray' specifies that all objects are exported in grayscale
39 % 'cmyk' specifies that all objects are exported in color
40 % using the CMYK color space
41 % 'Renderer' one of the strings 'painters', 'zbuffer', 'opengl'
42 % specifies the renderer to use
43 % 'Resolution' a positive scalar
44 % specifies the resolution in dots-per-inch.
45 %
46 % The default color setting is 'bw'.
47 %
48 % Font Parameters:
49 % 'FontMode' one of the strings 'scaled', 'fixed'
50 % 'FontSize' a positive scalar
51 % in 'scaled' mode multiplies with the font size of each
52 % text object to obtain the exported font size
53 % in 'fixed' mode specifies the font size of all text
54 % objects in points
55 % 'FontEncoding' one of the strings 'latin1', 'adobe'
56 % specifies the character encoding of the font
57 %
58 % If FontMode is 'scaled' but FontSize is not specified then a
59 % scaling factor is computed from the ratio of the size of the
60 % exported figure to the size of the actual figure. The minimum
61 % font size allowed after scaling is 5 points.
62 % If FontMode is 'fixed' but FontSize is not specified then the
63 % exported font sizes of all text objects is 7 points.
64 %
65 % The default 'FontMode' setting is 'scaled'.
66 %
67 % Line Width Parameters:
68 % 'LineMode' one of the strings 'scaled', 'fixed'
69 % 'LineWidth' a positive scalar
70 % the semantics of LineMode and LineWidth are exactly the
71 % same as FontMode and FontSize, except that they apply
72 % to line widths instead of font sizes. The minumum line
73 % width allowed after scaling is 0.5 points.
74 % If LineMode is 'fixed' but LineWidth is not specified
75 % then the exported line width of all line objects is 1
76 % point.
77 %
78 % Examples:
79 % exportfig(gcf,'fig1.eps','height',3);
80 % Exports the current figure to the file named 'fig1.eps' with
81 % a height of 3 inches (assuming the figure's PaperUnits is
82 % inches) and an aspect ratio the same as the figure's aspect
83 % ratio on screen.
84 %
85 % exportfig(gcf, 'fig2.eps', 'FontMode', 'fixed',...
86 % 'FontSize', 10, 'color', 'cmyk' );
87 % Exports the current figure to 'fig2.eps' in color with all
88 % text in 10 point fonts. The size of the exported figure is
89 % the figure's PaperPostion width and height.
90 
91 
92 if (nargin < 2)
93  error('Too few input arguments');
94 end
95 
96 % exportfig(H, filename, ...)
97 H = varargin{1};
98 if ~ishandle(H) | ~strcmp(get(H,'type'), 'figure')
99  error('First argument must be a handle to a figure.');
100 end
101 filename = varargin{2};
102 if ~ischar(filename)
103  error('Second argument must be a string.');
104 end
105 paramPairs = varargin(3:end);
106 
107 % Do some validity checking on param-value pairs
108 if (rem(length(paramPairs),2) ~= 0)
109  error(['Invalid input syntax. Optional parameters and values' ...
110  ' must be in pairs.']);
111 end
112 
113 format = 'eps';
114 preview = 'none';
115 width = -1;
116 height = -1;
117 color = 'bw';
118 fontsize = -1;
119 fontmode='scaled';
120 linewidth = -1;
121 linemode=[];
122 fontencoding = 'latin1';
123 renderer = [];
124 resolution = [];
125 
126 % Process param-value pairs
127 args = {};
128 for k = 1:2:length(paramPairs)
129  param = lower(paramPairs{k});
130  if (~ischar(param))
131  error('Optional parameter names must be strings');
132  end
133  value = paramPairs{k+1};
134 
135  switch (param)
136  case 'format'
137  format = value;
138  if (~strcmp(format,{'eps','eps2','jpeg','png','preview'}))
139  error(['Format must be ''eps'', ''eps2'', ''jpeg'', ''png'' or' ...
140  ' ''preview''.']);
141  end
142  case 'preview'
143  preview = value;
144  if (~strcmp(preview,{'none','tiff'}))
145  error('Preview must be ''none'' or ''tiff''.');
146  end
147  case 'width'
148  width = LocalToNum(value);
149  if(~LocalIsPositiveScalar(width))
150  error('Width must be a numeric scalar > 0');
151  end
152  case 'height'
153  height = LocalToNum(value);
154  if(~LocalIsPositiveScalar(height))
155  error('Height must be a numeric scalar > 0');
156  end
157  case 'color'
158  color = lower(value);
159  if (~strcmp(color,{'bw','gray','cmyk'}))
160  error('Color must be ''bw'', ''gray'' or ''cmyk''.');
161  end
162  case 'fontmode'
163  fontmode = lower(value);
164  if (~strcmp(fontmode,{'scaled','fixed'}))
165  error('FontMode must be ''scaled'' or ''fixed''.');
166  end
167  case 'fontsize'
168  fontsize = LocalToNum(value);
169  if(~LocalIsPositiveScalar(fontsize))
170  error('FontSize must be a numeric scalar > 0');
171  end
172  case 'fontencoding'
173  fontencoding = lower(value);
174  if (~strcmp(fontencoding,{'latin1','adobe'}))
175  error('FontEncoding must be ''latin1'' or ''adobe''.');
176  end
177  case 'linemode'
178  linemode = lower(value);
179  if (~strcmp(linemode,{'scaled','fixed'}))
180  error('LineMode must be ''scaled'' or ''fixed''.');
181  end
182  case 'linewidth'
183  linewidth = LocalToNum(value);
184  if(~LocalIsPositiveScalar(linewidth))
185  error('LineWidth must be a numeric scalar > 0');
186  end
187  case 'renderer'
188  renderer = lower(value);
189  if (~strcmp(renderer,{'painters','zbuffer','opengl'}))
190  error('Renderer must be ''painters'', ''zbuffer'' or ''opengl''.');
191  end
192  case 'resolution'
193  resolution = LocalToNum(value);
194  if ~(isnumeric(value) & (prod(size(value)) == 1) & (value >= 0));
195  error('Resolution must be a numeric scalar >= 0');
196  end
197  otherwise
198  error(['Unrecognized option ' param '.']);
199  end
200 end
201 
202 allLines = findall(H, 'type', 'line');
203 allText = findall(H, 'type', 'text');
204 allAxes = findall(H, 'type', 'axes');
205 allImages = findall(H, 'type', 'image');
206 allLights = findall(H, 'type', 'light');
207 allPatch = findall(H, 'type', 'patch');
208 allSurf = findall(H, 'type', 'surface');
209 allRect = findall(H, 'type', 'rectangle');
210 allFont = [allText; allAxes];
211 allColor = [allLines; allText; allAxes; allLights];
212 allMarker = [allLines; allPatch; allSurf];
213 allEdge = [allPatch; allSurf];
214 allCData = [allImages; allPatch; allSurf];
215 
216 old.objs = {};
217 old.prop = {};
218 old.values = {};
219 
220 % Process format and preview parameter
221 showPreview = strcmp(format,'preview');
222 if showPreview
223  format = 'png';
224  filename = [tempName '.png'];
225 end
226 if strncmp(format,'eps',3) & ~strcmp(preview,'none')
227  args = {args{:}, ['-' preview]};
228 end
229 
230 hadError = 0;
231 try
232  % Process size parameters
233  paperPos = get(H, 'PaperPosition');
234  old = LocalPushOldData(old, H, 'PaperPosition', paperPos);
235  figureUnits = get(H, 'Units');
236  set(H, 'Units', get(H,'PaperUnits'));
237  figurePos = get(H, 'Position');
238  aspectRatio = figurePos(3)/figurePos(4);
239  set(H, 'Units', figureUnits);
240  if (width == -1) & (height == -1)
241  width = paperPos(3);
242  height = paperPos(4);
243  elseif (width == -1)
244  width = height * aspectRatio;
245  elseif (height == -1)
246  height = width / aspectRatio;
247  end
248  set(H, 'PaperPosition', [0 0 width height]);
249  paperPosMode = get(H, 'PaperPositionMode');
250  old = LocalPushOldData(old, H, 'PaperPositionMode', paperPosMode);
251  set(H, 'PaperPositionMode', 'manual');
252 
253  % Process rendering parameters
254  switch (color)
255  case {'bw', 'gray'}
256  if ~strcmp(color,'bw') & strncmp(format,'eps',3)
257  format = [format 'c'];
258  end
259  args = {args{:}, ['-d' format]};
260 
261  %compute and set gray colormap
262  oldcmap = get(H,'Colormap');
263  newgrays = 0.30*oldcmap(:,1) + 0.59*oldcmap(:,2) + 0.11*oldcmap(:,3);
264  newcmap = [newgrays newgrays newgrays];
265  old = LocalPushOldData(old, H, 'Colormap', oldcmap);
266  set(H, 'Colormap', newcmap);
267 
268  %compute and set ColorSpec and CData properties
269  old = LocalUpdateColors(allColor, 'color', old);
270  old = LocalUpdateColors(allAxes, 'xcolor', old);
271  old = LocalUpdateColors(allAxes, 'ycolor', old);
272  old = LocalUpdateColors(allAxes, 'zcolor', old);
273  old = LocalUpdateColors(allMarker, 'MarkerEdgeColor', old);
274  old = LocalUpdateColors(allMarker, 'MarkerFaceColor', old);
275  old = LocalUpdateColors(allEdge, 'EdgeColor', old);
276  old = LocalUpdateColors(allEdge, 'FaceColor', old);
277  old = LocalUpdateColors(allCData, 'CData', old);
278 
279  case 'cmyk'
280  if strncmp(format,'eps',3)
281  format = [format 'c'];
282  args = {args{:}, ['-d' format], '-cmyk'};
283  else
284  args = {args{:}, ['-d' format]};
285  end
286  otherwise
287  error('Invalid Color parameter');
288  end
289  if (~isempty(renderer))
290  args = {args{:}, ['-' renderer]};
291  end
292  if (~isempty(resolution)) | ~strncmp(format,'eps',3)
293  if isempty(resolution)
294  resolution = 0;
295  end
296  args = {args{:}, ['-r' int2str(resolution)]};
297  end
298 
299  % Process font parameters
300  if (~isempty(fontmode))
301  oldfonts = LocalGetAsCell(allFont,'FontSize');
302  switch (fontmode)
303  case 'fixed'
304  oldfontunits = LocalGetAsCell(allFont,'FontUnits');
305  old = LocalPushOldData(old, allFont, {'FontUnits'}, oldfontunits);
306  set(allFont,'FontUnits','points');
307  if (fontsize == -1)
308  set(allFont,'FontSize',7);
309  else
310  set(allFont,'FontSize',fontsize);
311  end
312  case 'scaled'
313  if (fontsize == -1)
314  wscale = width/figurePos(3);
315  hscale = height/figurePos(4);
316  scale = min(wscale, hscale);
317  else
318  scale = fontsize;
319  end
320  newfonts = LocalScale(oldfonts,scale,5);
321  set(allFont,{'FontSize'},newfonts);
322  otherwise
323  error('Invalid FontMode parameter');
324  end
325  % make sure we push the size after the units
326  old = LocalPushOldData(old, allFont, {'FontSize'}, oldfonts);
327  end
328  if strcmp(fontencoding,'adobe') & strncmp(format,'eps',3)
329  args = {args{:}, '-adobecset'};
330  end
331 
332  % Process linewidth parameters
333  if (~isempty(linemode))
334  oldlines = LocalGetAsCell(allMarker,'LineWidth');
335  old = LocalPushOldData(old, allMarker, {'LineWidth'}, oldlines);
336  switch (linemode)
337  case 'fixed'
338  if (linewidth == -1)
339  set(allMarker,'LineWidth',1);
340  else
341  set(allMarker,'LineWidth',linewidth);
342  end
343  case 'scaled'
344  if (linewidth == -1)
345  wscale = width/figurePos(3);
346  hscale = height/figurePos(4);
347  scale = min(wscale, hscale);
348  else
349  scale = linewidth;
350  end
351  newlines = LocalScale(oldlines, scale, 0.5);
352  set(allMarker,{'LineWidth'},newlines);
353  otherwise
354  error('Invalid LineMode parameter');
355  end
356  end
357 
358  % Export
359  print(H, filename, args{:});
360  fixPSlinestyle(filename);
361 catch
362  hadError = 1;
363 end
364 
365 % Restore figure settings
366 for n=1:length(old.objs)
367  set(old.objs{n}, old.prop{n}, old.values{n});
368 end
369 
370 if hadError
371  error(deblank(lasterr));
372 end
373 
374 % Show preview if requested
375 if showPreview
376  X = imread(filename,'png');
377  delete(filename);
378  f = figure( 'Name', 'Preview', ...
379  'Menubar', 'none', ...
380  'NumberTitle', 'off', ...
381  'Visible', 'off');
382  image(X);
383  axis image;
384  ax = findobj(f, 'type', 'axes');
385  set(ax, 'Units', get(H,'PaperUnits'), ...
386  'Position', [0 0 width height], ...
387  'Visible', 'off');
388  set(ax, 'Units', 'pixels');
389  axesPos = get(ax,'Position');
390  figPos = get(f,'Position');
391  rootSize = get(0,'ScreenSize');
392  figPos(3:4) = axesPos(3:4);
393  if figPos(1) + figPos(3) > rootSize(3)
394  figPos(1) = rootSize(3) - figPos(3) - 50;
395  end
396  if figPos(2) + figPos(4) > rootSize(4)
397  figPos(2) = rootSize(4) - figPos(4) - 50;
398  end
399  set(f, 'Position',figPos, ...
400  'Visible', 'on');
401 end
402 
403 %
404 % Local Functions
405 %
406 
407 function outData = LocalPushOldData(inData, objs, prop, values)
408 outData.objs = {inData.objs{:}, objs};
409 outData.prop = {inData.prop{:}, prop};
410 outData.values = {inData.values{:}, values};
411 
412 function cellArray = LocalGetAsCell(fig,prop);
413 cellArray = get(fig,prop);
414 if (~isempty(cellArray)) & (~iscell(cellArray))
415  cellArray = {cellArray};
416 end
417 
418 function newArray = LocalScale(inArray, scale, minValue)
419 n = length(inArray);
420 newArray = cell(n,1);
421 for k=1:n
422  newArray{k} = max(minValue,scale*inArray{k}(1));
423 end
424 
425 function newArray = LocalMapToGray(inArray);
426 n = length(inArray);
427 newArray = cell(n,1);
428 for k=1:n
429  color = inArray{k};
430  if (~isempty(color))
431  if ischar(color)
432  switch color(1)
433  case 'y'
434  color = [1 1 0];
435  case 'm'
436  color = [1 0 1];
437  case 'c'
438  color = [0 1 1];
439  case 'r'
440  color = [1 0 0];
441  case 'g'
442  color = [0 1 0];
443  case 'b'
444  color = [0 0 1];
445  case 'w'
446  color = [1 1 1];
447  case 'k'
448  color = [0 0 0];
449  otherwise
450  newArray{k} = color;
451  end
452  end
453  if ~ischar(color)
454  color = 0.30*color(1) + 0.59*color(2) + 0.11*color(3);
455  end
456  end
457  if isempty(color) | ischar(color)
458  newArray{k} = color;
459  else
460  newArray{k} = [color color color];
461  end
462 end
463 
464 function newArray = LocalMapCData(inArray);
465 n = length(inArray);
466 newArray = cell(n,1);
467 for k=1:n
468  color = inArray{k};
469  if (ndims(color) == 3) & isa(color,'double')
470  gray = 0.30*color(:,:,1) + 0.59*color(:,:,2) + 0.11*color(:,:,3);
471  color(:,:,1) = gray;
472  color(:,:,2) = gray;
473  color(:,:,3) = gray;
474  end
475  newArray{k} = color;
476 end
477 
478 function outData = LocalUpdateColors(inArray, prop, inData)
479 value = LocalGetAsCell(inArray,prop);
480 outData.objs = {inData.objs{:}, inArray};
481 outData.prop = {inData.prop{:}, {prop}};
482 outData.values = {inData.values{:}, value};
483 if (~isempty(value))
484  if strcmp(prop,'CData')
485  value = LocalMapCData(value);
486  else
487  value = LocalMapToGray(value);
488  end
489  set(inArray,{prop},value);
490 end
491 
492 function bool = LocalIsPositiveScalar(value)
493 bool = isnumeric(value) & ...
494  prod(size(value)) == 1 & ...
495  value > 0;
496 
497 function value = LocalToNum(value)
498 if ischar(value)
499  value = str2num(value);
500 end