I'm converting some RTF text into an NSAttributedString using this code (Xamarin C#, but the same logic applies to Swift/Obj-C)
NSData data = NSData.FromString(RTFString);
NSDictionary opt = NSDictionary.FromObjectAndKey(NSObject.FromObject("NSDocumentTypeDocumentAttribute"), NSObject.FromObject(NSDocumentType.RTF));
NSDictionary docatt = null;
NSError error = null;
NSAttributedString sRTF = new NSAttributedString(data, opt, out docatt, ref error);
this works great, but when converting the RTF text it seems to set a default right margin value, so that the text is not flowing across the full width of the UILabel. There is no margin value set in the original RTF text.
How can I change or set the right-margin value of the resulting attributed text so that it flows across the entire width of my label?
Sample RTF
sample.rtf
{\\rtf1\\ansi\\ansicpg1252\\uc1\\htmautsp\\deff2{\\fonttbl{\\f0\\fcharset0 Times New Roman;}{\\f2\\fcharset0 Segoe UI;}}{\\colortbl\\red0\\green0\\blue0;\\red255\\green255\\blue255;\\red255\\green165\\blue0;}\\loch\\hich\\dbch\\pard\\plain\\ltrpar\\itap0{\\lang1033\\fs26\\f2\\cf0 \\cf0\\ql{\\f2 {\\b\\fs30\\cf2\\ltrch 1. Gum/Lip Irritation: }{\\fs24\\ltrch Whitening gel that comes into contact with gum tissue or the lips can become inflammed or bleached. The inflammation or whitening effect is temporary. You may feel a stinging and/or tingling sensation on these soft tissues during the treatment.2. Tooth Sensitivity: You may experience some tooth sensitivity during the first 24 hours after your treatment. Underlying tooth/gum disease such as existing sensitivity, cracked teeth, open cavities, leaking fillings, exposed roots or other dental conditions may worsen or prolong tooth sensitivity after treatment.3. Spots or Streaks: There is a potential to develop white spots or streaks on your teeth due to calcium deposits that naturally occur in teeth. These spots are not caused by Beaming White gel. The gel just brings these calcium deposits out and makes them more visible. This effect will fade over time.4. Relapse: After your treatment, it is natural for teeth color to regress over time. This relapse can be slowed down by avoiding staining agents such as coffee, tea, tobacco, red wine, colas etc. It is very important to avoid these staining agents, especially during the first 24 hours after treatment. I understand that the results of my treatment are not permanent, that up to 3 applications may be needed for desired effect and maintanence treatment every recommended very 4-6 months.}\\li0\\ri0\\sa0\\sb0\\fi0\\ql\\par}\r\n}\r\n}
Insert an extreme right margin (\margrXXXX), or one of just 0 should also work but does not always(?), to overide the default margin of your rtf string to allow the full width of your UILabel.Frame to be used for the paragraph reflow point.
Before:
After:
{\rtf1\ansi\ansicpg1252\uc1\htmautsp\margr20000\deff2{\fo...
Related
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?
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.
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.
In this sample code, the author does the following
\draw ($(closedStart.south) + (-.5em,0)$)
edge[stateEdge] node[edgeLabel, xshift=-3em]{\emph{Passive open}}
($(listen.north) + (-.5em,0)$);
What irritates me most about these markup based drawing tools is that I've to measure a value and specify. In this case, the author specifies .5em
I've yet not understood how to figure that out? How much is .5em? I don't even know the size of the base object, so how can I be sure that if I give a value it will be valid?
Is there any approach to do this? How should I decide the value and also the unit? Is em always the best unit to use?
Google is your friend: http://en.wikipedia.org/wiki/Em_%28typography%29
An em is a unit of measurement in the field of typography, equal to the point size of the current font. This unit is not defined in terms of any specific typeface, and thus is the same for all fonts at a given point size. So, 1 em in a 16 point typeface is 16 points.
You can change the unit of measurement to anything supported by latex, i'm sure: in, mm, cm, pts, picas, etc etc.
Not too sure how TikZ handles it but in LaTeX you can specify measurements as fractions (larger or smaller than 1) of known lengths, so you could set a length to be, say, 0.5\textwidth. My bet is that TikZ has this sort of facility so if you are going to be a long term user you'll want to figure it out.
To expand on what Mica says:
ems are the usual way that intercharacter space is measured, and is historically was the width of a character "M" in a given font ("M" normally being the widest letter in Latin-based fonts): crucially, it is a relative measure, and subscript fonts, say, have a smaller em than normal text. Modern fonts generally have narrower "M" characters than historically, and there are many non-Latin fonts, so the em measure is now derived from the dimensions of the square (or oblong) that the font designer places the character in, and is communicated as a parameter, together with such facts as the height of the baseline that text sits on, that tells us the scale of the font.
The point size of a font is the number of points (usually 1/72 inch) to an em, so a 12 point font is one whose character "M" is 1/6th of an inch wide (i.e., 12/72 in). The subscripts of a 12 point font are usually shown in a 7 point font, for which the em is just under 1/10th inch.
If you want to do positioning on a page, use points. If you want to control spacing in text, use ems because they scale.
Postscript
Fixed the discussion of the character `M', thanks to Mica.
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.