Every now and then I come across some internal Matlab bugs. In many cases I find a workaround and move on, sometimes bothering to report the bugs to MathWorks support, but often not. In truth, it’s a bit frustrating to hear the standard response that the issue [or “unexpected behavior”, but never “bug” – apparently that’s a taboo word] “has been reported to the development team and they will consider fixing it in one of the future releases of MATLAB”.
To date I’ve reported dozens of bugs and as far as I can tell, few if any of them have actually been fixed, years after I’ve reported them. None of them appear on Matlab’s official bug parade, which is only a small subset of the full list that MathWorks keeps hidden for some unknown reason (update: see the discussion in the comments thread below, especially the input by Steve Eddins). Never mind, I don’t take it personally, I simply find a workaround and move on. I’ve already posted about this before. Today I’ll discuss two additional bugs I’ve run across once-too-often, and my workarounds:
Nothing really earth-shattering, but annoying nonetheless.
Saving non-Latin Command Window text using diary
The diary function is well-known for saving Matlab’s Command-Window (CW) text to a file. The function has existed for the past two decades at least, possibly even longer.
Unfortunately, perhaps the developer never thought that Matlab would be used outside the Americas and Western Europe, otherwise I cannot understand why to this day diary saves the text in ASCII format rather than the UTF-16 variant used by the CW. This works ok for basic Latin characters, but anyone who outputs Chinese, Japanese, Korean, Hindi, Arabic, Hebrew or other alphabets to the CW, and tries to save it using diary, will find the file unreadable.
Here is a sample illustrative script, that outputs the Arabic word salaam (peace, سلام) to the CW and then tries to save this using diary. If you try it, you will see it ok in the CW, but garbage text in the generated text file:
>> fname='diary_bug.txt'; diary(fname); disp(char([1587,1604,1575,1605])); diary off; winopen(fname) سلام
The problem is that since diary assumes ASCII characters, any characters having a numeric value above 255 get truncated and are stored as invalid 1-byte characters, char(26) in this case.
Here’s my workaround:
% Output Command Window text to a text file function saveCmdWinText(filename) cmdWinDoc = com.mathworks.mde.cmdwin.CmdWinDocument.getInstance; txt = char(cmdWinDoc.getText(0,cmdWinDoc.getLength)); fid = fopen(filename,'W'); fwrite(fid,txt,'uint16'); % store as 2-byte characters fclose(fid); %winopen(filename); % in case you wish to verify... end
This works well, saving the characters in their original 2-byte format, for those alphabets that use 2-bytes: non-basic Latins, Greek, Cyrillic, Armenian, Arabic, Hebrew, Coptic, Syriac and Tāna (I don’t think there are more than a handful of Matlab users who use Coptic, Syriac or Tāna but never mind). However, UTF-8 specifies that CJK characters need 3-4 bytes and this is apparently not supported in Matlab, whose basic char data type only has 2 bytes, so I assume that Chinese, Japanese and Korean will probably require a different solution (perhaps the internal implementation of char and the CW is different in the Chinese/Japanese versions of Matlab, I really don’t know. If this is indeed the case, then perhaps a variant of my workaround can also be used for CJK output).
Correction #1: I have learned since posting (see Steve Eddins’ comment below) that Matlab actually uses UTF-16 rather than UTF-8, solving the CJK issue. I humbly stand corrected.
Correction #2: The
saveCmdWinTextcode above saves the CW text in UTF-16 format. This may be problematic in some text editors that are not UTF-savvy. For such editors (or if your editor get confused with the various BOM/endianness options), consider saving the data in UTF-8 format – again, assuming you’re not using an alphabet [such as CJK] outside the ASCII range (thanks Rob):
function saveCmdWinText_UTF8(filename) cmdWinDoc = com.mathworks.mde.cmdwin.CmdWinDocument.getInstance; txt = char(cmdWinDoc.getText(0,cmdWinDoc.getLength)); fid = fopen(filename,'W','n','utf-8'); fwrite(fid,txt,'char'); fclose(fid); %winopen(filename); % in case you wish to verify... end
Also, this workaround is problematic in the sense that it’s a one-time operation that stores the entire CW text that is visible at that point. This is more limited than diary‘s ability to start and stop output recording in mid-run, and to record output on-the-fly (rather than only at the end). Still, it does provide a solution in case you output non-ASCII 2-byte characters to the CW.
Update: I plan to post a utility to the Matlab File Exchange in the near future that will mimic diary‘s ability to start/stop text recording, rather than simply dumping the entire CW contents to file. I’ll update here when this utility is ready for download.
There are various other bugs related to entering non-Latin (and specifically RTL) characters in the CW and the Matlab Editor. Solving the diary bug is certainly the least of these worries. Life goes on…
p.s. – I typically use this translator to convert from native script to UTF codes that can be used in Matlab. I’m sure there are plenty of other translators, but this one does the job well enough for me.
For people interested in learning more about the Command Window internals, take a look at my cprintf and setPrompt utilities.
Printing GUIs reliably
Matlab has always tried to be far too sophisticated for its own good when printing figures. There’s plenty of internal code that tries to handle numerous circumstances in the figure contents for optimal output. Unfortunately, this code also has many bugs. Try printing even a slightly-complex GUI containing panels and/or Java controls and you’ll see components overlapping each other, not being printed, and/or being rendered incorrectly in the printed output. Not to mention the visible flicker that happens when Matlab modifies the figure in preparation for printing, and then modifies it back to the original.
All this when a simple printout of a screen-capture would be both much faster and 100% reliable.
Which is where my ScreenCapture utility comes in. Unlike Matlab’s print and getframe, ScreenCapture takes an actual screen-capture of an entire figure, or part of a figure (or even a desktop area outside any Matlab figure), and can then send the resulting image to a Matlab variable (2D RGB image), an image file, system clipboard, or the printer. We can easily modify the <Print> toolbar button and menu item to use this utility rather than the builtin print function:
hToolbar = findall(gcf,'tag','FigureToolBar'); hPrintButton = findall(hToolbar, 'tag','Standard.PrintFigure'); set(hPrintButton, 'ClickedCallback','screencapture(gcbf,,''printer'')'); hPrintMenuItem = findall(gcf, 'type','uimenu', 'tag','printMenu'); set(hPrintMenuItem, 'Callback','screencapture(gcbf,,''printer'')');
This prints the entire figure, including the frame, menubar and toolbar (if any). If you just wish to print the figure’s content area, then make sure to create a top-level uipanel that spans the entire content area and in which all the contents are included. Then simply pass this top-level container handle to ScreenCapture:
hTopLevelContainer = uipanel('BorderType','none', 'Parent',gcf, 'Units','norm', 'Pos',[0,0,1,1]); ... hToolbar = findall(gcf,'tag','FigureToolBar'); hPrintButton = findall(hToolbar, 'tag','Standard.PrintFigure'); set(hPrintButton, 'ClickedCallback',@(h,e)screencapture(hTopLevelContainer,,'printer')); hPrintMenuItem = findall(gcf, 'type','uimenu', 'tag','printMenu'); set(hPrintMenuItem, 'Callback',@(h,e)screencapture(hTopLevelContainer,,'printer'));
In certain cases (depending on platform/OS/Matlab-release), the result may capture a few pixels from the figure’s window frame. This can easily be corrected by specifying a small offset to ScreenCapture:
set(hPrintButton, 'ClickedCallback',@(h,e)printPanel(hTopLevelContainer)); set(hPrintMenuItem, 'Callback',@(h,e)printPanel(hTopLevelContainer)); function printPanel(hTopLevelContainer) pos = getpixelposition(hTopLevelContainer); screencapture(hTopLevelContainer, pos+[2,4,0,0], 'printer'); end