How do i convert NSAttributedString into HTML string? - html

As the title tells,now i can simple convert HTML into NSAttributedString with initWithHTML:documentAttributes: , but what i want to do here is reverse.
Is there any 3rd party library to achieve this?
#implementation NSAttributedString(HTML)
-(NSString *)htmlForAttributedString{
NSArray * exclude = [NSArray arrayWithObjects:#"doctype",
#"html",
#"head",
#"body",
#"xml",
nil
];
NSDictionary * htmlAtt = [NSDictionary
dictionaryWithObjectsAndKeys:NSHTMLTextDocumentType,
NSDocumentTypeDocumentAttribute,
exclude,
NSExcludedElementsDocumentAttribute,
nil
];
NSError * error;
NSData * htmlData = [self dataFromRange:NSMakeRange(0, [self length])
documentAttributes:htmlAtt error:&error
];
//NSAttributedString * htmlString = [[NSAttributedString alloc]initWithHTML:htmlData documentAttributes:&htmlAtt];
NSString * htmlString = [[NSString alloc] initWithData:htmlData encoding:NSUTF8StringEncoding];
return htmlString;
}
#end

Use dataFromRange:documentAttributes: with the document type attribute (NSDocumentTypeDocumentAttribute) set to HTML (NSHTMLTextDocumentType):
NSAttributedString *s = ...;
NSDictionary *documentAttributes = #{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType};
NSData *htmlData = [s dataFromRange:NSMakeRange(0, s.length) documentAttributes:documentAttributes error:NULL];
NSString *htmlString = [[NSString alloc] initWithData:htmlData encoding:NSUTF8StringEncoding];

This is a swift 4 conversion of #omz answer, hope is useful to anyone landing here
extension NSAttributedString {
var attributedString2Html: String? {
do {
let htmlData = try self.data(from: NSRange(location: 0, length: self.length), documentAttributes:[.documentType: NSAttributedString.DocumentType.html]);
return String.init(data: htmlData, encoding: String.Encoding.utf8)
} catch {
print("error:", error)
return nil
}
}
}

Related

Assigning HTML text to NSAttributedString throws EXC_BAD_ACESS on global queue

