Low Level Text Output - macos

I have not done any MACOS programming in several years. However, I now need to test out some algorithms for formatting text as paragraphs. For these tests, I need to be able to display characters with absolute position control.
These tests need to determine where line breaks are to go within a paragraph and the amount of work spacing, while taking into account hyphenation, ligatures, page breaks, kerning and the like.
When I last did such things, I was using the text functions of the CGContext class. Upon returning from my hiatus, I find all of these function are marked "deprecated" in the documentation—which conveniently leaves out what the current acceptable replacement is.
What is the current, approved process for drawing single glyphs on Mac OS?
Functions that deal with formatting strings would defeat the purpose of the academic experiment here.

Related

Terminal control sequence for character display width?

Correct display of Unicode in a terminal would appear to benefit from the displaying app knowing the number of character cells used to display text. Functions like wcwidth() are a reasonable start, but there can be a lot of variation, for example what a terminal displays for invalid characters, ambiguous width Asian characters, combining characters out of context, etc.
Would it be reasonable to extend terminal apps with a new control sequence to measure with display width of a string, which display apps could use to characterize the terminal? If so, what details are worth considering, e.g. what sequence to use, whether to specify UTF-8, also how to handle terminals that do not know this hypothetical new control sequence? Would it have any likelihood of wide adoption?
If not, what is the flaw in the idea? Is perhaps reading the cursor position after display a better (and already supported) option? Or is there a good different approach?
There's no need, because the existing cursor position report (which can be used to get the position before and after printing a string) gives the length.
Adding a new control sequence to get the attributes of a character (width, combining, controls such as tab) wouldn't help much because the application still has to work with the system's locale information for performance reasons: it would drastically slow down an application if it had to ask after each character where the cursor really was.

Best practice for sizing text buttons when doing globalization and localization

Say we have an overlay DHTML panel on a web page that contains two buttons in the top occupying whole width of the dialog, like this:
Text for Button 1 and Button 2 should be localized. Content of checkbox group is static and should not be localized.
There can be a big difference between button text width in different languages (~100% extra space in comparison with English version).
The question is which strategy to apply for sizing Button 1 and Button 2 depending on the length of the text content:
Make buttons fixed width and truncate text content with ellipsis.
Make buttons fixed width and wrap text content on the subsequent lines making buttons grow in height.
Make buttons fixed width but make them wide enough to host text in all languages without truncation or wrapping.
Make buttons dynamically adjust width and make panel grow horizontally with their size.
I am leaning towards using third or forth option according to the several UI localization best practices that were found:
Preparing the User Interface for Localization
Best Practices for Globalization and Localization
Globalization Step-by-Step - UI Considerations
Still we have some debate in the team discussing the best option and it would be interesting to hear concious outer voice of the community.
I am interested in the best approach for this specific case as well general guidelines for solving web UI sizing issues in regards of localization.
Thanks!
As you perhaps know, text shortened with ellipsis (or with a single dot) could be incomprehensible:
How compr. is th.?
You should seen a lot of this staff in mobile area (phones, tablets, etc.), such translations look ugly. OK, with lower screen resolutions you actually end up with no choice (unless you can create some auto-scrolling text). But in case of web interface you certainly have a choice.
Typically, there are two kind of solutions corresponding to your points #3 and #4. Personally, I am leaning to #4 - make buttons auto-sizable. This of course will result with inconsistently sized buttons, but there is little we can do about it.
If you cannot use solution #4 (i.e. UI Designer is strong opponent of this technique), you may modify solution #3 a bit. Basically, what I used in past projects was, I had buttons of fixed sizes and default size was capable to fit most of languages (except Polish and Russian of course), but I also had several CSS classes that defined wider buttons. When localizing into "too long" languages I simply used the most wide button class I could. If the text still didn't fit, then I asked the translators for shortening it (usually re-phrasing it and shorten the text with a single dot as a last resort).
However, please keep in mind that it increases localization costs. This is the reason I would not recommend this method.
As for solution #2, you will end up with ugly looking UI. You simply have no control over how browser will wrap a text, and you will have a lot of texts that go outside the button clipping rectangle (overlap it).
As for solution #1, using ellipsis is a bad idea for two reasons. First one is, ellipsis is not valid in many languages (this especially regards to Asian languages). The second one is, as I understand you want to do that automatically. In this case, you won't be able to measure strings - their actual, on screen size, written with a fall-back font. In case of web UI, you don't know whether user has particular font installed, so you will be guessing the size (OK, with Dojo you can theoretically measure it on the client side). This will of course result in overlapping text (if you decide on dynamic shortening over your chosen font) or totally incomprehensible text (if you decide on shortening after say 8 characters). I had a project which started to use static shortening, it was a total mess. Then we switch to dynamic and it is still not good enough.
To counter potential UI Designer argument that goes "we have no space for string expansion", I can only say that means that you designed the interface incorrectly for it is too crowded. This is the point where I18n goes hand in hand with UI design best practices: strive for simplicity (in UI design). The result would be easy to use and easy to localize application.

