I'm drawing some text in Mac/iOS cross-platform code using CoreText. I may be using fonts that do not have a real "Italic" version installed in the OS for all users, but they need to be aware that the text is italic even then.
With AppKit's NSAttributedString -drawAtPoint:, I can use NSObliquenessAttributeName to make the text slanted (and thus look italic -- well, oblique). CoreText doesn't seem to have an equivalent for this attribute. At least I found none in CTStringAttributes.h (not that there's any documentation even years after CoreText was released).
Does anyone know how I can get oblique text with CoreText on iOS?
I’d try using the affine transform argument to CTFontCreateWithName() with a shear matrix. For instance
CGAffineTransform matrix = { 1, 0, 0.5, 1, 0, 0 };
CTFontRef myFont = CTFontCreateWithName(CFSTR("Helvetica"), 48, &matrix);
That will create quite an extreme skew (assuming I got it right), but you get the idea.
Update:
In fact, the documentation appears to imply that this is the right way to do things.
Displaying a font that has no italic trait as italic is generally a bad idea. However, I can understand that there are some cases where this has to be enforced anyways.
The only solution that comes to my mind right now is to create a custom font with a sheared font matrix:
CGAffineTransform matrix = CGAffineTransformMake(1, tan(degreesToRadians(0)), tan(degreesToRadians(20)), 1, 0, 0);
CTFontRef myfont = CTFontCreateWithName(CFSTR("Helvetica"), 48, &matrix);
You'll have to play with the matrix and see what brings the best results. (Please not that this is a fake code mix out of my head and the internet.)
Haven't tried, but according to iOS Programming Pushing The Limits, passing kCTFontItalicTrait to CTFontCreateCopyWithSymbolicTraits will choose true italic if available, and oblique otherwise. There's also kCTFontSlantTrait for manual decimal slant up to 30 degrees.
Related
I'm a little stuck on how to apply a transformation to every glyph of a particular font using fonttools. I'm playing with an Open Source font where every character is just a taaaad too high up in its bounding box, so I was hoping to transform it down.
It is a variable font (Source Sans). This is the salient code sample:
from fontTools import ttLib
from fontTools.pens.transformPen import TransformPen
from fontTools.pens.ttGlyphPen import TTGlyphPen
font = ttLib.TTFont(font_path)
for glyphName in font["glyf"].keys():
print(glyphName)
glyph = font.getGlyphSet().get(glyphName)
glyphPen = TTGlyphPen(font.getGlyphSet())
transformPen = TransformPen(glyphPen, (1.0, 0.0, 0.0, 1.0, 0, -300))
glyph.draw(transformPen)
transformedGlyph = glyphPen.glyph()
font["glyf"][glyphName] = transformedGlyph
However, when I do this, the transformation doesn't quite go as expected... some characters are shifted down a lot, and other are shifted down only a little. i, 3, 4, 5, and 6 are some noteworthy examples, whereas other letters and numbers are unaffected.
Is there an easier way to do this in general, and if not, why are some letters getting transformed differently than others?
I found out what was happening.
Some glyphs are made up of other, nested glyphs. These are called composite glyphs, as opposed to simple glyphs.
Since I was transforming all glyphs, I was transforming all composite glyphs, as well as the simple glyphs that they were composed from. This led to composite glyphs getting the appearance of being transformed twice.
The new code looks like this:
glyphPen = TTGlyphPen(font.getGlyphSet())
transformPen = TransformPen(glyphPen, Transform().translate(0, -25))
for glyphName in font.getGlyphOrder():
glyph = font['glyf'][glyphName]
# Avoid double-transforming composite glyphs
if glyph.isComposite():
continue
glyph.draw(transformPen, font['glyf'])
font['glyf'][glyphName] = glyphPen.glyph()
First of all, there are many questions on StackOverflow, but none that fully answer this question.
The problem is mainly, but most likely not limited to, Thai and Arabic diacritics when rendered with a custom Latin-only font, using the text property of a UILabel. Which is also intrinsically sized in an auto-layout. I've already done everything Apple suggests, playing with the settings mentioned in their documentation, WWDC videos, as well as questions on StackOverflow (e.g. clipsToBounds = NO, etc.). Keep in mind, only my custom font setup clips in my scenario, not the iOS system font (.SF-UIDisplay), and not even the iOS system provided Helvetica or Helvetic Neue. The custom font has been checked and rechecked, and at this point the conclusion, iOS is the anomaly across all platforms, even macOS. To be even clearer, the same clipping behavior as the custom font can be seen with SF Pro, a font provided by Apple themselves here: https://developer.apple.com/fonts/
This question is about the most proper, least intrusive, and most complete way to do what is necessary to not clip diacritics. Meaning, how would you do this, ideally, from scratch.
All of my font research and test runs have led all those involved in this problem to believe that Apple has implemented special treatment specifically for their system fonts in UILabel, to avoid diacritic clipping. So making that an assumption, I'm also assuming the font is ok, and I'm looking for solutions that do not involve editing the font.
In my tries to use the font, the first thing to go wrong was vertical clipping of the ascender diacritics of Thai glyphs:
นื้ทั้มูHello
This means the glyphs of the font Thonburi when they cascade from the custom Latin-only font. The fix from this point, was to use a custom font only for Thai without any Latin characters, so it could be defined as the primary font, and cascade to the previously mentioned Latin-only custom font. After all this, the custom Thai font still has horizontal clipping issues on diacritics that come at the end of the text:
Worldฟล์
So now I am at a loss for anything further that font management puppetry can do (though still open to suggestions), and I am moving on to more code-centric fixes. I've seen quite a few questions and answers mentioning subclassing UILabel, but I'd like to know what this would look like that could accomplish what I've described.
I'd also like to know if just opting out of UILabel would be an option for anyone. Meaning would writing something from the ground up with TextKit be worth it to avoid all these bugs that seem to only plague iOS, and specifically UILabel.
At first I thought this was a problem with the framework but it's not, it's just a strict enforcement of a font's metrics. And in probably everything but web/app development, fonts are not rendered so strictly, which is why this problem rarely comes up. Fonts have a number of metrics that tell the program rendering it onto the screen how to render it, most importantly how to handle padding. And UILabel (and UITextField, and likely others) applies these metrics strictly. And the problem for us is that some fonts are very decorative and are often too thick or oblique to fit perfectly into the square canvas that each character must fit into (this is especially the case with accents, like umlauts). This isn't a problem outside of web/app development because when a character doesn't fit into its canvas, like a very thick, wide, and oblique W, the program just shows it anyway, and that's why a low-hanging g might spill into the line below it. But if that g was rendered in a single-line UILabel, because of how strict the font-metric enforcement is in iOS, that low-handing g is clipped.
Subclassing UILabel (in the case of UILabel) and overriding its intrinsicContentSize to add some extra padding is not a good idea, on further research. For one, it's kind of hacky, but more importantly, it produces constraint warnings in the debugger. The true fix, and the only acceptable fix AFAIK, is to edit the font's metrics.
Download a program like Glyphs (https://glyphsapp.com/), open the font, open the Font's Info, and in the Masters tab, give the font the proper ascender and descender values. To best understand how these values work, open the San Francisco font in the program and see how Apple did it (it's the font they made specifically for macOS and iOS development). As a side note, if you use this app, when you're editing the font's info, go into the Features tab as well, delete all of the features (using the minus icon in the lower left) and hit Update to let the program manage the font's features for you.
The last hurdle is clipping at the leading edge (not the top and bottom) which the ascender and descender metrics don't address. You can use the Glyphs program to edit the canvas size of individual characters to make sure they all fit but that changes the complexion of the font because it changes the character spacing too noticeably. For this problem, I think the best solution is to simply use attributed strings for your labels and text fields. And that's because attributed strings let you safely edit padding without hacking into intrinsic sizes. An example:
someLabel.attributedText = NSAttributedString(string: "Done", attributes: [NSAttributedString.Key.font: UIFont.blackItalic(size: 26), NSAttributedString.Key.foregroundColor: UIColor.black, NSAttributedString.Key.paragraphStyle: NSMutableParagraphStyle.kItalicCenter])
For convenience, I extended NSMutableParagraphStyle since I use this font all over:
extension NSMutableParagraphStyle {
static var kItalicCenter: NSMutableParagraphStyle {
let s = NSMutableParagraphStyle()
s.alignment = .center
s.firstLineHeadIndent = 2
s.headIndent = 2
return s
}
}
This label will push the font forward a couple of points to prevent clipping.
I was trying to solve similar problem with diacritics in Arabic and found workaround:
I have a UITableViewCell with UILabel with arabic text, it's diacritics were cut sometimes
I overrided - (void)drawRect:(CGRect)frame to directly draw NSAttributedString on UITableViewCell
Also I decreased alpha self.arabicLabel.alpha = 0.1; to draw manually on top of label position, I still keep it to calculate cell's height
- (void)drawRect:(CGRect)frame {
[super drawRect:frame];
if (self.viewModel == nil) return;
NSAttributedString *string = [self.viewModel arabicStringWithTajweed];
CGRect originalRect = [self convertRect:self.arabicLabel.frame fromView:self.arabicLabel];
[string drawInRect:originalRect];
}
The core problem on iOS is font substitution. You are specifying a latin font, the font does not contain glyphs for the characters that will be rendered, the system uses a different font to draw the glyphs, but it is still measuring based on the original font.
Option 1, the most robust option, is to manually choose fonts that include glyphs for the characters you will render. When the font assigned to UILabel, or the attributed string it is rendering, contains all the glyphs that will be rendered, and that font has good metrics as most system fonts do, then nothing will be clipped.
Option 2, manually measure the string using glyph bounds. Make a subclass of UILabel and override textRectForBounds and possibly drawText. Measure the string with .usesDeviceMetrics. This is slower that measuring by font metrics and produces different results. For example, the strings "a" and "A" will measure differently.
Option 3, use baseline offset and line height multiple to make room for the diacritics that are being clipped. Choose or compute constant values for each font for each language, and apply those to the attributed string of the UILabel. This can compensate for the different in font metrics between the font you chose and the font that is actually rendering glyphs. We had localized strings with the worst case clipped characters for each language, and used those to compute the offset and height. Different fonts have different worst case clipping characters.
I want to draw glyph like graphical elements (using a CGContextRef) that match the stroke width of a UIFont instance so that they combine with it harmoniously, even as the font size is varied.
So, given a UIFont, or a UIFontDescriptor, is there a reasonably robust mechanism under iOS to determine the font's stroke width?
Specifically, I only need to do this for Helvetica Neue. However, it would be nice, interesting and jolly satisfying, to have a general solution.
With numerous fonts, the stroke width varies even within individual glyphs (Zapfino, for example). In such cases, asking for a stroke width wouldn't make a lot of sense. A null result from a query would be entirely reasonable response.
In fact, I think even with Helvetica Neue, the stroke width may not be uniform within glyphs. Some robust and reasonable approximation would be great, though. The width used by the lowercase l, say. Or something.
I develop my app with AIR, Starling and FeatherUI for iOS.
I use Label with TextBlockTextRenderer (flash.text.engine.TextBlock).
I faced the following problem: http://monosnap.com/image/1chKMEoG2fDufCMJIdrgcX3dcTbLMa
In short: Some parts of letters are being cut. (this issue affect languages that has high glyphs, like Norway, German, Arabic etc...)
I already did ask the question about possible fix for this issue: http://forum.starling-framework.org/topic/why-does-label-cuts-letters but suggested workarounds are only good for certain cases. They do not solve the whole problem.
What I know so far:
baselineZero property doesn't work for me. See description here: http://forum.starling-framework.org/topic/why-does-label-cuts-letters#post-61471
Settings baselineFontDescription works but cannot be used as proper workaround - you have to manually measure baselineFontSize all the time. See description here: http://forum.starling-framework.org/topic/why-does-label-cuts-letters#post-61471
Any ideas how to fix this issue?
The workaround i am currently using is to add transparent shadow filter.
You may ned to increase radius depending on your font.
This is by no means a good solution, just a dirty temporary fix.
public static function defaultTextRendererFactory():ITextRenderer
{
// Transparent shadow helps to avoid clipping on some fonts
var clipFix:DropShadowFilter = new DropShadowFilter(0.0, 0.0, 0x000000, 0.0, 2.0, 2.0, 0.0);
var textRenderer:TextBlockTextRenderer = new TextBlockTextRenderer();
textRenderer.nativeFilters = [clipFix];
return textRenderer;
}
Im just a new user of Corona SDK and Im following some exercises of a book. I tried to create a rectangle and color it, but if i put setFillColor(255,0,0) or put 255 in green or blue it works. The problem is when i try to mix colors like setFillColor(100,129,93) it just paints a white rectangle.
This is my main.lua:
rect_upperBackground = display.newRect(150, 150, 100, 50)
rect_upperBackground:setFillColor(49, 49, 49)
According to the documentation, setFillColor requires colors in the range of [0, 1] as opposed to [0, 255]. So for example, you might try this instead.
rect_upperBackground:setFillColor(100 / 255, 129 / 255, 93 / 255)
rect_upperBackground:setFillColor(0.4, 0.2, 0.5)
object:setFillColor() used to use values 0-255 but in the latest release of the SDK they changed that to 0-1 so they could handle larger color values. (Because 0-1 is a larger range than 0-255, you know.)
That means all books, video tutorials, etc., created before mid-November are wrong.
You'll also need to watch for object:setReferencePoint() because that has been deprecated. You'll now need to use object.anchorX and object.anchorY (defaults to the center of the object so if that's what you want, no tweaking needed).
Here's an article someone wrote explaining three big changes you'll need to watch out for:
http://www.develephant.net/3-things-you-need-to-know-about-corona-sdk-graphics-2-0/
Those changes are as of build 2013.2076 of Corona SDK.