I am trying to draw an NSAttributedString (actually, a constructed NSMutableAttributedString) where the "original" text has been struck and replacement text inserted above it (I'm trying to replicate the look/feel of an Ancient Greek manuscript).
My technique is a combination of NSBaselineOffsetAttributeName with NSKernAttributeName, but it appears that using a negative value for NSKernAttributeName "wipes away" the strikethrough of the text, even if the characters don't overlap.
If I put an extra space after the "A" character (in the original text), the "A" gets the strikethrough, but the "EI" is also offset to the right. So, it appears that the offset/kerning of the "EI" text affects how much of the strikethrough actually occurs.
Here's what I'd like to reproduce (I don't care about the angle; it's not about a picture-perfect reproduction; just the gist):
Here's what is currently happening:
This is when I add an extra space after the strikethrough:
So, the only other thing I can think of would be to render a separate NSAttributedString in the correct place, separate from the current one, but I have no idea how to calculate the location of a specific character in an NSAttributedString when it's drawn. I'm drawing to a PDF, not to any on-screen control like a UILabel. Alternatively, I could draw the "strikethrough" myself as a line, but that seems to still require knowing the coordinates for the text in question, which is calculated on-the-fly, and I hope to use this method to reproduce a large sample of ancient texts, which means doing it by hand just isn't a good answer here.
Anything I'm missing, or any out-of-the-box ideas to try?
Related
I have a font where unfortunately the numbers and letters are different heights. I need to display a reference code which is a mix of letters and numbers and the uneven heights of the characters looks jarring. Is it possible with core text (or another technology on iOS) to render certain characters with a slightly stretched height so that it looks even numbers and letters are displayed together.
E.g i have the string '23Rt59RQ' I need the 2,3,5,9 to be rendered with a larger height.
AFAICT, there's nothing in the CGContext API (which is what you'd want to use for laying out sets of glyphs) which would directly, easily facilitate this.
If it's really very important to use the font you are using, you could make separate calls to CGContextShowGlyphsAtPositions for alphabetic and numeral characters, calling CGContextSetFontSize each time so that the end result ends up matching, but this is a lot of overhead for just drawing text, and will probably result in undesirable performance.
My real advice would be to pick a better font so that this isn't even an issue :)
In the end of used regex to identify the character groups and then created an attributed string varying the font size in the font given in the NSFontAttributeName attribute according to which characters were to be displayed.
Kinda hacky but it had the desired effect.
I would like to implement karaoke-like progress highlight for iOS.
I know I could use NSAttributedString and highlight the text character by character. However, I would like the highlight to progress pixel by pixel, not character by character.
Any ideas?
P.S. No need for sample code, just point me to the right direction.
Here is an example:
I can't think of any automatic way to do that. There would be several problems to solve. It would be pretty hard, I think.
The hardest would probably be figuring out the pixel position of each word so you can pace the coloring to match the timing within the music. With text and attributed layout, you could probably get the text engine to give you the boundaries of each word and then apply the color attribute to each word as it's spoken/sung. You'd have to have data about the time offset for the beginning and end of each word's being sung.
You might have to use Core Text to get layout information about the bounding rectangles of each word.
Once you get that you could build a path (UIBezierPath or CGPath; they're pretty interchangeable) that follows the flow of the text, and then install that path in to a shape layer. You could then make the text transparent, make the shape layer a colored background that shows through, and animate the shape layer's strokeStart and/or strokeEnd properties to make it fill the text. You might need to do it word by word with a short animation that interpolates between one word and the next to get the timing right.
You probably want to have a look at Core Text, which is the lower level framework used for laying out text, using this you can obtain necessary paths that you need to render said effect (I suggest starting from answers similar to this)
There are plenty of answers for alternative, perhaps simpler answers, for example character by character or word-by-word, which may be easier to implement.
Question
Is there a definitive way to detect if the text direction in NSString or NSAttributedString is right-to-left? Text is received from external resource, not entered in the app (can't use UITextInput protocol methods).
Bad solution
I have been detecting it using CFStringTokenizerCopyBestStringLanguage() and then +[NSLocale characterDirectionForLanguage:], but it is pretty unreliable for strings, where there are only a few arabic characters and many latin. They are processes as a RTL when displayed in the label, but incorrect direction detection makes inconsistent behaviour.
Investigation
During development, I have found out that when creating NSAttributedString with no attributes except for the font and displaying it using TTTAttributedLabel, RTL text aligns to the right edge. When using simple NSString, or when using standard UILabel with attributed string, alignment stays left.
So, there has to be something in the text that says "it is RTL" and there is some method to detect it.
I analysed what I am receiving from server, and there were no standard Unicode bidi chars. This is how it looks. JSON is sent in ascii with all unicode characters escaped:
"\u0643\u062a\u0628\u062a \u0648\u0648\u062c\u0650 .."
When unescaping, it looks like this, with \u0643 is on the far right before the dots:
"كتبت ووجِ .."
As far as I can tell, every character represents a single character in arabic language, and there are no U+202B or similar control characters which could be used to easily detect direction.
Yet, when rendering this text through TTTAttributedLabel, it aligns right. I started looking into the source code, and it doesn't do anything to detect the direction or set the alignment. It only created the framesetter using CTFramesetterCreateWithAttributedString() and then, when it gets lines from it using CTFrameGetLines() and line positions with CTFrameGetLineOrigins(), they are already aligned to the right.
So, does anyone know if direction can be detected solely from the text with publicly available (and preferably fast) API methods?
Check This link that helps to you Automatically align text in UILabel according to text language
Change this for right and left alignment
if (isCodePointStrongRTL(utf32chars[i]))
return 2;
if (isCodePointStrongLTR(utf32chars[i]))
return 0;
saw this comment on other question, and thought it's important for others to check this out. this was exactly what I was looking for RTL-LTR :
"
but I've been using a solution to explicitly set the direction based on known RTL languages, which used this as a starting point:
https://stackoverflow.com/a/16309559
"
I have an NSLayoutManager which is drawing text using the following code:
[[self textLayoutManager] drawGlyphsForGlyphRange: NSMakeRange(0, [[self text] length])
atPoint: textFrame.origin];
in my view's -drawRect:. This works wonderfully, but what I'd really like to be able to do is animate the text in, character by character, as if it were being typed.
I've tried to append characters to a "visible string" variable, then call -[self setNeedsDisplay], but when dealing with text over approximately 20 characters, it begins to lag, as it redraws all of the text every time.
Regression: How can I animate NSLayoutManager's -drawGlyphsForGlyphRange:atPoint:?
(Disclaimer: I don't have a terrible lot of experience with the new APIs, so this is mostly coming from previous experience with text rendering.)
Your major slowdown is going to come from full-on changing the text the layout manager is working with. Even if you're just appending text, replacing the text it's using is going to cause it to throw out all its layout calculations - spacing, needed glyphs, actually reading those glyphs into RAM, applying attributes, etc. - and start over, which gets very expensive very quickly. In terms of actual NSLayoutManager, this is "invalidating the layout".
I see a couple of potential solutions off the top of my head. You could subclass NSLayoutManager ("You can create a subclass of NSLayoutManager to handle additional text attributes, whether inherent or not.") and override the showCGGlyphs:positions:count:font:matrix:attributes:inContext: to progressively ignore certain glyphs (thereby leaving the original text it's using intact). Another approach would be to emulate exactly what happens when you input text in a native container - use a mutable text storage, and append the desired text character-by-character, so that text calculation is done iteratively.
If those alone still don't have great performance, consider using those techniques in tandem with some of the native text views; although this is less true now than in past SDKs, a non-editable UITextView or a UILabel (for mutable and immutable text, respectively) contain far more optimizations than our mere mortal minds could comprehend.
How can I fully justify a block of text (like MS Word does, not only on the right and not only on the left but on both sides)?
I want to justify some texts (mainly arabic text) adjusted to certain screen size (some handheld device screen actually, and its text viewer doesn't have this function) and save this text as justified. So I can reload and reuse it again elsewhere.
(The problem with MS word is, that if you copy the justified text from MS Word and paste it to another editor it'll copy it un-justified).
Update : for now I'm thinking of doing it like this:
get-a-word
get-word-width
add-word-to-total-Word and add-Word-width-to-total-word-width
check if total-Word-width = myscreen-width then continue
else if total-Word-width is between myscree-wdith and (myscreen-width -3) then
add-spaces-To-total-word until it = myscreen-width
This is what I'm thinking now, but I put this question up and hope to see if there is a better solution, or somebody else already implemented it.
PS: I hope I have made my question clear and I'm sorry for bad expression if there is.
edit1 : changed the title to make it more clear.
If you want to justify plain text, you can only add extra spaces to the lines to get them align on the left and right. Unfortunately the character widths differ in fonts; so doing it this way will only work for a certain font, unless you limit yourself to monospaced fonts where all characters have the same size.
If you want a result like in Word, adding spaces won't cut it. Word will not add spaces, but stretch and shrink the existing spaces. This information is lost when you copy and paste it into another app.
Either way, justifying is an optimization problem. If you are interested in a good solution and its implementation: have a look a TeX. For an implementation that works on plain text with monospaced fonts have a look at par
There are some API calls that may help:
ExtTextOut and GetCharacterPlacement
Look at the GCP_JUSTIFY flag for GetCharacterPlacement
ExtTextOut is used by Canvas.TextRect
The problem you are going to face is always going to be differences in the rendering of the font. Word handles full justification by adjusting kerning as well as adjusting the number of pixels between words by a few (either way). The end result is lined up both margins. This pixel adjustment is done BOTH ways, and as evenly as possible.
To properly handle this in your portable device you will have to also perform the same algorithm for the display of the text there.
If this is not possible, then the ONLY way you can even get somewhat close would be to add whitespace between words.
As has been pointed out in other answers Word does full justification by stretching the existing spaces often by very small amounts. This is only possible if you have full control over how your text is drawn on the screen (which word - or any other windows program has).
You only real option in this regard would be to implement your own text viewer on the platform you are targeting. Eg you would need to draw the text on the screen yourself (any platform that allows games should allow you to draw on the screen). However this seems like an awful lot of trouble to get justified text.
Sorry couldn't be of more help.