How do you change the letter-spacing/tracking in core text?

This could probably also be asked as "Is kCTKernAttributeName a misnomer?"
I need to change the letter spacing/tracking of some text in iOS. (The font I'm using is a little too tight at small sizes.) There are core graphics routines that will change character spacing, but those routines don't handle Unicode. There are other core graphics routines that are defined in terms of glyphs but those seem like a world of hurt, among other things, not having the safety net of reverting back to system fonts for glyphs that don't exist in my font.
So core text seems like the way to do this and core text supports kCTKernAttributeName on CFAttributedString. I think this will do what I want, though this really isn't kerning since kerning is a generally a character-pair attribute and this (appears to be, from the docs) just a uniform adjustment to the glyph advance for all glyphs, i.e., tracking.
If anyone knows before I go down the rather painful path of converting to the core text API ...
kCTKernAttribute name should do what you want. Setting it over a range of text adjusts the inter-glyph spacing consistently, irrespective of the specific glyphs.
I think part of the problem is that kerning seems to have been a virtual synonym of tracking (it's still just "adjust the spacing between (letters or characters) in a piece of text to be printed" in the dictionary that comes with OS X), and is now adopting just the meaning of pair kerning because of the redundancy. Probably an etymologist would be better placed to comment on that side of things...

Display of Asian characters (with Unicode): Difference in character spacing when presented in a RichEdit control compared with using ExtTextOut

This picture illustrates my predicament:
All of the characters appear to be the same size, but the space between them is different when presented in a RichEdit control compared with when I use ExtTextOut.
I would like to present the characters the same as in the RichEdit control (ideally), in order to preserve wrap positions.
Can anyone tell me:
a) Which is the more correct representation?
b) Why the RichEdit control displays the text with no gaps between the Asian Characters?
c) Is there any way to make ExtTextOut reproduce the behaviour of the RichEdit control when drawing these characters?
d) Would this be any different if I was working on an Asian version of Windows?
Perhaps I'm being optimistic, but if anyone has any hints to offer, I'd be very interested to hear.
In case it helps:
Here's my text:
快的棕色狐狸跳在懶惰狗1 2 3 4 5 6 7 8 9 0
apologies to Asian readers, this is merely for testing our Unicode implemetation and I don't even know what language the characters are taken from, let alone whether they mean anything
In order to view the effect by pasting these characters into a RichEdit control (eg. Wordpad), you may find you have to swipe them and set the font to 'Arial'.
The rich text that I obtain is:
{\rtf1\ansi\ansicpg1252\deff0\deflang2057{\fonttbl{\f0\fnil\fcharset0 Arial;}}{\colortbl ;\red0\green0\blue0;}\viewkind4\uc1\pard\sa200\sl276\slmult1\lang9\fs22\u24555?\u30340?\u26837?\u33394?\u29392?\u29432?\u36339?\u22312?\u25078?\u24816?\u29399?1 2 3 4 5 6 7 8 9 0\par\pard\'a3 $$ \'80\'80\cf1\lang2057\fs16\par}
It doesn't appear to contain a value for character 'pitch' which was my first thought.
I don't know the answer, but there are several things to suspect:
There are several versions of the rich edit control. Perhaps you're using an older one that doesn't have all the latest typographic improvements.
There are many styles and flags that affect the behavior of a rich editcontrol, so you might want to explore which ones are set and what they do. For example, look at EM_GETEDITSTYLE.
Many Asian fonts come in two versions on Windows. One is optimized for horizontal layout, and the other for vertical layout. That latter usually has the same name, but has # prepended to it. Perhaps you are using the wrong one in the rich edit control.
UPDATE: By messing around with Wordpad, I was able to reproduce the problem with the crowded text in the rich edit control.
Open a new document in Wordpad on Windows 7. Note that the selected font is Calibri.
Paste the sample text into the document.
Text appears correct, but Wordpad changed the font to SimSun.
Select the text and change the font back to Calibri or Arial.
The text will now be overcrowded, very similar to your example. Thus it appears the fundamental problem is with font linking and fallback. ExtTextOut is probably selecting an appropriate font for the script automatically. Your challenge is to figure out how to identify the right font for the script and set that font in the rich edit control.
This will only help with part of your problem, but there is a way to draw text to a DC that will look exactly the same as it does with RichEdit: what's called the windowless RichEdit control. It not exactly easy to use: I wrote a CodeProject article on it a few years back. I used this to solve the problem of a scrollable display of blocks of text, each one of which can be edited by clicking on it: the normal drawing is done with the windowless RichEdit, and the editing by showing a "real" RichEdit control on the top of it.
That would at least get you the text looking the same in both cases, though unfortunately both cases would show too little character spacing.
One further thought: if you could rely on Microsoft Office being installed, you could also try later versions of RichEdit that come with office. There's more about these on Murray Sargent's blog, as well as some interesting articles on font binding that might also help.
ExtTextOut allows you to specify the logical spacing between records. It has the parameter lpDx which is a const pointer to an array of values that indicate the distance between origins of adjacent character cells. The Microsoft API documentation notes that if you don't set it, then it sets it's own default spacing. I would have to say that's why ExtTextOut is working fine.
In particular, when you construct a EMR_EXTTEXTOUTW record in EMF, it populates an EMR_TEXT structure with this DX array - which looking at one of your comments, allowed the RichEdit to insert the EMF with the information contained in the record, whereby if you didn't set a font binding then the RTF record does some matching to work out what font to use.
In terms of the RichEdit control, the following article might be useful:
Use Font Binding in a Rich Edit Control
After character sets are assigned, Rich Edit scans the text around the
insertion point forward and backward to find the nearest fonts that
have been used for the character sets. If no font is found for a
character set, Rich Edit uses the font chosen by the client for that
character set. If the client hasn't specified a font for the character
set, Rich Edit uses the default font for that character set. If the
client wants some other font, the client can always change it, but
this approach will work most of the time. The current default font
choices are based on the following table. Note that the default fonts
are set per-process, and there are separate lists for UI usage and for
non-UI usage.
If you haven't set the characterset, then it further explains that it falls back to ANSI_CHARSET. However, it's most definitely a lot more complicated than that, as that blog article by Murray Sargent (a programmer at Microsoft) shows.

Localizing a modern xib-based Mac application

Ours is an open-source Mac application localized by volunteers. These volunteers will do their work on special localization builds of the software (with unstripped nibs), then send us the changes to integrate into the original xib and strings files.
The problem is that, while there is a way to integrate string changes without blowing away previous size changes, I can't see a way to integrate new string and size changes (as when we add or replace views).
The only way to do both that I can see is for localizers to work directly with the original xibs and send us diffs. That means they have to download the entire source code, not just a localizable version of the release, work in Xcode as well as IB, and either run the diff command themselves (per xib) or install and use Mercurial.
Is there any better way for a xib-based application?
UPDATE January 2014: Apple’s ‘autolayout’ code combined with their ‘Base’ localization stuff took most my ideas and improved on them. I recommend against using my old tools that I talk about in this answer. But, also, man was I right!
I strongly strongly STRONGLY recommend against frame changes in localizations. I know this runs counter to Apple's advice, but there are SO MANY problems with allowing frame changes - you end up with a billion edge cases.
Imagine you have 10 XIBs in your app, and you support 12 languages. You've got 120 different layouts to support, now. You just can't do this.
Change the strings, leave the views where they are. Make 'em bigger in ALL languages, if you need to. It sounds like this shouldn't work but it does. (I won three Apple Design Awards with an app that's localized in 10 or so languages this way.)
Specifics:
For radio and checkboxes, just let them extend far to the right, beyond the last English character. That also provides a nice big landing area for imprecise mousers.
For buttons, they should be wide anyhow, because it never looks good to have text cramped in the middle of the buttons.
For titles on tableview columns, you should autosize when you load 'em up, if needed.
For explanatory text, you should have some extra space to the right, and maybe an extra line. It just makes the English version of the XIB seem less cluttered. Sure, the Germans are going to see a slightly tighter XIB, but, hey, they're Germans -- they're probably used to that. There's probably even a German word for it. "Deutscheninterfakkenclutterlongen."
If a text field is centered, just add equal space on both sides. There's no reason not to.
I've combined this with scripts that suck all the strings out of my XIBs and put them in .strings files, and then dynamically put the strings back at run-time, so anyone can localize my app without any special tools. Just drop in a bunch of .strings files and run it!
Blog post including full source: [Lost in Translations]¹.
I confess that I'm not all that familiar with the process of localizing Mac apps. But I did run across a script that's part of the Three20 iPhone library that seems like it might be useful: diffstrings.py is a Python script that "compares your primary locale with all your other locales to help you determine which new strings need to be translated. It outputs XML files which can be translated, and then merged back into your strings files."
EDIT: As a companion to Wil Shipley's answer to this question, I'll add a link to a blog post he just wrote that goes into more detail about localization, and provides some of the tools that he's built to ease the process.
Where I used to work, we had this issue as well. Our app was getting translated into 10 different languages.
At first, we tried doing what Wil suggested, which is to make everything super wide and fit in every language. Unfortunately, "online backup" might be pretty short in English, but in other languages (especially Spanish), it's really long ("copia de seguridad" just means "backup"). Widening our UI made everything look pretty terrible.
One day, I was playing around with some Core Animation stuff and discovered the CAConstraint class. CAConstraint is basically a way to define a layout relationship between two CALayers. You give one layer a name (like "layerA") and then say "layerB is constrained [in such-and-such a way] to a sibling layer called layerA". Then, whenever the layer named layerA is repositioned or resized, layerB automatically moves as well. It's really neat, and it's just what we were looking for.
After a couple of days of work, I came up with what is now CHLayoutManager. It's basically a re-make of CAConstraint and friends, but for NSViews. Here's a simple example of how it works:
CHLayoutConstraint * centerHorizontal = [CHLayoutConstraint constraintWithAttribute:CHLayoutConstraintAttributeMidX relativeTo:#"superview" attribute:CHLayoutConstraintAttributeMidX];
CHLayoutConstraint * centerVertical = [CHLayoutConstraint constraintWithAttribute:CHLayoutConstraintAttributeMidY relativeTo:#"superview" attribute:CHLayoutConstraintAttributeMidY];
[aView addConstraint:centerHorizontal];
[aView addConstraint:centerVertical];
This will keep aView centered in its superview, regardless of how the superview is resized. Here's another:
[button1 setLayoutName:#"button1"];
[button2 addConstraint:[CHLayoutConstraint constraintWithAttribute:CHLayoutConstraintAttributeMinX relativeTo:#"button1" attribute:CHLayoutConstraintAttributeMaxX]];
[button2 addConstraint:[CHLayoutConstraint constraintWithAttribute:CHLayoutConstraintAttributeMaxY relativeTo:#"button1" attribute:CHLayoutConstraintAttributeMaxY]];
[button2 addConstraint:[CHLayoutConstraint constraintWithAttribute:CHLayoutConstraintAttributeWidth relativeTo:#"button1" attribute:CHLayoutConstraintAttributeWidth]];
This will keep button2 anchored to the right edge of button1, as well as keeping button2's Y position and width the same as button1's.
Internally, CHLayoutManager uses an NSValueTransformer to calculate the new positioning information. Some of the CHLayoutConstraint initializers accept an NSValueTransformer, so you can create arbitrarily complex layout manipulations.
We used this for constraining and laying out the entire UI, and then doing all of the localization in code (and subsequently calling -sizeToFit, with some modifications). Our UI would just flow into its final layout. It turned out to be extremely convenient. We'd just package up our .strings files, send them off to the translators, and then drop them in to place when we got them back, and our app would instantly be localized for that language.
CHLayoutManager isn't perfect. It doesn't resolve conflicts, but simply applies constraints in the order they're added. So you can constrain (for example) the MinX of a view 42 different ways, but only the last one will be used. Also, if you constrain the MinX and the MaxX, they'll also be applied in the order they're added and will not end up stretching or shrinking the width. In other words, constraining one attribute of a view will not affect the other attributes. It's compatible with 10.5+ (GC and non). However, due to some changes in Lion, it's unlikely that I'll address the shortcomings.
Despite these shortcomings, it's an extremely flexible framework, and (IMO) some pretty nifty code. (Plus, I swizzle -[NSView dealloc]! Yay!)
Update
Now that AppKit has this same functionality (via NSLayoutConstraint), I recommend using that system instead of CHLayoutManager. It is far more robust.
Bear in mind that XIB files are merely XML files in an obscure format, so you can translate those easily enough, provided that you can find what strings there are there to translate in the first place. For example, here's the snippet that creates a button called Jenson:
<object class="NSButtonCell" key="NSCell" id="41219959">
<int key="NSCellFlags">67239424</int>
<int key="NSCellFlags2">134217728</int>
<string key="NSContents">Jenson</string>
...
</object>
So you can get that string translated and then substitute occurrences of it in the XIB with your translated value. In order to verify it's working as expected, you could change the language to use random keys instead (like BUTTON_TITLE) which will make it easier to spot when one's missing.
However, the positions/sizes of the items are fixed, so you can have titles that overflow the space given in a different language. That's one of the reasons why Macs have separate XIB files for every language, to allow adjustments to be made on a language-by-language basis, however difficult it is to maintain.

Resources