I am assigning HTML text to NSAttributedString. This attributed string is then assigned to one of the UILabel in UitableViewCell in cellForRowAtIndexPath Method. Due to cell reuse initWithData: method is causing sluggishness at UI. Hence, I made the code to execute on global queue. I am doing something like this:
-(void)assignAttrText:(NSDictionary *)dict{
NSDictionary *msgThreadDict = [dict objectForKey:#"messgDict"];
__block DRMessageThreadTableViewCell *cell = [dict objectForKey:#"cell"];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSError *err = nil;
NSAttributedString *attributedString =
[[NSAttributedString alloc]
initWithData: [[msgThreadDict objectForKey:#"text"] dataUsingEncoding:NSUTF8StringEncoding]
options: #{ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType }
documentAttributes: nil
error: &err];
if(err){
NSLog(#"Unable to parse label text: %#", err);
}
else{
cell.messageTextLabel.attributedText = attributedString;
}
});
Any leads as to what is causing the app to crash with EXC_BAD_ACCESS at the method:
[[NSAttributedString alloc]
initWithData: [[msgThreadDict objectForKey:#"text"] dataUsingEncoding:NSUTF8StringEncoding]
options: #{ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType }
documentAttributes: nil
error: &err];
Make a NSData *mydata object from [msgThreadDict objectForKey:#"text"], then use [[NSAttributedString alloc] initWithData: mydata ...
I think your object is not of NSData type, use string then convert to data, hence EXC_BAD_ACCESS .
NSString *ceva = [NSString stringWithFormat:#"%#",[msgThreadDict objectForKey:#"text"]];
NSData *mydata = [ceva dataUsingEncoding:NSUTF8StringEncoding];

How to save bold font using html in objective c?

I've typed some text in uitextview and also I've select some text and make it bold.After that I'm going to save this data into my application.Now,When I'm going to fetch that data then it will not displaying same as I've saved.
Its not save bold font.
Below is the code :
pragma mark - btnActions
-(IBAction)btnActions:(UIButton *)sender
{
[self addOrRemoveFontTraitWithName:#"Bold" andValue:UIFontDescriptorTraitBold];
}
pragma mark - Private method implementation
-(void)addOrRemoveFontTraitWithName:(NSString *)traitName andValue:(uint32_t)traitValue{
NSRange selectedRange = [txtViewNote selectedRange];
NSDictionary *currentAttributesDict = [txtViewNote.textStorage attributesAtIndex:selectedRange.location
effectiveRange:nil];
UIFont *currentFont = [currentAttributesDict objectForKey:NSFontAttributeName];
UIFontDescriptor *fontDescriptor = [currentFont fontDescriptor];
NSString *fontNameAttribute = [[fontDescriptor fontAttributes] objectForKey:UIFontDescriptorNameAttribute];
UIFontDescriptor *changedFontDescriptor;
if ([fontNameAttribute rangeOfString:traitName].location == NSNotFound) {
uint32_t existingTraitsWithNewTrait = [fontDescriptor symbolicTraits] | traitValue;
changedFontDescriptor = [fontDescriptor fontDescriptorWithSymbolicTraits:existingTraitsWithNewTrait];
}
else{
uint32_t existingTraitsWithoutTrait = [fontDescriptor symbolicTraits] & ~traitValue;
changedFontDescriptor = [fontDescriptor fontDescriptorWithSymbolicTraits:existingTraitsWithoutTrait];
}
UIFont *updatedFont = [UIFont fontWithDescriptor:changedFontDescriptor size:0.0];
NSDictionary *dict = #{NSFontAttributeName: updatedFont};
[txtViewNote.textStorage beginEditing];
[txtViewNote.textStorage setAttributes:dict range:selectedRange];
[txtViewNote.textStorage endEditing];
}
pragma mark - btnSave
-(IBAction)btnSave:(id)sender
{
NSURL *documentDirectoryURL = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
NSURL *documentURL = [documentDirectoryURL URLByAppendingPathComponent:#"test.html"];
NSString *htmlCode = txtViewNote.text;
NSError* error;
if (![htmlCode writeToURL:documentURL atomically:YES encoding:NSUTF8StringEncoding error:&error]) {
NSLog(#"Couldn't save file because: %#", error);
}
NSString* fileToUpload = [NSString stringWithContentsOfURL:documentURL encoding:NSUTF8StringEncoding error:&error];
if (!fileToUpload) {
NSLog(#"Couldn't read file because: %#", error);
}
}
can any one html me?
The problem is you are saving the plain text of the textView instead of the attributedText.
NSString *htmlCode = txtViewNote.text;
Instead you should save the attributed text like this:
NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:NSHTMLTextDocumentType,NSDocumentTypeDocumentAttribute, nil];
NSData *htmlData = [[self txtViewNote].attributedText dataFromRange:NSMakeRange(0, [self txtViewNote].attributedText.length) documentAttributes:attributes error:NULL];
NSString *htmlCode = [[NSString alloc]initWithData:htmlData encoding:NSUTF8StringEncoding];
If you want to write the htmlCode to the textView, you should:
NSMutableAttributedString *tmp = [[NSMutableAttributedString alloc] initWithData:htmlData options:#{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: #(NSUTF8StringEncoding)} documentAttributes:nil error:nil];
[txtViewNote setAttributedText:tmp];

Generate HTML file from NSAttributedString

Recently, I come into a problem in my project. I need convert NSAttributedString to HTML file. The solution Click here doesn't work for me because want to output the HTML file only with TagName and idName and ClassName, at the same time, output the CSS style file to control how the HTML file will display.
Here is my sample code, I wish you can get my intend:
- (NSDictionary *)html
{
NSTextStorage *textStorage = [self contents];
NSArray *arr = [textStorage paragraphs];
// Initialize the CSS dictionay
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjectsAndKeys:
nil];
NSEnumerator *paragraphEnumerator;
paragraphEnumerator = [arr objectEnumerator];
NSAttributedString *paragraph;
NSMutableArray *paragrapHTMLStrings = [[NSMutableArray alloc] initWithCapacity:[arr count]];
NSMutableString *cssString = [[NSMutableString alloc] initWithCapacity:0];
[cssString appendString:#"div{"];
[cssString appendString:[NSString stringWithFormat:#"-webkit-column-count:%ld;", self.columnCount]];
[cssString appendString:[NSString stringWithFormat:#"width:%fpx;", self.bounds.size.width]];
[cssString appendString:[NSString stringWithFormat:#"height:%fpx;", self.bounds.size.height]];
[cssString appendString:#"}"];
[dict setObject:cssString forKey:#"css"];
while (paragraph = [paragraphEnumerator nextObject]) {
// initialize
NSUInteger length;
NSRange effectiveRange = NSMakeRange(0, 0);
id attributeValue;
length = [paragraph length];
// get the font attributes
attributeValue = [paragraph attribute:NSFontAttributeName atIndex:NSMaxRange(effectiveRange) effectiveRange:&effectiveRange];
NSLog(#"font is %#", [attributeValue fontName]);
NSLog(#"font-size is %f", [[[attributeValue fontDescriptor] objectForKey:NSFontSizeAttribute] floatValue]);
NSMutableString *htmlString = [NSMutableString stringWithFormat:#"", [attributeValue fontName],
[[[attributeValue fontDescriptor] objectForKey:NSFontSizeAttribute] floatValue]];
[htmlString appendString:[paragraph string]];
[htmlString appendString:#""];
NSLog(#"htmlString is %#", htmlString);
[paragrapHTMLStrings addObject:htmlString];
htmlString = nil;
}
NSMutableString *htmlStringOfGraphToReturn = [NSMutableString stringWithString:#""];
NSString *stringToAdd;
NSEnumerator *stringEnumerator;
stringEnumerator = [paragrapHTMLStrings objectEnumerator];
while (stringToAdd = [stringEnumerator nextObject])
{
[htmlStringOfGraphToReturn appendString:stringToAdd];
}
[htmlStringOfGraphToReturn appendString:#""];
[dict setObject:htmlStringOfGraphToReturn forKey:#"html"];
// test part
CSSSetGenerator *generater = [[CSSSetGenerator alloc] init];
NSMutableString *string = [generater outputCSSStyleContent:self];
NSLog(#"%#", string);
return dict;
}
I got the solution from Github, There's an open project named DTCoreText. I hope this might be useful for someone.

Parsed results in UITableView

I am using HTMLParser by Ben Reeves. It works great but the only problem is that I couldn't put the output in UITableView. Anyone can tell me what's wrong with this code? ...................................................................................
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSError *error = nil;
NSURL *url=[[NSURL alloc] initWithString:#"http://website.com/"];
NSString *strin=[[NSString alloc] initWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil];
HTMLParser *parser = [[HTMLParser alloc] initWithString:strin error:&error];
if (error) {
NSLog(#"Error: %#", error);
return;
}
HTMLNode *bodyNode = [parser body];
NSArray *divNodes = [bodyNode findChildTags:#"div"];
for (HTMLNode *inputNode in divNodes) {
if ([[inputNode getAttributeNamed:#"class"] isEqualToString:#"views-field-title"]) {
NSLog(#"%#", [inputNode allContents]);
listData = [[NSArray alloc] initWithObjects:[inputNode allContents], nil];
}
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.listData count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *SimpleTableIdentifier = #"SimpleTableIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: SimpleTableIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:SimpleTableIdentifier];
}
NSUInteger row = [indexPath row];
cell.textLabel.text = [listData objectAtIndex:row];
return cell;
}
#end
You're reinitializing your array every time you find a new element. I think you need to move
listData = [[NSArray alloc] initWithObjects:[inputNode allContents], nil];
outside of your loop and change it to
listData = [[NSMutableArray alloc] init];
listData should be an NSMutableArray so you can add data to it. You'll need to change this in your variable definition too.
Then inside your loop, use [listData addObject:[inputNode allContents]];

Trouble on parsing JSON and displaying it on UITableView

I have this url: http://maps.google.com.br/maps/api/directions/json?origin=porto+alegre&destination=novo+hamburgo&sensor=false
And this code:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[connection release];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
[responseData release];
NSMutableDictionary *theDictionary = [responseString JSONValue];
NSLog(#"%#", theDictionary);
[responseString release];
if (theDictionary != nil)
{
NSArray *routesArray = [theDictionary objectForKey:#"routes"];
NSDictionary *firstRoute = [routesArray objectAtIndex:0];
NSArray *legsArray = [firstRoute objectForKey:#"legs"];
NSDictionary *firstLeg = [legsArray objectAtIndex:0];
steps = [firstLeg objectForKey:#"steps"];
}
[tableView reloadData];
}
I want to display every single #"html_instructions" on a uitableview.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return [self.steps count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cachedCell"];
if (cell == nil)
cell = [[[UITableViewCell alloc]
initWithFrame:CGRectZero reuseIdentifier:#"cachedCell"] autorelease];
return cell;
}
What's missing in my code??? Should be some little detail... any help would be GREAT!
I would do it like this:
Create url request with your url
NSMutableURLRequest *request = [[NSMutableURLRequest alloc]init];
[request setURL:[NSURL URLWithString:#"http://maps.google.com.br/maps/api/directions/json?origin=porto+alegre&destination=novo+hamburgo&sensor=false"]];
then create NSData structure, that holds response to the request and I converted it to string
NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil]; //Or async request
NSString *returnString = [[NSString alloc] initWithData:returnData encoding:NSUTF8StringEncoding];
After that, you load JSON into Dictionary, then to move one level down, pass it to another dictionary (result 1)
NSError *error=nil;
result = [NSJSONSerialization JSONObjectWithData:returnData options:kNilOptions error:&error];
result1 = [result objectForKey:#"routes"];
result2 = [result1 objectForKey:#"legs"];
Then move it into NSMutableArray
jsonMutArray= [result2 objectForKey:#"steps"];
From there, you just put label into your custom cell attach it as a IBOutlet, synthetize and do in cellForRowAtIndexPath method
cell.yourLabel.text = [jsonMutArray objectForKey:#"html_instructions"];
However, keep in mind, that I'm just begginer, so this might not be the most effective way of doing it :) Good Luck!
Don't you need to be doing something like this in your CellForRowAtIndexPath ?
NSString *cellValue = **WHATEVER VALUE YOU WANT TO PUT IN**;
cell.textLabel.text = cellValue;
return cell;
I don't see you adding your data to the cell.