diff --git a/Hackers.xcworkspace/xcuserdata/weiran.xcuserdatad/UserInterfaceState.xcuserstate b/Hackers.xcworkspace/xcuserdata/weiran.xcuserdatad/UserInterfaceState.xcuserstate index 53d02f44..6d8c2cdf 100644 Binary files a/Hackers.xcworkspace/xcuserdata/weiran.xcuserdatad/UserInterfaceState.xcuserstate and b/Hackers.xcworkspace/xcuserdata/weiran.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Podfile b/Podfile index 2cb6c47c..a4f34158 100644 --- a/Podfile +++ b/Podfile @@ -1,3 +1,4 @@ platform :ios, '6.0' pod 'AFNetworking' -pod 'SWRevealViewController' \ No newline at end of file +pod 'SWRevealViewController' +pod 'DTCoreText', :podspec => 'https://raw.github.com/Cocoanetics/DTCoreText/master/DTCoreText.podspec' \ No newline at end of file diff --git a/Podfile.lock b/Podfile.lock index af0ffe14..facc6f70 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,14 +1,21 @@ +PODS: +- AFNetworking (1.0.1) +- DTCoreText (1.1.0) +- SWRevealViewController (0.0.1) + +EXTERNAL SOURCES: + DTCoreText: + :podspec: https://raw.github.com/Cocoanetics/DTCoreText/master/DTCoreText.podspec + +COCOAPODS: 0.15.2 + SPEC CHECKSUMS: + DTCoreText: 4b2df201ce1ab01f1f8deafaaf6f34b8eef6dd70 AFNetworking: 0bce3ae41023e080e96b8a2e0d07c94709f72a81 SWRevealViewController: b9216cc294de5719201a96274a574e2a282774a0 -COCOAPODS: 0.15.2 - DEPENDENCIES: - AFNetworking +- DTCoreText (from `https://raw.github.com/Cocoanetics/DTCoreText/master/DTCoreText.podspec') - SWRevealViewController - -PODS: -- AFNetworking (1.0.1) -- SWRevealViewController (0.0.1) diff --git a/Pods/BuildHeaders/DTCoreText/CGUtils.h b/Pods/BuildHeaders/DTCoreText/CGUtils.h new file mode 120000 index 00000000..3c86f395 --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/CGUtils.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/CGUtils.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/DTAttributedTextCell.h b/Pods/BuildHeaders/DTCoreText/DTAttributedTextCell.h new file mode 120000 index 00000000..a7b6056b --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/DTAttributedTextCell.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/DTAttributedTextCell.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/DTAttributedTextContentView.h b/Pods/BuildHeaders/DTCoreText/DTAttributedTextContentView.h new file mode 120000 index 00000000..32207557 --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/DTAttributedTextContentView.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/DTAttributedTextContentView.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/DTAttributedTextView.h b/Pods/BuildHeaders/DTCoreText/DTAttributedTextView.h new file mode 120000 index 00000000..37079080 --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/DTAttributedTextView.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/DTAttributedTextView.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/DTCSSListStyle.h b/Pods/BuildHeaders/DTCoreText/DTCSSListStyle.h new file mode 120000 index 00000000..b0f36802 --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/DTCSSListStyle.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/DTCSSListStyle.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/DTCSSStylesheet.h b/Pods/BuildHeaders/DTCoreText/DTCSSStylesheet.h new file mode 120000 index 00000000..ea8830f8 --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/DTCSSStylesheet.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/DTCSSStylesheet.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/DTColor+HTML.h b/Pods/BuildHeaders/DTCoreText/DTColor+HTML.h new file mode 120000 index 00000000..c12f38bc --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/DTColor+HTML.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/DTColor+HTML.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/DTCompatibility.h b/Pods/BuildHeaders/DTCoreText/DTCompatibility.h new file mode 120000 index 00000000..bc77b203 --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/DTCompatibility.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/DTCompatibility.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/DTCoreText.h b/Pods/BuildHeaders/DTCoreText/DTCoreText.h new file mode 120000 index 00000000..8dab4358 --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/DTCoreText.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/DTCoreText.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/DTCoreTextConstants.h b/Pods/BuildHeaders/DTCoreText/DTCoreTextConstants.h new file mode 120000 index 00000000..6014613f --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/DTCoreTextConstants.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/DTCoreTextConstants.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/DTCoreTextFontCollection.h b/Pods/BuildHeaders/DTCoreText/DTCoreTextFontCollection.h new file mode 120000 index 00000000..e074ae4a --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/DTCoreTextFontCollection.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/DTCoreTextFontCollection.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/DTCoreTextFontDescriptor.h b/Pods/BuildHeaders/DTCoreText/DTCoreTextFontDescriptor.h new file mode 120000 index 00000000..6176025d --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/DTCoreTextFontDescriptor.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/DTCoreTextFontDescriptor.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/DTCoreTextFunctions.h b/Pods/BuildHeaders/DTCoreText/DTCoreTextFunctions.h new file mode 120000 index 00000000..566b75b5 --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/DTCoreTextFunctions.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/DTCoreTextFunctions.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/DTCoreTextGlyphRun.h b/Pods/BuildHeaders/DTCoreText/DTCoreTextGlyphRun.h new file mode 120000 index 00000000..f2a5d79f --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/DTCoreTextGlyphRun.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/DTCoreTextGlyphRun.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/DTCoreTextLayoutFrame.h b/Pods/BuildHeaders/DTCoreText/DTCoreTextLayoutFrame.h new file mode 120000 index 00000000..76af805f --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/DTCoreTextLayoutFrame.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/DTCoreTextLayoutFrame.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/DTCoreTextLayoutLine.h b/Pods/BuildHeaders/DTCoreText/DTCoreTextLayoutLine.h new file mode 120000 index 00000000..1edae631 --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/DTCoreTextLayoutLine.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/DTCoreTextLayoutLine.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/DTCoreTextLayouter.h b/Pods/BuildHeaders/DTCoreText/DTCoreTextLayouter.h new file mode 120000 index 00000000..63bccd78 --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/DTCoreTextLayouter.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/DTCoreTextLayouter.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/DTCoreTextParagraphStyle.h b/Pods/BuildHeaders/DTCoreText/DTCoreTextParagraphStyle.h new file mode 120000 index 00000000..599c04e3 --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/DTCoreTextParagraphStyle.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/DTCoreTextParagraphStyle.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/DTHTMLAttributedStringBuilder.h b/Pods/BuildHeaders/DTCoreText/DTHTMLAttributedStringBuilder.h new file mode 120000 index 00000000..ba23fb34 --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/DTHTMLAttributedStringBuilder.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/DTHTMLAttributedStringBuilder.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/DTHTMLElement.h b/Pods/BuildHeaders/DTCoreText/DTHTMLElement.h new file mode 120000 index 00000000..b27d4749 --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/DTHTMLElement.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/DTHTMLElement.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/DTHTMLElementAttachment.h b/Pods/BuildHeaders/DTCoreText/DTHTMLElementAttachment.h new file mode 120000 index 00000000..96a598a3 --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/DTHTMLElementAttachment.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/DTHTMLElementAttachment.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/DTHTMLElementBR.h b/Pods/BuildHeaders/DTCoreText/DTHTMLElementBR.h new file mode 120000 index 00000000..e984d68f --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/DTHTMLElementBR.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/DTHTMLElementBR.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/DTHTMLElementHR.h b/Pods/BuildHeaders/DTCoreText/DTHTMLElementHR.h new file mode 120000 index 00000000..8cccbeee --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/DTHTMLElementHR.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/DTHTMLElementHR.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/DTHTMLElementLI.h b/Pods/BuildHeaders/DTCoreText/DTHTMLElementLI.h new file mode 120000 index 00000000..3e734476 --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/DTHTMLElementLI.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/DTHTMLElementLI.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/DTHTMLElementStylesheet.h b/Pods/BuildHeaders/DTCoreText/DTHTMLElementStylesheet.h new file mode 120000 index 00000000..acfd6981 --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/DTHTMLElementStylesheet.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/DTHTMLElementStylesheet.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/DTHTMLElementText.h b/Pods/BuildHeaders/DTCoreText/DTHTMLElementText.h new file mode 120000 index 00000000..381956fc --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/DTHTMLElementText.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/DTHTMLElementText.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/DTHTMLParserNode.h b/Pods/BuildHeaders/DTCoreText/DTHTMLParserNode.h new file mode 120000 index 00000000..d174e89d --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/DTHTMLParserNode.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/DTHTMLParserNode.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/DTHTMLParserTextNode.h b/Pods/BuildHeaders/DTCoreText/DTHTMLParserTextNode.h new file mode 120000 index 00000000..be986374 --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/DTHTMLParserTextNode.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/DTHTMLParserTextNode.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/DTHTMLWriter.h b/Pods/BuildHeaders/DTCoreText/DTHTMLWriter.h new file mode 120000 index 00000000..cc18ff76 --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/DTHTMLWriter.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/DTHTMLWriter.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/DTImage+HTML.h b/Pods/BuildHeaders/DTCoreText/DTImage+HTML.h new file mode 120000 index 00000000..0dde477c --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/DTImage+HTML.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/DTImage+HTML.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/DTLazyImageView.h b/Pods/BuildHeaders/DTCoreText/DTLazyImageView.h new file mode 120000 index 00000000..c712524d --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/DTLazyImageView.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/DTLazyImageView.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/DTLinkButton.h b/Pods/BuildHeaders/DTCoreText/DTLinkButton.h new file mode 120000 index 00000000..f31b34e1 --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/DTLinkButton.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/DTLinkButton.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/DTTextAttachment.h b/Pods/BuildHeaders/DTCoreText/DTTextAttachment.h new file mode 120000 index 00000000..1b65e9d5 --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/DTTextAttachment.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/DTTextAttachment.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/DTTextBlock.h b/Pods/BuildHeaders/DTCoreText/DTTextBlock.h new file mode 120000 index 00000000..f9a1fe34 --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/DTTextBlock.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/DTTextBlock.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/DTWebVideoView.h b/Pods/BuildHeaders/DTCoreText/DTWebVideoView.h new file mode 120000 index 00000000..17e214dc --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/DTWebVideoView.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/DTWebVideoView.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/NSAttributedString+DTCoreText.h b/Pods/BuildHeaders/DTCoreText/NSAttributedString+DTCoreText.h new file mode 120000 index 00000000..97852c5f --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/NSAttributedString+DTCoreText.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/NSAttributedString+DTCoreText.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/NSAttributedString+HTML.h b/Pods/BuildHeaders/DTCoreText/NSAttributedString+HTML.h new file mode 120000 index 00000000..6fb7ecf4 --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/NSAttributedString+HTML.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/NSAttributedString+HTML.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/NSAttributedString+SmallCaps.h b/Pods/BuildHeaders/DTCoreText/NSAttributedString+SmallCaps.h new file mode 120000 index 00000000..3bb57d5f --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/NSAttributedString+SmallCaps.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/NSAttributedString+SmallCaps.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/NSAttributedStringRunDelegates.h b/Pods/BuildHeaders/DTCoreText/NSAttributedStringRunDelegates.h new file mode 120000 index 00000000..05643fb3 --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/NSAttributedStringRunDelegates.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/NSAttributedStringRunDelegates.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/NSCharacterSet+HTML.h b/Pods/BuildHeaders/DTCoreText/NSCharacterSet+HTML.h new file mode 120000 index 00000000..d5e8d13c --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/NSCharacterSet+HTML.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/NSCharacterSet+HTML.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/NSMutableAttributedString+HTML.h b/Pods/BuildHeaders/DTCoreText/NSMutableAttributedString+HTML.h new file mode 120000 index 00000000..e7cfdba9 --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/NSMutableAttributedString+HTML.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/NSMutableAttributedString+HTML.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/NSMutableString+HTML.h b/Pods/BuildHeaders/DTCoreText/NSMutableString+HTML.h new file mode 120000 index 00000000..6d4228c6 --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/NSMutableString+HTML.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/NSMutableString+HTML.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/NSScanner+HTML.h b/Pods/BuildHeaders/DTCoreText/NSScanner+HTML.h new file mode 120000 index 00000000..ba9fab3f --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/NSScanner+HTML.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/NSScanner+HTML.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/NSString+CSS.h b/Pods/BuildHeaders/DTCoreText/NSString+CSS.h new file mode 120000 index 00000000..535dd78e --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/NSString+CSS.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/NSString+CSS.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/NSString+HTML.h b/Pods/BuildHeaders/DTCoreText/NSString+HTML.h new file mode 120000 index 00000000..4ee7a7cd --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/NSString+HTML.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/NSString+HTML.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/NSString+Paragraphs.h b/Pods/BuildHeaders/DTCoreText/NSString+Paragraphs.h new file mode 120000 index 00000000..38e318e7 --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/NSString+Paragraphs.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/NSString+Paragraphs.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/NSString+UTF8Cleaner.h b/Pods/BuildHeaders/DTCoreText/NSString+UTF8Cleaner.h new file mode 120000 index 00000000..622fd22c --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/NSString+UTF8Cleaner.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/NSString+UTF8Cleaner.h \ No newline at end of file diff --git a/Pods/BuildHeaders/DTCoreText/UIFont+DTCoreText.h b/Pods/BuildHeaders/DTCoreText/UIFont+DTCoreText.h new file mode 120000 index 00000000..bccce6eb --- /dev/null +++ b/Pods/BuildHeaders/DTCoreText/UIFont+DTCoreText.h @@ -0,0 +1 @@ +../../DTCoreText/Core/Source/UIFont+DTCoreText.h \ No newline at end of file diff --git a/Pods/DTCoreText/Core/Source/CGUtils.h b/Pods/DTCoreText/Core/Source/CGUtils.h new file mode 100755 index 00000000..a059e0b5 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/CGUtils.h @@ -0,0 +1,22 @@ +// +// CGUtils.h +// CoreTextExtensions +// +// Created by Oliver Drobnik on 1/16/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +//see implementation file for note on deprecation +//CGPathRef newPathForRoundedRect(CGRect rect, CGFloat cornerRadius, BOOL roundTopCorners, BOOL roundBottomCorners); + +/** + Determines the new zoom only computing if the sizeToFit is smaller than the originalSize. The zoom scale is computed by whichever resizing scale along the X or Y is smaller preserving the aspect ratio by respecting the axis with more room. The new size is then computed by multipliying the originalSize by that zoom scale. + @returns New size that fits the sizeToFit while still preserving the aspect ratio of the originalSize. + */ +CGSize sizeThatFitsKeepingAspectRatio2(CGSize originalSize, CGSize sizeToFit); + +/** + Convenience method to find the center of a CGRect. Uses CGRectGetMidX and CGRectGetMidY. + @returns The point which is the center of rect. + */ +CGPoint CGRectCenter(CGRect rect); diff --git a/Pods/DTCoreText/Core/Source/CGUtils.m b/Pods/DTCoreText/Core/Source/CGUtils.m new file mode 100644 index 00000000..d7ac290b --- /dev/null +++ b/Pods/DTCoreText/Core/Source/CGUtils.m @@ -0,0 +1,99 @@ +// +// CGUtils.m +// CoreTextExtensions +// +// Created by Oliver Drobnik on 1/16/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import "CGUtils.h" + + +// deprecated: use bezierPathWithRoundedRect instead + +//CGPathRef newPathForRoundedRect(CGRect rect, CGFloat cornerRadius, BOOL roundTopCorners, BOOL roundBottomCorners) +// +//{ +// CGMutablePathRef retPath = CGPathCreateMutable(); +// +// CGRect innerRect = CGRectInset(rect, cornerRadius, cornerRadius); +// +// CGFloat inside_right = innerRect.origin.x + innerRect.size.width; +// CGFloat outside_right = rect.origin.x + rect.size.width; +// CGFloat inside_bottom = innerRect.origin.y + innerRect.size.height; +// CGFloat outside_bottom = rect.origin.y + rect.size.height; +// +// CGFloat inside_top = innerRect.origin.y; +// CGFloat outside_top = rect.origin.y; +// CGFloat outside_left = rect.origin.x; +// +// +// if (roundTopCorners) +// { +// CGPathMoveToPoint(retPath, NULL, innerRect.origin.x, outside_top); +// CGPathAddLineToPoint(retPath, NULL, inside_right, outside_top); +// +// CGPathAddArcToPoint(retPath, NULL, outside_right, outside_top, outside_right, inside_top, cornerRadius); +// } +// else +// { +// CGPathMoveToPoint(retPath, NULL, outside_left, outside_top); +// CGPathAddLineToPoint(retPath, NULL, outside_right, outside_top); +// +// } +// +// if (roundBottomCorners) +// { +// CGPathAddLineToPoint(retPath, NULL, outside_right, inside_bottom); +// CGPathAddArcToPoint(retPath, NULL, outside_right, outside_bottom, inside_right, outside_bottom, cornerRadius); +// +// CGPathAddLineToPoint(retPath, NULL, innerRect.origin.x, outside_bottom); +// CGPathAddArcToPoint(retPath, NULL, outside_left, outside_bottom, outside_left, inside_bottom, cornerRadius); +// } +// else +// { +// CGPathAddLineToPoint(retPath, NULL, outside_right, outside_bottom); +// CGPathAddLineToPoint(retPath, NULL, outside_left, outside_bottom); +// } +// +// +// +// if (roundTopCorners) +// { +// CGPathAddLineToPoint(retPath, NULL, outside_left, inside_top); +// CGPathAddArcToPoint(retPath, NULL, outside_left, outside_top, innerRect.origin.x, outside_top, cornerRadius); +// } +// else +// { +// CGPathAddLineToPoint(retPath, NULL, rect.origin.x, outside_top); +// +// } +// +// +// CGPathCloseSubpath(retPath); +// +// return retPath; +//} + +CGSize sizeThatFitsKeepingAspectRatio2(CGSize originalSize, CGSize sizeToFit) +{ + if (originalSize.width <= sizeToFit.width && originalSize.height <= sizeToFit.height) + { + return originalSize; + } + + CGFloat necessaryZoomWidth = sizeToFit.width / originalSize.width; + CGFloat necessaryZoomHeight = sizeToFit.height / originalSize.height; + + CGFloat smallerZoom = MIN(necessaryZoomWidth, necessaryZoomHeight); + + CGSize scaledSize = CGSizeMake(roundf(originalSize.width*smallerZoom), roundf(originalSize.height*smallerZoom)); + + return scaledSize; +} + + +CGPoint CGRectCenter(CGRect rect) +{ + return (CGPoint){ CGRectGetMidX(rect), CGRectGetMidY(rect) }; +} diff --git a/Pods/DTCoreText/Core/Source/DTAttributedTextCell.h b/Pods/DTCoreText/Core/Source/DTAttributedTextCell.h new file mode 100644 index 00000000..e51b45ba --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTAttributedTextCell.h @@ -0,0 +1,22 @@ +// +// DTAttributedTextCell.h +// CoreTextExtensions +// +// Created by Oliver Drobnik on 8/4/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +@class DTAttributedTextContentView; + +@interface DTAttributedTextCell : UITableViewCell + +@property (nonatomic, strong) NSAttributedString *attributedString; +@property (nonatomic, readonly) DTAttributedTextContentView *attributedTextContextView; + +- (id)initWithReuseIdentifier:(NSString *)reuseIdentifier accessoryType:(UITableViewCellAccessoryType)accessoryType; + +- (void)setHTMLString:(NSString *)html; + +- (CGFloat)requiredRowHeightInTableView:(UITableView *)tableView; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTAttributedTextCell.m b/Pods/DTCoreText/Core/Source/DTAttributedTextCell.m new file mode 100644 index 00000000..63d4ff97 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTAttributedTextCell.m @@ -0,0 +1,136 @@ +// +// DTAttributedTextCell.m +// CoreTextExtensions +// +// Created by Oliver Drobnik on 8/4/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import "DTCoreText.h" +#import "DTAttributedTextCell.h" +#import "DTCSSStylesheet.h" + +@implementation DTAttributedTextCell +{ + NSAttributedString *_attributedString; + DTAttributedTextContentView *_attributedTextContextView; + + NSUInteger _htmlHash; // preserved hash to avoid relayouting for same HTML +} + +- (id)initWithReuseIdentifier:(NSString *)reuseIdentifier accessoryType:(UITableViewCellAccessoryType)accessoryType +{ + self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier]; + if (self) + { + // don't know size jetzt because there's no string in it + _attributedTextContextView = [[DTAttributedTextContentView alloc] initWithFrame:CGRectZero]; + _attributedTextContextView.edgeInsets = UIEdgeInsetsMake(5, 5, 5, 5); + [self.contentView addSubview:_attributedTextContextView]; + } + return self; +} + + +- (void)layoutSubviews +{ + [super layoutSubviews]; + + CGFloat neededContentHeight = [self requiredRowHeightInTableView:(UITableView *)self.superview]; + + // after the first call here the content view size is correct + CGRect frame = CGRectMake(0, 0, self.contentView.bounds.size.width, neededContentHeight); + + // only change frame if width has changed to avoid extra layouting + if (_attributedTextContextView.frame.size.width != frame.size.width) + { + _attributedTextContextView.frame = frame; + } +} + +- (void)willMoveToSuperview:(UIView *)newSuperview +{ + UITableView *tableView = (UITableView *)newSuperview; + + if (tableView.style == UITableViewStyleGrouped) + { + // need no background because otherwise this would overlap the rounded corners + _attributedTextContextView.backgroundColor = [DTColor clearColor]; + } + + [super willMoveToSuperview:newSuperview]; +} + +- (CGFloat)requiredRowHeightInTableView:(UITableView *)tableView +{ + + CGFloat contentWidth = tableView.frame.size.width; + + // reduce width for accessories + switch (self.accessoryType) + { + case UITableViewCellAccessoryDisclosureIndicator: + case UITableViewCellAccessoryCheckmark: + contentWidth -= 20.0f; + break; + case UITableViewCellAccessoryDetailDisclosureButton: + contentWidth -= 33.0f; + break; + case UITableViewCellAccessoryNone: + break; + } + + // reduce width for grouped table views + if (tableView.style == UITableViewStyleGrouped) + { + contentWidth -= 19; + } + + CGSize neededSize = [_attributedTextContextView suggestedFrameSizeToFitEntireStringConstraintedToWidth:contentWidth]; + + // note: non-integer row heights caused trouble < iOS 5.0 + return neededSize.height; +} + +#pragma mark Properties + + +- (void)setSelected:(BOOL)selected animated:(BOOL)animated +{ + [super setSelected:selected animated:animated]; + + // Configure the view for the selected state +} + +- (void)setHTMLString:(NSString *)html +{ + // we don't preserve the html but compare it's hash + NSUInteger newHash = [html hash]; + + if (newHash == _htmlHash) + { + return; + } + + _htmlHash = newHash; + + NSData *data = [html dataUsingEncoding:NSUTF8StringEncoding]; + NSAttributedString *string = [[NSAttributedString alloc] initWithHTMLData:data documentAttributes:NULL]; + self.attributedString = string; +} + +- (void)setAttributedString:(NSAttributedString *)attributedString +{ + if (_attributedString != attributedString) + { + _attributedString = attributedString; + + // passthrough + _attributedTextContextView.attributedString = _attributedString; + } +} + +@synthesize attributedString = _attributedString; +@synthesize attributedTextContextView = _attributedTextContextView; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTAttributedTextContentView.h b/Pods/DTCoreText/Core/Source/DTAttributedTextContentView.h new file mode 100644 index 00000000..59a0c977 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTAttributedTextContentView.h @@ -0,0 +1,147 @@ +// +// TextView.h +// CoreTextExtensions +// +// Created by Oliver Drobnik on 1/9/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import + +@class DTAttributedTextContentView; +@class DTCoreTextLayoutFrame; +@class DTTextBlock; +@class DTCoreTextLayouter; +@class DTTextAttachment; + +/** + Protocol to provide custom views for elements in an DTAttributedTextContentView. Also the delegate gets notified once the text view has been drawn. + */ +@protocol DTAttributedTextContentViewDelegate + +@optional + +/** + @name Notifications + */ + + +/** + Called after a layout frame or a part of it is drawn. + + @param attributedTextContentView The content view that drew a layout frame + @param layoutFrame The layout frame that was drawn for + @param context The graphics context that was drawn into + */ +- (void)attributedTextContentView:(DTAttributedTextContentView *)attributedTextContentView didDrawLayoutFrame:(DTCoreTextLayoutFrame *)layoutFrame inContext:(CGContextRef)context; + + +/** + Called before the text belonging to a text block is drawn. + + This gives the developer an opportunity to draw a custom background below a text block. + + @param attributedTextContentView The content view that drew a layout frame + @param textBlock The text block + @param rect The frame within the content view's coordinate system that will be drawn into + @param context The graphics context that will be drawn into + @param layoutFrame The layout frame that will be drawn for + @param returns `YES` is the standard fill of the text block should be drawn, `NO` if it should not + */ +- (BOOL)attributedTextContentView:(DTAttributedTextContentView *)attributedTextContentView shouldDrawBackgroundForTextBlock:(DTTextBlock *)textBlock frame:(CGRect)frame context:(CGContextRef)context forLayoutFrame:(DTCoreTextLayoutFrame *)layoutFrame; + +/** + @name Providing Custom Views for Content + */ + + +/** + Provide custom view for an attachment, e.g. an imageView for images + + @param attributedTextContentView The content view asking for a custom view + @param attachment The that this view should represent + @param frame The frame that the view should use to fit on top of the space reserved for the attachment + @returns The view that should represent the given attachment + */ +- (UIView *)attributedTextContentView:(DTAttributedTextContentView *)attributedTextContentView viewForAttachment:(DTTextAttachment *)attachment frame:(CGRect)frame; + + +/** + Provide button to be placed over links, the identifier is used to link multiple parts of the same A tag + + @param attributedTextContentView The content view asking for a custom view + @param url The `NSURL` of the hyperlink + @param identifier An identifier that uniquely identifies the hyperlink within the document + @param frame The frame that the view should use to fit on top of the space reserved for the attachment + @returns The view that should represent the given hyperlink + */ +- (UIView *)attributedTextContentView:(DTAttributedTextContentView *)attributedTextContentView viewForLink:(NSURL *)url identifier:(NSString *)identifier frame:(CGRect)frame; + + +/** + Provide generic views for all attachments. + + This is only called if the more specific delegate methods are not implemented. + + @param attributedTextContentView The content view asking for a custom view + @param string The attributed sub-string containing this element + @param frame The frame that the view should use to fit on top of the space reserved for the attachment + @returns The view that should represent the given hyperlink or text attachment + @see attributedTextContentView:viewForAttachment:frame: and attributedTextContentView:viewForAttachment:frame: + */ +- (UIView *)attributedTextContentView:(DTAttributedTextContentView *)attributedTextContentView viewForAttributedString:(NSAttributedString *)string frame:(CGRect)frame; + +@end + + + +@interface DTAttributedTextContentView : UIView +{ + NSAttributedString *_attributedString; + DTCoreTextLayoutFrame *_layoutFrame; + + UIEdgeInsets _edgeInsets; + + NSMutableDictionary *customViewsForAttachmentsIndex; +} + +- (id)initWithAttributedString:(NSAttributedString *)attributedString width:(CGFloat)width; + +- (void)layoutSubviewsInRect:(CGRect)rect; +- (void)relayoutText; +- (void)removeAllCustomViews; +- (void)removeAllCustomViewsForLinks; + +- (CGSize)attributedStringSizeThatFits:(CGFloat)width; +- (CGSize)suggestedFrameSizeToFitEntireStringConstraintedToWidth:(CGFloat)width; // obeys the edge insets + +// properties are overwritten with locking to avoid problem with async drawing +@property (atomic, strong) DTCoreTextLayouter *layouter; +@property (atomic, strong) DTCoreTextLayoutFrame *layoutFrame; + +@property (nonatomic, strong) NSMutableSet *customViews; + +@property (nonatomic, copy) NSAttributedString *attributedString; +@property (nonatomic) UIEdgeInsets edgeInsets; +@property (nonatomic) BOOL drawDebugFrames; +@property (nonatomic) BOOL shouldDrawImages; +@property (nonatomic) BOOL shouldDrawLinks; +@property (nonatomic) BOOL shouldLayoutCustomSubviews; +@property (nonatomic) CGPoint layoutOffset; +@property (nonatomic) CGSize backgroundOffset; + +@property (nonatomic, assign) IBOutlet id delegate; // subtle simulator bug - use assign not __unsafe_unretained + +@property (nonatomic, assign) dispatch_semaphore_t selfLock; + + +@end + + +@interface DTAttributedTextContentView (Tiling) + ++ (void)setLayerClass:(Class)layerClass; ++ (Class)layerClass; + +@end + diff --git a/Pods/DTCoreText/Core/Source/DTAttributedTextContentView.m b/Pods/DTCoreText/Core/Source/DTAttributedTextContentView.m new file mode 100644 index 00000000..b125bc5b --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTAttributedTextContentView.m @@ -0,0 +1,805 @@ +// +// TextView.m +// CoreTextExtensions +// +// Created by Oliver Drobnik on 1/9/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import "DTAttributedTextContentView.h" +#import "DTCoreText.h" +#import + +#if !__has_feature(objc_arc) +#error THIS CODE MUST BE COMPILED WITH ARC ENABLED! +#endif + +// Commented code useful to find deadlocks +#define SYNCHRONIZE_START(lock) /* NSLog(@"LOCK: FUNC=%s Line=%d", __func__, __LINE__), */dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); +#define SYNCHRONIZE_END(lock) dispatch_semaphore_signal(lock) /*, NSLog(@"UN-LOCK")*/; + +@interface DTAttributedTextContentView () +{ + BOOL _drawDebugFrames; + BOOL _shouldDrawImages; + BOOL _shouldDrawLinks; + BOOL _shouldLayoutCustomSubviews; + + NSMutableSet *customViews; + NSMutableDictionary *customViewsForLinksIndex; + + BOOL _isTiling; + + DTCoreTextLayouter *_layouter; + + CGPoint _layoutOffset; + CGSize _backgroundOffset; + + // lookup bitmask what delegate methods are implemented + struct + { + unsigned int delegateSupportsCustomViewsForAttachments:1; + unsigned int delegateSupportsCustomViewsForLinks:1; + unsigned int delegateSupportsGenericCustomViews:1; + unsigned int delegateSupportsNotificationAfterDrawing:1; + unsigned int delegateSupportsNotificationBeforeTextBoxDrawing:1; + } _delegateFlags; + + __unsafe_unretained id _delegate; +} + +@property (nonatomic, strong) NSMutableDictionary *customViewsForLinksIndex; +@property (nonatomic, strong) NSMutableDictionary *customViewsForAttachmentsIndex; + +- (void)removeAllCustomViews; +- (void)removeSubviewsOutsideRect:(CGRect)rect; +- (void)removeAllCustomViewsForLinks; + +@end + +static Class _layerClassToUseForDTAttributedTextContentView = nil; + +@implementation DTAttributedTextContentView (Tiling) + ++ (void)setLayerClass:(Class)layerClass +{ + _layerClassToUseForDTAttributedTextContentView = layerClass; +} + ++ (Class)layerClass +{ + if (_layerClassToUseForDTAttributedTextContentView) + { + return _layerClassToUseForDTAttributedTextContentView; + } + + return [CALayer class]; +} + +@end + + +@implementation DTAttributedTextContentView +@synthesize selfLock; + +- (void)setup +{ + self.contentMode = UIViewContentModeTopLeft; // to avoid bitmap scaling effect on resize + _shouldLayoutCustomSubviews = YES; + + // by default we draw images, if custom views are supported (by setting delegate) this is disabled + // if you still want images to be drawn together with text then set it back to YES after setting delegate + _shouldDrawImages = YES; + + // by default we draw links. If you don't want that because you want to highlight the text in + // DTLinkButton set this property to NO and create a highlighted version of the attributed string + _shouldDrawLinks = YES; + + // possibly already set in NIB + if (!self.backgroundColor) + { + self.backgroundColor = [DTColor whiteColor]; + } + + // set tile size if applicable + CATiledLayer *layer = (id)self.layer; + if ([layer isKindOfClass:[CATiledLayer class]]) + { + // get larger dimension and multiply by scale + UIScreen *mainScreen = [UIScreen mainScreen]; + CGFloat largerDimension = MAX(mainScreen.applicationFrame.size.width, mainScreen.applicationFrame.size.height); + CGFloat scale = mainScreen.scale; + + // this way tiles cover entire screen regardless of orientation or scale + CGSize tileSize = CGSizeMake(largerDimension * scale, largerDimension * scale); + layer.tileSize = tileSize; + + _isTiling = YES; + } + + [self selfLock]; +} + +- (id)initWithFrame:(CGRect)frame +{ + if ((self = [super initWithFrame:frame])) + { + [self setup]; + } + return self; +} + +- (id)initWithAttributedString:(NSAttributedString *)attributedString width:(CGFloat)width +{ + self = [self initWithFrame:CGRectMake(0, 0, width, 0)]; + + if (self) + { + // causes appropriate sizing + self.attributedString = attributedString; + [self sizeToFit]; + } + + return self; +} + +- (void)awakeFromNib +{ + [self setup]; +} + +- (void)dealloc +{ + [self removeAllCustomViews]; + + dispatch_release(selfLock); +} + +- (void)layoutSubviewsInRect:(CGRect)rect +{ + // if we are called for partial (non-infinate) we remove unneeded custom subviews first + if (!CGRectIsInfinite(rect)) + { + [self removeSubviewsOutsideRect:rect]; + } + + [CATransaction begin]; + [CATransaction setDisableActions:YES]; + + DTCoreTextLayoutFrame *theLayoutFrame = self.layoutFrame; + + SYNCHRONIZE_START(selfLock) + { + NSAttributedString *layoutString = [theLayoutFrame attributedStringFragment]; + NSArray *lines; + if (CGRectIsInfinite(rect)) + { + lines = [theLayoutFrame lines]; + } + else + { + lines = [theLayoutFrame linesVisibleInRect:rect]; + } + + // hide all customViews + for (UIView *view in self.customViews) + { + view.hidden = YES; + } + + for (DTCoreTextLayoutLine *oneLine in lines) + { + NSRange lineRange = [oneLine stringRange]; + + NSUInteger skipRunsBeforeLocation = 0; + + for (DTCoreTextGlyphRun *oneRun in oneLine.glyphRuns) + { + // add custom views if necessary + NSRange runRange = [oneRun stringRange]; + CGRect frameForSubview = CGRectZero; + + if (runRange.location>=skipRunsBeforeLocation) + { + // see if it's a link + NSRange effectiveRangeOfLink; + NSRange effectiveRangeOfAttachment; + + // make sure that a link is only as long as the area to the next attachment or the current attachment itself + DTTextAttachment *attachment = [layoutString attribute:NSAttachmentAttributeName atIndex:runRange.location longestEffectiveRange:&effectiveRangeOfAttachment inRange:lineRange]; + + // if there is no attachment then the effectiveRangeOfAttachment contains the range until the next attachment + NSURL *linkURL = [layoutString attribute:DTLinkAttribute atIndex:runRange.location longestEffectiveRange:&effectiveRangeOfLink inRange:effectiveRangeOfAttachment]; + + // avoid chaining together glyph runs for an attachment + if (linkURL && !attachment) + { + // compute bounding frame over potentially multiple (chinese) glyphs + skipRunsBeforeLocation = effectiveRangeOfLink.location+effectiveRangeOfLink.length; + + // make one link view for all glyphruns in this line + frameForSubview = [oneLine frameOfGlyphsWithRange:effectiveRangeOfLink]; + runRange = effectiveRangeOfLink; + } + else + { + // individual glyph run + + if (attachment) + { + // frame might be different due to image vertical alignment + CGFloat ascender = [attachment ascentForLayout]; + CGFloat descender = [attachment descentForLayout]; + + frameForSubview = CGRectMake(oneRun.frame.origin.x, oneLine.baselineOrigin.y - ascender, oneRun.frame.size.width, ascender+descender); + } + else + { + frameForSubview = oneRun.frame; + } + } + + if (CGRectIsEmpty(frameForSubview)) + { + continue; + } + + NSNumber *indexKey = [NSNumber numberWithInteger:runRange.location]; + + // offset layout if necessary + if (!CGPointEqualToPoint(_layoutOffset, CGPointZero)) + { + frameForSubview.origin.x += _layoutOffset.x; + frameForSubview.origin.y += _layoutOffset.y; + } + + // round frame + frameForSubview.origin.x = floorf(frameForSubview.origin.x); + frameForSubview.origin.y = ceilf(frameForSubview.origin.y); + frameForSubview.size.width = roundf(frameForSubview.size.width); + frameForSubview.size.height = roundf(frameForSubview.size.height); + + + if (CGRectGetMinY(frameForSubview)> CGRectGetMaxY(rect) || CGRectGetMaxY(frameForSubview) < CGRectGetMinY(rect)) + { + // is still outside even though the bounds of the line already intersect visible area + continue; + } + + if (_delegateFlags.delegateSupportsCustomViewsForAttachments || _delegateFlags.delegateSupportsGenericCustomViews) + { + if (attachment) + { + indexKey = [NSNumber numberWithInteger:[attachment hash]]; + + UIView *existingAttachmentView = [self.customViewsForAttachmentsIndex objectForKey:indexKey]; + + if (existingAttachmentView) + { + existingAttachmentView.hidden = NO; + existingAttachmentView.frame = frameForSubview; + + existingAttachmentView.alpha = 1; + [existingAttachmentView setNeedsLayout]; + [existingAttachmentView setNeedsDisplay]; + + linkURL = nil; // prevent adding link button on top of image view + } + else + { + UIView *newCustomAttachmentView = nil; + + + if (_delegateFlags.delegateSupportsCustomViewsForAttachments) + { + newCustomAttachmentView = [_delegate attributedTextContentView:self viewForAttachment:attachment frame:frameForSubview]; + } + else if (_delegateFlags.delegateSupportsGenericCustomViews) + { + NSAttributedString *string = [layoutString attributedSubstringFromRange:runRange]; + newCustomAttachmentView = [_delegate attributedTextContentView:self viewForAttributedString:string frame:frameForSubview]; + } + + if (newCustomAttachmentView) + { + // delegate responsible to set frame + if (newCustomAttachmentView) + { + newCustomAttachmentView.tag = [indexKey integerValue]; + [self addSubview:newCustomAttachmentView]; + + [self.customViews addObject:newCustomAttachmentView]; + [self.customViewsForAttachmentsIndex setObject:newCustomAttachmentView forKey:indexKey]; + + linkURL = nil; // prevent adding link button on top of image view + } + } + } + } + } + + + if (linkURL && (_delegateFlags.delegateSupportsCustomViewsForLinks || _delegateFlags.delegateSupportsGenericCustomViews)) + { + UIView *existingLinkView = [self.customViewsForLinksIndex objectForKey:indexKey]; + + if (existingLinkView) + { + existingLinkView.frame = frameForSubview; + existingLinkView.hidden = NO; + } + else + { + UIView *newCustomLinkView = nil; + + if (_delegateFlags.delegateSupportsCustomViewsForLinks) + { + NSDictionary *attributes = [layoutString attributesAtIndex:runRange.location effectiveRange:NULL]; + + NSString *guid = [attributes objectForKey:DTGUIDAttribute]; + newCustomLinkView = [_delegate attributedTextContentView:self viewForLink:linkURL identifier:guid frame:frameForSubview]; + } + else if (_delegateFlags.delegateSupportsGenericCustomViews) + { + NSAttributedString *string = [layoutString attributedSubstringFromRange:runRange]; + newCustomLinkView = [_delegate attributedTextContentView:self viewForAttributedString:string frame:frameForSubview]; + } + + // delegate responsible to set frame + if (newCustomLinkView) + { + newCustomLinkView.tag = runRange.location; + [self addSubview:newCustomLinkView]; + + [self.customViews addObject:newCustomLinkView]; + [self.customViewsForLinksIndex setObject:newCustomLinkView forKey:indexKey]; + } + } + } + } + } + } + + [CATransaction commit]; + } + SYNCHRONIZE_END(selfLock) +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + + if (_shouldLayoutCustomSubviews) + { + [self layoutSubviewsInRect:CGRectInfinite]; + } +} + +- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx +{ + // needs clearing of background + CGRect rect = CGContextGetClipBoundingBox(ctx); + + if (_backgroundOffset.height || _backgroundOffset.width) + { + CGContextSetPatternPhase(ctx, _backgroundOffset); + } + + CGContextSetFillColorWithColor(ctx, [self.backgroundColor CGColor]); + CGContextFillRect(ctx, rect); + + // offset layout if necessary + if (!CGPointEqualToPoint(_layoutOffset, CGPointZero)) + { + CGAffineTransform transform = CGAffineTransformMakeTranslation(_layoutOffset.x, _layoutOffset.y); + CGContextConcatCTM(ctx, transform); + } + + DTCoreTextLayoutFrame *theLayoutFrame = self.layoutFrame; + + // need to prevent updating of string and drawing at the same time + SYNCHRONIZE_START(selfLock) + { + [theLayoutFrame drawInContext:ctx drawImages:_shouldDrawImages drawLinks:_shouldDrawLinks]; + + if (_delegateFlags.delegateSupportsNotificationAfterDrawing) + { + [_delegate attributedTextContentView:self didDrawLayoutFrame:theLayoutFrame inContext:ctx]; + } + } + SYNCHRONIZE_END(selfLock) +} + +- (void)drawRect:(CGRect)rect +{ + CGContextRef context = UIGraphicsGetCurrentContext(); + [self.layoutFrame drawInContext:context drawImages:YES drawLinks:YES]; +} + +- (CGSize)sizeThatFits:(CGSize)size +{ + if (size.width==0) + { + size.width = self.bounds.size.width; + } + + CGSize neededSize = CGSizeMake(size.width, CGRectGetMaxY(self.layoutFrame.frame) + _edgeInsets.bottom); + + return neededSize; +} + +- (CGSize)suggestedFrameSizeToFitEntireStringConstraintedToWidth:(CGFloat)width +{ + if (!isnormal(width)) + { + width = self.bounds.size.width; + } + + CGSize neededSize = [self.layouter suggestedFrameSizeToFitEntireStringConstraintedToWidth:width-_edgeInsets.left-_edgeInsets.right]; + + // add vertical insets + neededSize.height += _edgeInsets.top + _edgeInsets.bottom; + + return neededSize; +} + +- (CGSize)attributedStringSizeThatFits:(CGFloat)width +{ + if (!isnormal(width)) + { + width = self.bounds.size.width; + } + + // attributedStringSizeThatFits: returns an unreliable measure prior to 4.2 for very long documents. + CGSize neededSize = [self.layouter suggestedFrameSizeToFitEntireStringConstraintedToWidth:width-_edgeInsets.left-_edgeInsets.right]; + return neededSize; +} + + +- (NSString *)description +{ + NSString *extract = [[[_layoutFrame attributedStringFragment] string] substringFromIndex:[self.layoutFrame visibleStringRange].location]; + + if ([extract length]>10) + { + extract = [extract substringToIndex:10]; + } + + return [NSString stringWithFormat:@"<%@ %@ range:%@ '%@...'>", [self class], NSStringFromCGRect(self.frame),NSStringFromRange([self.layoutFrame visibleStringRange]), extract]; +} + +- (void)relayoutText +{ + // Make sure we actually have a superview before attempting to relayout the text. + if (self.superview) { + // need new layouter + self.layouter = nil; + self.layoutFrame = nil; + + // remove all links because they might have merged or split + [self removeAllCustomViewsForLinks]; + + if (_attributedString) + { + // triggers new layout + CGSize neededSize = [self sizeThatFits:self.bounds.size]; + + // set frame to fit text preserving origin + // call super to avoid endless loop + [self willChangeValueForKey:@"frame"]; + super.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, neededSize.width, neededSize.height); + [self didChangeValueForKey:@"frame"]; + } + + [self setNeedsDisplay]; + [self setNeedsLayout]; + } +} + +- (void)removeAllCustomViewsForLinks +{ + NSArray *linkViews = [customViewsForLinksIndex allValues]; + + for (UIView *customView in linkViews) + { + [customView removeFromSuperview]; + [customViews removeObject:customView]; + } + + [customViewsForLinksIndex removeAllObjects]; +} + +- (void)removeAllCustomViews +{ + NSSet *allCustomViews = [NSSet setWithSet:customViews]; + for (UIView *customView in allCustomViews) + { + [customView removeFromSuperview]; + [customViews removeObject:customView]; + } + + [customViewsForAttachmentsIndex removeAllObjects]; + [customViewsForLinksIndex removeAllObjects]; +} + +- (void)removeSubviewsOutsideRect:(CGRect)rect +{ + NSSet *allCustomViews = [NSSet setWithSet:customViews]; + for (UIView *customView in allCustomViews) + { + if (CGRectGetMinY(customView.frame)> CGRectGetMaxY(rect) || CGRectGetMaxY(customView.frame) < CGRectGetMinY(rect)) + { + NSNumber *indexKey = [NSNumber numberWithInteger:customView.tag]; + + [customView removeFromSuperview]; + [customViews removeObject:customView]; + + [customViewsForAttachmentsIndex removeObjectForKey:indexKey]; + [customViewsForLinksIndex removeObjectForKey:indexKey]; + } + } +} + +#pragma mark Properties +- (void)setEdgeInsets:(UIEdgeInsets)edgeInsets +{ + if (!UIEdgeInsetsEqualToEdgeInsets(edgeInsets, _edgeInsets)) + { + _edgeInsets = edgeInsets; + + [self relayoutText]; + } +} + +- (void)setAttributedString:(NSAttributedString *)string +{ + if (_attributedString != string) + { + + _attributedString = [string copy]; + + // new layout invalidates all positions for custom views + [self removeAllCustomViews]; + + [self relayoutText]; + } +} + +- (void)setFrame:(CGRect)frame //relayoutText:(BOOL)relayoutText +{ + CGRect oldFrame = self.frame; + + [super setFrame:frame]; + + if (!_layoutFrame) + { + return; + } + + BOOL frameDidChange = !CGRectEqualToRect(oldFrame, frame); + + // having a layouter means we are responsible for layouting yourselves + if (frameDidChange) + { + [self relayoutText]; + } +} + +//- (void)setFrame:(CGRect)frame +//{ +// // sizeToFit also calls this, but we want to be able to avoid relayouting +// [self setFrame:frame relayoutText:_relayoutTextOnFrameChange]; +//} + +- (void)setDrawDebugFrames:(BOOL)drawDebugFrames +{ + if (_drawDebugFrames != drawDebugFrames) + { + _drawDebugFrames = drawDebugFrames; + + [self setNeedsDisplay]; + } +} + +- (void)setShouldDrawImages:(BOOL)shouldDrawImages +{ + if (_shouldDrawImages != shouldDrawImages) + { + _shouldDrawImages = shouldDrawImages; + + [self setNeedsDisplay]; + } +} + +- (void)setBackgroundColor:(DTColor *)newColor +{ + super.backgroundColor = newColor; + + if ([newColor alphaComponent]<1.0) + { + self.opaque = NO; + } + else + { + self.opaque = YES; + } +} + + +- (DTCoreTextLayouter *)layouter +{ + SYNCHRONIZE_START(selfLock) + { + if (!_layouter) + { + if (_attributedString) + { + _layouter = [[DTCoreTextLayouter alloc] initWithAttributedString:_attributedString]; + } + } + } + SYNCHRONIZE_END(selfLock) + + return _layouter; +} + +- (void)setLayouter:(DTCoreTextLayouter *)layouter +{ + SYNCHRONIZE_START(selfLock) + { + if (_layouter != layouter) + { + _layouter = layouter; + } + } + SYNCHRONIZE_END(selfLock) +} + +- (DTCoreTextLayoutFrame *)layoutFrame +{ + DTCoreTextLayouter *theLayouter = self.layouter; + + if (!_layoutFrame) + { + // prevent unnecessary locking if we don't need to create new layout frame + SYNCHRONIZE_START(selfLock) + { + // Test again - small window where another thread could have been setting this value + if (!_layoutFrame) + { + // we can only layout if we have our own layouter + if (theLayouter) + { + CGRect rect = UIEdgeInsetsInsetRect(self.bounds, _edgeInsets); + rect.size.height = CGFLOAT_OPEN_HEIGHT; // necessary height set as soon as we know it. + + _layoutFrame = [theLayouter layoutFrameWithRect:rect range:NSMakeRange(0, 0)]; + + if (_delegateFlags.delegateSupportsNotificationBeforeTextBoxDrawing) + { + __unsafe_unretained DTAttributedTextContentView *weakself = self; + + [_layoutFrame setTextBlockHandler:^(DTTextBlock *textBlock, CGRect frame, CGContextRef context, BOOL *shouldDrawDefaultBackground) { + BOOL result = [weakself->_delegate attributedTextContentView:weakself shouldDrawBackgroundForTextBlock:textBlock frame:frame context:context forLayoutFrame:weakself->_layoutFrame]; + + if (shouldDrawDefaultBackground) + { + *shouldDrawDefaultBackground = result; + } + + }]; + } + } + } + } + SYNCHRONIZE_END(selfLock) + } + + return _layoutFrame; +} + +- (void)setLayoutFrame:(DTCoreTextLayoutFrame *)layoutFrame +{ + SYNCHRONIZE_START(selfLock) + { + if (_layoutFrame != layoutFrame) + { + [self removeAllCustomViewsForLinks]; + + if (layoutFrame) + { + [self setNeedsLayout]; + [self setNeedsDisplay]; + } + _layoutFrame = layoutFrame; + } + } + SYNCHRONIZE_END(selfLock) +} + +- (NSMutableSet *)customViews +{ + if (!customViews) + { + customViews = [[NSMutableSet alloc] init]; + } + + return customViews; +} + +- (NSMutableDictionary *)customViewsForLinksIndex +{ + if (!customViewsForLinksIndex) + { + customViewsForLinksIndex = [[NSMutableDictionary alloc] init]; + } + + return customViewsForLinksIndex; +} + +- (NSMutableDictionary *)customViewsForAttachmentsIndex +{ + if (!customViewsForAttachmentsIndex) + { + customViewsForAttachmentsIndex = [[NSMutableDictionary alloc] init]; + } + + return customViewsForAttachmentsIndex; +} + +- (void)setDelegate:(id)delegate +{ + _delegate = delegate; + + _delegateFlags.delegateSupportsCustomViewsForAttachments = [_delegate respondsToSelector:@selector(attributedTextContentView:viewForAttachment:frame:)]; + _delegateFlags.delegateSupportsCustomViewsForLinks = [_delegate respondsToSelector:@selector(attributedTextContentView:viewForLink:identifier:frame:)]; + _delegateFlags.delegateSupportsGenericCustomViews = [_delegate respondsToSelector:@selector(attributedTextContentView:viewForAttributedString:frame:)]; + _delegateFlags.delegateSupportsNotificationAfterDrawing = [_delegate respondsToSelector:@selector(attributedTextContentView:didDrawLayoutFrame:inContext:)]; + _delegateFlags.delegateSupportsNotificationBeforeTextBoxDrawing = [_delegate respondsToSelector:@selector(attributedTextContentView:shouldDrawBackgroundForTextBlock:frame:context:forLayoutFrame:)]; + + if (!_delegateFlags.delegateSupportsCustomViewsForLinks && !_delegateFlags.delegateSupportsGenericCustomViews) + { + [self removeAllCustomViewsForLinks]; + } + + // we don't draw the images if imageViews are provided by the delegate method + // if you want images to be drawn even though you use custom views, set it back to YES after setting delegate + if (_delegateFlags.delegateSupportsGenericCustomViews || _delegateFlags.delegateSupportsCustomViewsForAttachments) + { + _shouldDrawImages = NO; + } + else + { + _shouldDrawImages = YES; + } +} + + +- (dispatch_semaphore_t)selfLock +{ + if (!selfLock) + { + selfLock = dispatch_semaphore_create(1); + } + + return selfLock; +} + + +@synthesize layouter = _layouter; +@synthesize layoutFrame = _layoutFrame; +@synthesize attributedString = _attributedString; +@synthesize delegate = _delegate; +@synthesize edgeInsets = _edgeInsets; +@synthesize drawDebugFrames = _drawDebugFrames; +@synthesize shouldDrawImages = _shouldDrawImages; +@synthesize shouldDrawLinks = _shouldDrawLinks; +@synthesize shouldLayoutCustomSubviews = _shouldLayoutCustomSubviews; +@synthesize layoutOffset = _layoutOffset; +@synthesize backgroundOffset = _backgroundOffset; + +@synthesize customViews; +@synthesize customViewsForLinksIndex; +@synthesize customViewsForAttachmentsIndex; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTAttributedTextView.h b/Pods/DTCoreText/Core/Source/DTAttributedTextView.h new file mode 100644 index 00000000..3e28ae02 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTAttributedTextView.h @@ -0,0 +1,34 @@ +// +// DTAttributedTextView.h +// CoreTextExtensions +// +// Created by Oliver Drobnik on 1/12/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import "DTAttributedTextContentView.h" + +@class DTAttributedTextView; + +@interface DTAttributedTextView : UIScrollView +{ + DTAttributedTextContentView *contentView; + UIView *backgroundView; +} + +@property (nonatomic, strong) NSAttributedString *attributedString; + +@property (nonatomic, strong, readonly) DTAttributedTextContentView *contentView; +@property (nonatomic, strong) IBOutlet UIView *backgroundView; + +@property (nonatomic, unsafe_unretained) IBOutlet id textDelegate; + + +/** + Scrolls the receiver to the anchor with the given name to the top. + @param anchorName The name of the href anchor. + @param animated `YES` if the movement should be animated. + */ +- (void)scrollToAnchorNamed:(NSString *)anchorName animated:(BOOL)animated; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTAttributedTextView.m b/Pods/DTCoreText/Core/Source/DTAttributedTextView.m new file mode 100644 index 00000000..b58b0864 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTAttributedTextView.m @@ -0,0 +1,244 @@ +// +// DTAttributedTextView.m +// CoreTextExtensions +// +// Created by Oliver Drobnik on 1/12/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import "DTAttributedTextView.h" +#import "DTCoreText.h" + +@interface DTAttributedTextView () + +- (void)setup; + +@end + + + +@implementation DTAttributedTextView + +- (id)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + + if (self) + { + [self setup]; + } + + return self; +} + +- (void)dealloc +{ + contentView.delegate = nil; + [contentView removeObserver:self forKeyPath:@"frame"]; +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + + [self contentView]; + + // layout custom subviews for visible area + [contentView layoutSubviewsInRect:self.bounds]; +} + +- (void)awakeFromNib +{ + [self setup]; +} + +// default +- (void)setup +{ + if (!self.backgroundColor) + { + self.backgroundColor = [DTColor whiteColor]; + self.opaque = YES; + return; + } + + CGFloat alpha = [self.backgroundColor alphaComponent]; + + if (alpha < 1.0) + { + self.opaque = NO; + self.contentView.opaque = NO; + } + else + { + self.opaque = YES; + self.contentView.opaque = YES; + } + + self.autoresizesSubviews = YES; + self.clipsToBounds = YES; +} + +// override class e.g. for mutable content view +- (Class)classForContentView +{ + return [DTAttributedTextContentView class]; +} + +#pragma mark External Methods +- (void)scrollToAnchorNamed:(NSString *)anchorName animated:(BOOL)animated +{ + NSRange range = [self.contentView.attributedString rangeOfAnchorNamed:anchorName]; + + if (range.length != NSNotFound) + { + // get the line of the first index of the anchor range + DTCoreTextLayoutLine *line = [self.contentView.layoutFrame lineContainingIndex:range.location]; + + // make sure we don't scroll too far + CGFloat maxScrollPos = self.contentSize.height - self.bounds.size.height + self.contentInset.bottom + self.contentInset.top; + CGFloat scrollPos = MIN(line.frame.origin.y, maxScrollPos); + + // scroll + [self setContentOffset:CGPointMake(0, scrollPos) animated:animated]; + } +} + +#pragma mark Notifications +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +{ + if (object == contentView && [keyPath isEqualToString:@"frame"]) + { + CGRect newFrame = [[change objectForKey:NSKeyValueChangeNewKey] CGRectValue]; + self.contentSize = newFrame.size; + } +} + +#pragma mark Properties +- (DTAttributedTextContentView *)contentView +{ + if (!contentView) + { + // subclasses can specify a DTAttributedTextContentView subclass instead + Class classToUse = [self classForContentView]; + + contentView = [[classToUse alloc] initWithFrame:self.bounds]; + contentView.userInteractionEnabled = YES; + contentView.backgroundColor = self.backgroundColor; + contentView.shouldLayoutCustomSubviews = NO; // we call layout when scrolling + + // we want to know if the frame changes so that we can adjust the scrollview content size + [contentView addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew context:nil]; + + [self addSubview:contentView]; + } + + return contentView; +} + +- (void)setBackgroundColor:(DTColor *)newColor +{ + if ([newColor alphaComponent]<1.0) + { + super.backgroundColor = newColor; + contentView.backgroundColor = [DTColor clearColor]; + self.opaque = NO; + } + else + { + super.backgroundColor = newColor; + + if (contentView.opaque) + { + contentView.backgroundColor = newColor; + } + } +} + +- (UIView *)backgroundView +{ + if (!backgroundView) + { + backgroundView = [[UIView alloc] initWithFrame:self.bounds]; + backgroundView.backgroundColor = [DTColor whiteColor]; + + // default is no interaction because background should have no interaction + backgroundView.userInteractionEnabled = NO; + + [self insertSubview:backgroundView belowSubview:self.contentView]; + + // make content transparent so that we see the background + contentView.backgroundColor = [DTColor clearColor]; + contentView.opaque = NO; + } + + return backgroundView; +} + +- (void)setBackgroundView:(UIView *)newBackgroundView +{ + if (backgroundView != newBackgroundView) + { + [backgroundView removeFromSuperview]; + backgroundView = newBackgroundView; + + [self insertSubview:backgroundView belowSubview:self.contentView]; + + if (backgroundView) + { + // make content transparent so that we see the background + contentView.backgroundColor = [DTColor clearColor]; + contentView.opaque = NO; + } + else + { + contentView.backgroundColor = [DTColor whiteColor]; + contentView.opaque = YES; + } + } +} + +- (void)setAttributedString:(NSAttributedString *)string +{ + self.contentView.attributedString = string; + + // might need layout for visible custom views + [self setNeedsLayout]; + + // adjust content size right away + self.contentSize = self.contentView.frame.size; +} + +- (NSAttributedString *)attributedString +{ + return self.contentView.attributedString; +} + + +- (void)setFrame:(CGRect)frame +{ + if (!CGRectEqualToRect(self.frame, frame)) + { + if (self.frame.size.width != frame.size.width) + { + contentView.frame = CGRectMake(0,0,frame.size.width, frame.size.height); + } + [super setFrame:frame]; + } +} + +- (void)setTextDelegate:(id)aTextDelegate +{ + self.contentView.delegate = aTextDelegate; +} + +- (id)textDelegate +{ + return contentView.delegate; +} + +@synthesize attributedString; +@synthesize contentView; +@synthesize textDelegate; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTCSSListStyle.h b/Pods/DTCoreText/Core/Source/DTCSSListStyle.h new file mode 100644 index 00000000..36864852 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCSSListStyle.h @@ -0,0 +1,168 @@ +// +// DTCSSListStyle.h +// CoreTextExtensions +// +// Created by Oliver Drobnik on 8/11/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +/** + List Styles + */ + +typedef enum +{ + DTCSSListStyleTypeInherit = 0, + DTCSSListStyleTypeNone, + DTCSSListStyleTypeCircle, + DTCSSListStyleTypeDecimal, + DTCSSListStyleTypeDecimalLeadingZero, + DTCSSListStyleTypeDisc, + DTCSSListStyleTypeSquare, + DTCSSListStyleTypeUpperAlpha, + DTCSSListStyleTypeUpperLatin, + DTCSSListStyleTypeLowerAlpha, + DTCSSListStyleTypeLowerLatin, + DTCSSListStyleTypePlus, + DTCSSListStyleTypeUnderscore, + DTCSSListStyleTypeImage, + DTCSSListStyleTypeInvalid = NSIntegerMax +} DTCSSListStyleType; + +/** + List Marker Positions + */ +typedef enum +{ + DTCSSListStylePositionInherit = 0, + DTCSSListStylePositionInside, + DTCSSListStylePositionOutside, + DTCSSListStylePositionInvalid = NSIntegerMax +} DTCSSListStylePosition; + + +/** + This class is the equivalent of `NSTextList` on Mac with the added handling of the marker position. + */ +@interface DTCSSListStyle : NSObject + + +/** + @name Getting Types from Strings + */ + +/** + Convert a string into a list style type. + + @param string The string to convert + */ ++ (DTCSSListStyleType)listStyleTypeFromString:(NSString *)string; + + +/** + Convert a string into a marker position. + + @param string The string to convert + */ ++ (DTCSSListStylePosition)listStylePositionFromString:(NSString *)string; + +/** + @name Creating List Styles + */ + +/** + Creates a list style from the passed CSS style dictionary + + @param styles A CSS style dictionary from which the construct a suitable list style + */ +- (id)initWithStyles:(NSDictionary *)styles; + +/** + @name Working with CSS Styles + */ + +/** + Update the receiver from the CSS styles dictionary passed + + @param styles A dictionary of CSS styles. + */ +- (void)updateFromStyleDictionary:(NSDictionary *)styles; + + +/** + @name Working with Prefixes + */ + + +/** + Returns the prefix for lists of the receiver's settings. + + @param counter The counter value to use for ordered lists. + @returns The prefix string to prepend to list items. + */ +- (NSString *)prefixWithCounter:(NSInteger)counter; + + +/** + @name Managing Item Numbering + */ + + +/** + Sets the starting item number for the text list. + + The default value is `1`. This value will be used only for ordered lists, and ignored in other cases. + + @param itemNum The item number. + */ +- (void)setStartingItemNumber:(NSInteger)itemNum; + + +/** + Returns the starting item number for the text list. + + The default value is `1`. This value will be used only for ordered lists, and ignored in other cases. + @returns The item number. + */ +- (NSInteger)startingItemNumber; + + +/** + @name Getting Information about Lists + */ + +/** + Returns if the receiver is an ordered or unordered list + + @returns `YES` if the receiver is ordered, `NO` if it is unordered + */ +- (BOOL)isOrdered; + + +/** + If the list style is inherited. + + @warn This is not implemented. + */ +@property (nonatomic, assign) BOOL inherit; + + +/** + The type of the text list + */ +@property (nonatomic, assign) DTCSSListStyleType type; + + +/** + The position of the marker in the prefix. + */ +@property (nonatomic, assign) DTCSSListStylePosition position; + + +/** + The image name to use for the marker + */ +@property (nonatomic, copy) NSString *imageName; + + +@end diff --git a/Pods/DTCoreText/Core/Source/DTCSSListStyle.m b/Pods/DTCoreText/Core/Source/DTCSSListStyle.m new file mode 100644 index 00000000..1f4f1b9c --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCSSListStyle.m @@ -0,0 +1,380 @@ +// +// DTCSSListStyle.m +// CoreTextExtensions +// +// Created by Oliver Drobnik on 8/11/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import "DTCSSListStyle.h" + +#import "DTCoreTextConstants.h" + +#import "NSScanner+HTML.h" +//#import "NSString+HTML.h" + + + + +@interface DTCSSListStyle () + +- (void)updateFromStyleDictionary:(NSDictionary *)styles; + +@property (nonatomic, assign) NSInteger startingItemNumber; + +@end + + + +@implementation DTCSSListStyle +{ + BOOL _inherit; + + DTCSSListStyleType _type; + DTCSSListStylePosition _position; + + NSString *_imageName; + NSInteger _startingItemNumber; +} + +- (id)initWithStyles:(NSDictionary *)styles +{ + self = [super init]; + + if (self) + { + // default + _position = DTCSSListStylePositionOutside; + _startingItemNumber = 1; + + [self updateFromStyleDictionary:styles]; + } + + return self; +} + + +// convert string to listStyleType ++ (DTCSSListStyleType)listStyleTypeFromString:(NSString *)string +{ + if (!string) + { + return DTCSSListStyleTypeInvalid; + } + + // always compare lower case + string = [string lowercaseString]; + + if ([string isEqualToString:@"inherit"]) + { + return DTCSSListStyleTypeInherit; + } + else if ([string isEqualToString:@"none"]) + { + return DTCSSListStyleTypeNone; + } + else if ([string isEqualToString:@"circle"]) + { + return DTCSSListStyleTypeCircle; + } + else if ([string isEqualToString:@"square"]) + { + return DTCSSListStyleTypeSquare; + } + else if ([string isEqualToString:@"decimal"]) + { + return DTCSSListStyleTypeDecimal; + } + else if ([string isEqualToString:@"decimal-leading-zero"]) + { + return DTCSSListStyleTypeDecimalLeadingZero; + } + else if ([string isEqualToString:@"disc"]) + { + return DTCSSListStyleTypeDisc; + } + else if ([string isEqualToString:@"upper-alpha"]||[string isEqualToString:@"upper-latin"]) + { + return DTCSSListStyleTypeUpperAlpha; + } + else if ([string isEqualToString:@"lower-alpha"]||[string isEqualToString:@"lower-latin"]) + { + return DTCSSListStyleTypeLowerAlpha; + } + else if ([string isEqualToString:@"plus"]) + { + return DTCSSListStyleTypePlus; + } + else if ([string isEqualToString:@"underscore"]) + { + return DTCSSListStyleTypeUnderscore; + } + else + { + return DTCSSListStyleTypeNone; + } +} + ++ (DTCSSListStylePosition)listStylePositionFromString:(NSString *)string +{ + if (!string) + { + return DTCSSListStylePositionInvalid; + } + + // always compare lower case + string = [string lowercaseString]; + + if ([string isEqualToString:@"inherit"]) + { + return DTCSSListStylePositionInherit; + } + else if ([string isEqualToString:@"inside"]) + { + return DTCSSListStylePositionInside; + } + else if ([string isEqualToString:@"outside"]) + { + return DTCSSListStylePositionOutside; + } + else + { + return DTCSSListStylePositionInherit; + } +} + +// returns NO if not a valid type +- (BOOL)setTypeWithString:(NSString *)string +{ + DTCSSListStyleType type = [DTCSSListStyle listStyleTypeFromString:string]; + if (type == DTCSSListStyleTypeInvalid) + { + return NO; + } + + _type = type; + + return YES; +} + +// returns NO if not a valid type +- (BOOL)setPositionWithString:(NSString *)string +{ + DTCSSListStylePosition position = [DTCSSListStyle listStylePositionFromString:string]; + + if (position == DTCSSListStylePositionInvalid) + { + return NO; + } + + _position = position; + return YES; +} + +- (void)updateFromStyleDictionary:(NSDictionary *)styles +{ + NSString *shortHand = [[styles objectForKey:@"list-style"] lowercaseString]; + + if (shortHand) + { + if ([shortHand isEqualToString:@"inherit"]) + { + _inherit = YES; + return; + } + + NSArray *components = [shortHand componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; + + BOOL typeWasSet = NO; + BOOL positionWasSet = NO; + + + for (NSString *oneComponent in components) + { + if ([oneComponent hasPrefix:@"url"]) + { + // list-style-image + NSString *urlString; + NSScanner *scanner = [NSScanner scannerWithString:oneComponent]; + + if ([scanner scanCSSURL:&urlString]) + { + self.imageName = urlString; + continue; + } + } + + if (!typeWasSet && [self setTypeWithString:oneComponent]) + { + typeWasSet = YES; + continue; + } + + if (!positionWasSet && [self setPositionWithString:oneComponent]) + { + positionWasSet = YES; + continue; + } + } + + return; + } + + // not a short hand, set from individual types + + [self setTypeWithString:[styles objectForKey:@"list-style-type"]]; + [self setPositionWithString:[styles objectForKey:@"list-style-position"]]; + + NSString *tmpStr = [styles objectForKey:@"list-style-image"]; + + if (tmpStr) + { + // extract just the name + + NSString *urlString; + NSScanner *scanner = [NSScanner scannerWithString:tmpStr]; + + if ([scanner scanCSSURL:&urlString]) + { + self.imageName = urlString; + } + } +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@ %p type=%d position=%d>", NSStringFromClass([self class]), self, (int)_type, (int)_position]; +} + +#pragma mark Copying + +- (id)copyWithZone:(NSZone *)zone +{ + DTCSSListStyle *newStyle = [[DTCSSListStyle allocWithZone:zone] init]; + newStyle.type = self.type; + newStyle.position = self.position; + newStyle.imageName = self.imageName; + + return newStyle; +} + +#pragma mark Utilities + +- (NSString *)prefixWithCounter:(NSInteger)counter +{ + NSString *token = nil; + + DTCSSListStyleType listStyleType = _type; + + if (self.imageName) + { + listStyleType = DTCSSListStyleTypeImage; + } + + + switch (listStyleType) + { + case DTCSSListStyleTypeNone: + case DTCSSListStyleTypeInherit: // should never be called with inherit + case DTCSSListStyleTypeInvalid: + { + return nil; + } + case DTCSSListStyleTypeImage: + { + token = UNICODE_OBJECT_PLACEHOLDER; + break; + } + case DTCSSListStyleTypeCircle: + { + token = @"\u25e6"; + break; + } + case DTCSSListStyleTypeSquare: + { + token = @"\u25aa"; + break; + } + case DTCSSListStyleTypeDecimal: + { + token = [NSString stringWithFormat:@"%d.", (int)counter]; + break; + } + case DTCSSListStyleTypeDecimalLeadingZero: + { + token = [NSString stringWithFormat:@"%02d.", (int)counter]; + break; + } + case DTCSSListStyleTypeDisc: + { + token = @"\u2022"; + break; + } + case DTCSSListStyleTypeUpperAlpha: + case DTCSSListStyleTypeUpperLatin: + { + char letter = 'A' + (char)(counter - 1); + token = [NSString stringWithFormat:@"%c.", letter]; + break; + } + case DTCSSListStyleTypeLowerAlpha: + case DTCSSListStyleTypeLowerLatin: + { + char letter = 'a' + (char)(counter - 1); + token = [NSString stringWithFormat:@"%c.", letter]; + break; + } + case DTCSSListStyleTypePlus: + { + token = @"+"; + break; + } + case DTCSSListStyleTypeUnderscore: + { + token = @"_"; + } + } + + if (_position == DTCSSListStylePositionInside) + { + // iOS needs second tab, Mac ignores position outside +#if TARGET_OS_IPHONE + return [NSString stringWithFormat:@"\x09\x09%@", token]; +#else + return [NSString stringWithFormat:@"\x09%@\x09", token]; +#endif + } + else + { + return [NSString stringWithFormat:@"\x09%@\x09", token]; + } +} + +- (BOOL)isOrdered +{ + switch (_type) + { + case DTCSSListStyleTypeDecimal: + case DTCSSListStyleTypeDecimalLeadingZero: + case DTCSSListStyleTypeUpperAlpha: + case DTCSSListStyleTypeUpperLatin: + case DTCSSListStyleTypeLowerAlpha: + case DTCSSListStyleTypeLowerLatin: + return YES; + + default: + return NO; + } +} + +#pragma mark Properties + +@synthesize inherit = _inherit; +@synthesize type = _type; +@synthesize position = _position; +@synthesize imageName = _imageName; +@synthesize startingItemNumber = _startingItemNumber; + +@end + +// TO DO: Implement image diff --git a/Pods/DTCoreText/Core/Source/DTCSSStylesheet.h b/Pods/DTCoreText/Core/Source/DTCSSStylesheet.h new file mode 100644 index 00000000..0f147609 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCSSStylesheet.h @@ -0,0 +1,80 @@ +// +// DTCSSStylesheet.h +// CoreTextExtensions +// +// Created by Oliver Drobnik on 9/5/11. +// Copyright (c) 2011 Drobnik.com. All rights reserved. +// + +#import + +@class DTHTMLElement; + +/** + This class represents a CSS style sheet used for specifying formatting for certain CSS selectors. + + It supports matching styles by class, by id or by tag name. Hierarchy matching is not supported yet. + */ +@interface DTCSSStylesheet : NSObject + + +/** + @name Creating Stylesheets + */ + +/** + Creates the default stylesheet. + + This stylesheet is based on the standard styles that Webkit provides for these tags. This stylesheet is loaded from an embedded copy of default.css. + */ ++ (DTCSSStylesheet *)defaultStyleSheet; + + +/** + Creates a stylesheet with a given style block + + @param css The CSS string for the style block + */ +- (id)initWithStyleBlock:(NSString *)css; + + +/** + @name Working with CSS Style Blocks + */ + + +/** + Parses a style block string and adds the found style rules to the receiver. + + @param css The CSS string for the style block +*/ +- (void)parseStyleBlock:(NSString *)css; + + +/** + Merges styles from given stylesheet into the receiver + + @param stylesheet the stylesheet to merge + */ +- (void)mergeStylesheet:(DTCSSStylesheet *)stylesheet; + + +/** + @name Accessing Style Information + */ + +/** + Returns a dictionary that contains the merged style for a given element and the applicable style rules from the receiver. + + @param element The HTML element. + @returns The merged style dictionary containing only styles which selector matches the element + */ +- (NSDictionary *)mergedStyleDictionaryForElement:(DTHTMLElement *)element; + + +/** + Returns a dictionary of the styles of the receiver + */ +- (NSDictionary *)styles; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTCSSStylesheet.m b/Pods/DTCoreText/Core/Source/DTCSSStylesheet.m new file mode 100644 index 00000000..6b7cb54a --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCSSStylesheet.m @@ -0,0 +1,386 @@ +// +// DTCSSStylesheet.m +// CoreTextExtensions +// +// Created by Oliver Drobnik on 9/5/11. +// Copyright (c) 2011 Drobnik.com. All rights reserved. +// + +#import "DTCSSStylesheet.h" +#import "DTCSSListStyle.h" + +#import "DTHTMLElement.h" +#import "NSScanner+HTML.h" +#import "NSString+CSS.h" +#import "NSString+HTML.h" + + +// external symbols generated via custom build rule and xxd +extern unsigned char default_css[]; +extern unsigned int default_css_len; + + +@implementation DTCSSStylesheet +{ + NSMutableDictionary *_styles; +} + +#pragma mark Creating Stylesheets + ++ (DTCSSStylesheet *)defaultStyleSheet +{ + static DTCSSStylesheet *defaultDTCSSStylesheet = nil; + if (defaultDTCSSStylesheet != nil) { + return defaultDTCSSStylesheet; + } + + @synchronized(self) { + if (defaultDTCSSStylesheet == nil) { + // get the data from the external symbol + NSData *data = [NSData dataWithBytes:default_css length:default_css_len]; + NSString *cssString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + + defaultDTCSSStylesheet = [[DTCSSStylesheet alloc] initWithStyleBlock:cssString]; + } + } + return defaultDTCSSStylesheet; +} + +- (id)initWithStyleBlock:(NSString *)css +{ + self = [super init]; + + if (self) + { + _styles = [[NSMutableDictionary alloc] init]; + + [self parseStyleBlock:css]; + } + + return self; +} + +- (id)initWithStylesheet:(DTCSSStylesheet *)stylesheet +{ + self = [super init]; + + if (self) + { + _styles = [[NSMutableDictionary alloc] init]; + + [self mergeStylesheet:stylesheet]; + } + + return self; +} + +- (NSString *)description +{ + return [_styles description]; +} + +#pragma mark Working with Style Blocks + +- (void)_uncompressShorthands:(NSMutableDictionary *)styles +{ + NSString *shortHand = [[styles objectForKey:@"list-style"] lowercaseString]; + + if (shortHand) + { + [styles removeObjectForKey:@"list-style"]; + + if ([shortHand isEqualToString:@"inherit"]) + { + [styles setObject:@"inherit" forKey:@"list-style-type"]; + [styles setObject:@"inherit" forKey:@"list-style-position"]; + return; + } + + NSArray *components = [shortHand componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; + + BOOL typeWasSet = NO; + BOOL positionWasSet = NO; + + DTCSSListStyleType listStyleType = DTCSSListStyleTypeNone; + DTCSSListStylePosition listStylePosition = DTCSSListStylePositionInherit; + + for (NSString *oneComponent in components) + { + if ([oneComponent hasPrefix:@"url"]) + { + // list-style-image + NSScanner *scanner = [NSScanner scannerWithString:oneComponent]; + + if ([scanner scanCSSURL:NULL]) + { + [styles setObject:oneComponent forKey:@"list-style-image"]; + + continue; + } + } + + if (!typeWasSet) + { + // check if valid type + listStyleType = [DTCSSListStyle listStyleTypeFromString:oneComponent]; + + if (listStyleType != DTCSSListStyleTypeInvalid) + { + [styles setObject:oneComponent forKey:@"list-style-type"]; + + typeWasSet = YES; + continue; + } + } + + if (!positionWasSet) + { + // check if valid position + listStylePosition = [DTCSSListStyle listStylePositionFromString:oneComponent]; + + if (listStylePosition != DTCSSListStylePositionInvalid) + { + [styles setObject:oneComponent forKey:@"list-style-position"]; + + positionWasSet = YES; + continue; + } + } + } + + return; + } +} + +- (void)_addStyleRule:(NSString *)rule withSelector:(NSString*)selectors +{ + NSArray *split = [selectors componentsSeparatedByString:@","]; + + for (NSString *selector in split) + { + NSString *cleanSelector = [selector stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + + NSMutableDictionary *ruleDictionary = [[rule dictionaryOfCSSStyles] mutableCopy]; + + // need to uncompress because otherwise we might get shorthands and non-shorthands together + [self _uncompressShorthands:ruleDictionary]; + + // check if there is a pseudo selector + NSRange colonRange = [cleanSelector rangeOfString:@":"]; + NSString *pseudoSelector = nil; + + if (colonRange.length==1) + { + pseudoSelector = [cleanSelector substringFromIndex:colonRange.location+1]; + cleanSelector = [cleanSelector substringToIndex:colonRange.location]; + + // prefix all rules with the pseudo-selector + for (NSString *oneRuleKey in [ruleDictionary allKeys]) + { + // remove double quotes + NSString *value = [ruleDictionary objectForKey:oneRuleKey]; + + if ([value hasPrefix:@"\""] && [value hasSuffix:@"\""]) + { + // treat as HTML string, remove quotes + NSRange range = NSMakeRange(1, [value length]-2); + + value = [[value substringWithRange:range] stringByAddingHTMLEntities]; + } + + // prefix key with the pseudo selector + NSString *prefixedKey = [NSString stringWithFormat:@"%@:%@", pseudoSelector, oneRuleKey]; + [ruleDictionary setObject:value forKey:prefixedKey]; + [ruleDictionary removeObjectForKey:oneRuleKey]; + } + } + + NSDictionary *existingRulesForSelector = [_styles objectForKey:cleanSelector]; + + if (existingRulesForSelector) + { + // substitute new rules over old ones + NSMutableDictionary *tmpDict = [existingRulesForSelector mutableCopy]; + + // append new rules + [tmpDict addEntriesFromDictionary:ruleDictionary]; + + // save it + [_styles setObject:tmpDict forKey:cleanSelector]; + } + else + { + [_styles setObject:ruleDictionary forKey:cleanSelector]; + } + } +} + + +- (void)parseStyleBlock:(NSString*)css +{ + NSUInteger braceLevel = 0, braceMarker = 0; + + NSString* selector; + + NSUInteger length = [css length]; + + for (NSUInteger i = 0; i < length; i++) { + + unichar c = [css characterAtIndex:i]; + + if (c == '/') + { + i++; + // skip until closing / + + for (; i < length; i++) + { + if ([css characterAtIndex:i] == '/') + { + break; + } + } + + if (i < length) + { + braceMarker = i+1; + continue; + } + else + { + // end of string + return; + } + } + + + // An opening brace! It could be the start of a new rule, or it could be a nested brace. + if (c == '{') { + + // If we start a new rule... + + if (braceLevel == 0) + { + // Grab the selector (we'll process it in a moment) + selector = [css substringWithRange:NSMakeRange(braceMarker, i-braceMarker)]; + + // And mark our position so we can grab the rule's CSS when it is closed + braceMarker = i + 1; + } + + // Increase the brace level. + braceLevel += 1; + } + + // A closing brace! + else if (c == '}') + { + // If we finished a rule... + if (braceLevel == 1) + { + NSString *rule = [css substringWithRange:NSMakeRange(braceMarker, i-braceMarker)]; + + [self _addStyleRule:rule withSelector: selector]; + + braceMarker = i + 1; + } + + braceLevel = MAX(braceLevel-1, 0ul); + } + } +} + + +- (void)mergeStylesheet:(DTCSSStylesheet *)stylesheet +{ + [_styles addEntriesFromDictionary:[stylesheet styles]]; +} + +#pragma mark Accessing Style Information + +- (NSDictionary *)mergedStyleDictionaryForElement:(DTHTMLElement *)element +{ + // We are going to combine all the relevant styles for this tag. + // (Note that when styles are applied, the later styles take precedence, + // so the order in which we grab them matters!) + + NSMutableDictionary *tmpDict = [NSMutableDictionary dictionary]; + + // Get based on element + NSDictionary *byTagName = [self.styles objectForKey:element.name]; + + if (byTagName) + { + [tmpDict addEntriesFromDictionary:byTagName]; + } + + // Get based on class(es) + NSString *classString = [element.attributes objectForKey:@"class"]; + NSArray *classes = [classString componentsSeparatedByString:@" "]; + + for (NSString *class in classes) + { + NSString *classRule = [NSString stringWithFormat:@".%@", class]; + NSString *classAndTagRule = [NSString stringWithFormat:@"%@.%@", element.name, class]; + + NSDictionary *byClass = [_styles objectForKey:classRule]; + NSDictionary *byClassAndName = [_styles objectForKey:classAndTagRule]; + + if (byClass) + { + [tmpDict addEntriesFromDictionary:byClass]; + } + + if (byClassAndName) + { + [tmpDict addEntriesFromDictionary:byClassAndName]; + } + } + + // Get based on id + NSString *idRule = [NSString stringWithFormat:@"#%@", [element.attributes objectForKey:@"id"]]; + NSDictionary *byID = [_styles objectForKey:idRule]; + + if (byID) + { + [tmpDict addEntriesFromDictionary:byID]; + } + + // Get tag's local style attribute + NSString *styleString = [element.attributes objectForKey:@"style"]; + + if ([styleString length]) + { + NSMutableDictionary *localStyles = [[styleString dictionaryOfCSSStyles] mutableCopy]; + + // need to uncompress because otherwise we might get shorthands and non-shorthands together + [self _uncompressShorthands:localStyles]; + + [tmpDict addEntriesFromDictionary:localStyles]; + } + + if ([tmpDict count]) + { + return tmpDict; + } + else + { + return nil; + } +} + +- (NSDictionary *)styles +{ + return _styles; +} + +#pragma mark NSCopying + +- (id)copyWithZone:(NSZone *)zone +{ + DTCSSStylesheet *newStylesheet = [[DTCSSStylesheet allocWithZone:zone] initWithStylesheet:self]; + + return newStylesheet; +} + +@end diff --git a/Pods/DTCoreText/Core/Source/DTColor+HTML.h b/Pods/DTCoreText/Core/Source/DTColor+HTML.h new file mode 100755 index 00000000..2a07d087 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTColor+HTML.h @@ -0,0 +1,98 @@ +// +// DTColor+HTML.h +// CoreTextExtensions +// +// Created by Oliver Drobnik on 1/9/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#if TARGET_OS_IPHONE + +/** + Methods used to work with HTML representations of colors. + */ +@interface UIColor (HTML) + +/** + Takes a CSS color string ('333', 'F9FFF9'), determines the RGB values used, and returns a UIColor object of that color. + For each part of the RGB color those numbers for that color are converted to a number using a category on NSString. Then that number is divided by the maximum value, 15 for 3 character strings and 255 for 6 character strings, making the color a percentage and within the range 0.0 and 1.0 that UIColor uses. + @param hex A CSS hexadecimal color string of length 6 or 3. + @returns A UIColor object generated from the hexadecimal color string with alpha 1.0. + */ ++ (UIColor *)colorWithHexString:(NSString *)hex; + +/** + Takes an English string representing a color and maps it to a numeric RGB value as declared by the HTML and CSS specifications (see http://www.w3schools.com/html/html_colornames.asp). Also accepts CSS `#` hexadecimal colors, `rgba`, and `rgb` and does the right thing returning a corresponding UIColor. + If a color begins with a `#` we know that it is a hexadecimal color and send it to colorWithHexString:. If the string is an `rgba()` color declaration the comma delimited r, g, b, and a values are made into percentages and then made into a UIColor which is returned. If the string is an `rgb()` color declaration the same process happens except with an alpha of 1.0. + The last case is that the color string is not a numeric declaration `#`, nor a `rgba` or `rgb` declaration so the CSS color value matching the English string is found in a lookup dictionary and then passed to colorWithHexString: which will make a UIColor out of the hexadecimal string. + @param name The CSS color string that we want to map from a name into an RGB color. + @returns A UIColor object representing the name parameter as numeric values declared by the HTML and CSS specifications, a `rgba()` color, or a `rgb()` color. + */ ++ (UIColor *)colorWithHTMLName:(NSString *)name; + +/** + Return a string hexadecimal representation of this UIColor. Splits the color into components with CGColor methods, re-maps them from percentages to the range 0-255, and depending on the number of components returns a grayscale (repeating string of two characters) or color RGB (alpha is stripped) six character string. In the event of a non-2 or non-4 component color nil is returned as it is from an unsupported color space. + @returns A CSS hexadecimal NSString specifying this UIColor. + */ +- (NSString *)htmlHexString; + + +/** + A quick method to return the alpha component of this UIColor by using the CGColorGetAlpha method. + @returns The floating point alpha value of this UIColor. + */ +- (CGFloat)alphaComponent; + +@end + +#else + +/** + Methods used to work with HTML representations of colors. + */ +@interface NSColor (HTML) + + +/** + Takes a CSS color string ('333', 'F9FFF9'), determines the RGB values used, and returns an NSColor object of that color. + For each part of the RGB color those numbers for that color are converted to a number using a category on NSString. Then that number is divided by the maximum value, 15 for 3 character strings and 255 for 6 character strings, making the color a percentage and within the range 0.0 and 1.0 that NSColor uses. + @param hex A CSS hexadecimal color string of length 6 or 3. + @returns An NSColor object generated from the hexadecimal color string with alpha 1.0. + */ ++ (NSColor *)colorWithHexString:(NSString *)hex; + + +/** + Takes an English string representing a color and maps it to a numeric RGB value as declared by the HTML and CSS specifications (see http://www.w3schools.com/html/html_colornames.asp). Also accepts CSS `#` hexadecimal colors, `rgba`, and `rgb` and does the right thing returning a corresponding NSColor. + If a color begins with a `#` we know that it is a hexadecimal color and send it to colorWithHexString:. If the string is an `rgba()` color declaration the comma delimited r, g, b, and a values are made into percentages and then made into an NSColor which is returned. If the string is an `rgb()` color declaration the same process happens except with an alpha of 1.0. + The last case is that the color string is not a numeric declaration `#`, nor a `rgba` or `rgb` declaration so the CSS color value matching the English string is found in a lookup dictionary and then passed to colorWithHexString: which will make an NSColor out of the hexadecimal string. + @param name The CSS color string that we want to map from a name into an RGB color. + @returns An NSColor object representing the name parameter as numeric values declared by the HTML and CSS specifications, a `rgba()` color, or a `rgb()` color. + */ ++ (NSColor *)colorWithHTMLName:(NSString *)name; + + +/** + Return a string hexadecimal representation of this NSColor. Splits the color into components with CGColor methods, re-maps them from percentages in the range 0-255, and returns the RGB color (alpha is stripped) in a six character string. + @returns A CSS hexadecimal NSString specifying this NSColor. + */ +- (NSString *)htmlHexString; + +#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_7 +/** + Converts a CGColorRef into an NSColor by placing each component into an NSColor and pending on the component count to return a grayscale or rgb color. If there are not 2 (grayscale) or 4 (rgba) components the color is from an unsupported color space and nil is returned. + @param cgColor The CGColorRef to convert + @returns An NSColor of this CGColorRef + */ ++ (NSColor *)colorWithCGColor:(CGColorRef)cgColor; + +/** + Converts an NSColor into a CGColorRef. + @returns A CGColorRef of this NSColor +*/ +- (CGColorRef)CGColor; +#endif + +@end + +#endif diff --git a/Pods/DTCoreText/Core/Source/DTColor+HTML.m b/Pods/DTCoreText/Core/Source/DTColor+HTML.m new file mode 100644 index 00000000..f35cbd65 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTColor+HTML.m @@ -0,0 +1,535 @@ +// +// DTColor+HTML.m +// CoreTextExtensions +// +// Created by Oliver Drobnik on 1/9/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import "DTColor+HTML.h" +#import "NSString+HTML.h" + +static NSDictionary *colorLookup = nil; + +#if TARGET_OS_IPHONE + +@implementation UIColor (HTML) + ++ (UIColor *)colorWithHexString:(NSString *)hex +{ + if ([hex length]!=6 && [hex length]!=3) + { + return nil; + } + + NSUInteger digits = [hex length]/3; + CGFloat maxValue = (digits==1)?15.0:255.0; + + CGFloat red = [[hex substringWithRange:NSMakeRange(0, digits)] integerValueFromHex]/maxValue; + CGFloat green = [[hex substringWithRange:NSMakeRange(digits, digits)] integerValueFromHex]/maxValue; + CGFloat blue = [[hex substringWithRange:NSMakeRange(2*digits, digits)] integerValueFromHex]/maxValue; + + return [UIColor colorWithRed:red green:green blue:blue alpha:1.0]; +} + + +// Source: http://www.w3schools.com/html/html_colornames.asp ++ (UIColor *)colorWithHTMLName:(NSString *)name +{ + if ([name hasPrefix:@"#"]) + { + return [UIColor colorWithHexString:[name substringFromIndex:1]]; + } + + if ([name hasPrefix:@"rgba"]) { + NSString *rgbaName = [name stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"rgba() "]]; + NSArray *rgba = [rgbaName componentsSeparatedByString:@","]; + + if ([rgba count] != 4) { + // Incorrect syntax + return nil; + } + + return [UIColor colorWithRed:[[rgba objectAtIndex:0] floatValue] / 255 + green:[[rgba objectAtIndex:1] floatValue] / 255 + blue:[[rgba objectAtIndex:2] floatValue] / 255 + alpha:[[rgba objectAtIndex:3] floatValue]]; + } + + if([name hasPrefix:@"rgb"]) + { + NSString * rgbName = [name stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"rbg() "]]; + NSArray* rbg = [rgbName componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@","]]; + if ([rbg count] != 3) { + // Incorrect syntax + return nil; + } + return [UIColor colorWithRed:[[rbg objectAtIndex:0]floatValue]/255 + green:[[rbg objectAtIndex:1]floatValue] /255 + blue:[[rbg objectAtIndex:2]floatValue] /255 + alpha:1.0]; + } + + static dispatch_once_t predicate; + dispatch_once(&predicate, ^{ + colorLookup = [[NSDictionary alloc] initWithObjectsAndKeys: + @"F0F8FF", @"aliceblue", + @"FAEBD7", @"antiquewhite", + @"00FFFF", @"aqua", + @"7FFFD4", @"aquamarine", + @"F0FFFF", @"azure", + @"F5F5DC", @"beige", + @"FFE4C4", @"bisque", + @"000000", @"black", + @"FFEBCD", @"blanchedalmond", + @"0000FF", @"blue", + @"8A2BE2", @"blueviolet", + @"A52A2A", @"brown", + @"DEB887", @"burlywood", + @"5F9EA0", @"cadetblue", + @"7FFF00", @"chartreuse", + @"D2691E", @"chocolate", + @"FF7F50", @"coral", + @"6495ED", @"cornflowerblue", + @"FFF8DC", @"cornsilk", + @"DC143C", @"crimson", + @"00FFFF", @"cyan", + @"00008B", @"darkblue", + @"008B8B", @"darkcyan", + @"B8860B", @"darkgoldenrod", + @"A9A9A9", @"darkgray", + @"A9A9A9", @"darkgrey", + @"006400", @"darkgreen", + @"BDB76B", @"darkkhaki", + @"8B008B", @"darkmagenta", + @"556B2F", @"darkolivegreen", + @"FF8C00", @"darkorange", + @"9932CC", @"darkorchid", + @"8B0000", @"darkred", + @"E9967A", @"darksalmon", + @"8FBC8F", @"darkseagreen", + @"483D8B", @"darkslateblue", + @"2F4F4F", @"darkslategray", + @"2F4F4F", @"darkslategrey", + @"00CED1", @"darkturquoise", + @"9400D3", @"darkviolet", + @"FF1493", @"deeppink", + @"00BFFF", @"deepskyblue", + @"696969", @"dimgray", + @"696969", @"dimgrey", + @"1E90FF", @"dodgerblue", + @"B22222", @"firebrick", + @"FFFAF0", @"floralwhite", + @"228B22", @"forestgreen", + @"FF00FF", @"fuchsia", + @"DCDCDC", @"gainsboro", + @"F8F8FF", @"ghostwhite", + @"FFD700", @"gold", + @"DAA520", @"goldenrod", + @"808080", @"gray", + @"808080", @"grey", + @"008000", @"green", + @"ADFF2F", @"greenyellow", + @"F0FFF0", @"honeydew", + @"FF69B4", @"hotpink", + @"CD5C5C", @"indianred", + @"4B0082", @"indigo", + @"FFFFF0", @"ivory", + @"F0E68C", @"khaki", + @"E6E6FA", @"lavender", + @"FFF0F5", @"lavenderblush", + @"7CFC00", @"lawngreen", + @"FFFACD", @"lemonchiffon", + @"ADD8E6", @"lightblue", + @"F08080", @"lightcoral", + @"E0FFFF", @"lightcyan", + @"FAFAD2", @"lightgoldenrodyellow", + @"D3D3D3", @"lightgray", + @"D3D3D3", @"lightgrey", + @"90EE90", @"lightgreen", + @"FFB6C1", @"lightpink", + @"FFA07A", @"lightsalmon", + @"20B2AA", @"lightseagreen", + @"87CEFA", @"lightskyblue", + @"778899", @"lightslategray", + @"778899", @"lightslategrey", + @"B0C4DE", @"lightsteelblue", + @"FFFFE0", @"lightyellow", + @"00FF00", @"lime", + @"32CD32", @"limegreen", + @"FAF0E6", @"linen", + @"FF00FF", @"magenta", + @"800000", @"maroon", + @"66CDAA", @"mediumaquamarine", + @"0000CD", @"mediumblue", + @"BA55D3", @"mediumorchid", + @"9370D8", @"mediumpurple", + @"3CB371", @"mediumseagreen", + @"7B68EE", @"mediumslateblue", + @"00FA9A", @"mediumspringgreen", + @"48D1CC", @"mediumturquoise", + @"C71585", @"mediumvioletred", + @"191970", @"midnightblue", + @"F5FFFA", @"mintcream", + @"FFE4E1", @"mistyrose", + @"FFE4B5", @"moccasin", + @"FFDEAD", @"navajowhite", + @"000080", @"navy", + @"FDF5E6", @"oldlace", + @"808000", @"olive", + @"6B8E23", @"olivedrab", + @"FFA500", @"orange", + @"FF4500", @"orangered", + @"DA70D6", @"orchid", + @"EEE8AA", @"palegoldenrod", + @"98FB98", @"palegreen", + @"AFEEEE", @"paleturquoise", + @"D87093", @"palevioletred", + @"FFEFD5", @"papayawhip", + @"FFDAB9", @"peachpuff", + @"CD853F", @"peru", + @"FFC0CB", @"pink", + @"DDA0DD", @"plum", + @"B0E0E6", @"powderblue", + @"800080", @"purple", + @"FF0000", @"red", + @"BC8F8F", @"rosybrown", + @"4169E1", @"royalblue", + @"8B4513", @"saddlebrown", + @"FA8072", @"salmon", + @"F4A460", @"sandybrown", + @"2E8B57", @"seagreen", + @"FFF5EE", @"seashell", + @"A0522D", @"sienna", + @"C0C0C0", @"silver", + @"87CEEB", @"skyblue", + @"6A5ACD", @"slateblue", + @"708090", @"slategray", + @"708090", @"slategrey", + @"FFFAFA", @"snow", + @"00FF7F", @"springgreen", + @"4682B4", @"steelblue", + @"D2B48C", @"tan", + @"008080", @"teal", + @"D8BFD8", @"thistle", + @"FF6347", @"tomato", + @"40E0D0", @"turquoise", + @"EE82EE", @"violet", + @"F5DEB3", @"wheat", + @"FFFFFF", @"white", + @"F5F5F5", @"whitesmoke", + @"FFFF00", @"yellow", + @"9ACD32", @"yellowgreen", + nil]; + }); + + NSString *hexString = [colorLookup objectForKey:[name lowercaseString]]; + + return [UIColor colorWithHexString:hexString]; +} + +- (NSString *)htmlHexString +{ + CGColorRef color = self.CGColor; + size_t count = CGColorGetNumberOfComponents(color); + const CGFloat *components = CGColorGetComponents(color); + + static NSString *stringFormat = @"%02x%02x%02x"; + + // Grayscale + if (count == 2) + { + NSUInteger white = (NSUInteger)(components[0] * (CGFloat)255); + return [NSString stringWithFormat:stringFormat, white, white, white]; + } + + // RGB + else if (count == 4) + { + return [NSString stringWithFormat:stringFormat, (NSUInteger)(components[0] * (CGFloat)255), + (NSUInteger)(components[1] * (CGFloat)255), (NSUInteger)(components[2] * (CGFloat)255)]; + } + + // Unsupported color space + return nil; +} + +- (CGFloat)alphaComponent +{ + return CGColorGetAlpha(self.CGColor); +} + +@end + +#else + +@implementation NSColor (HTML) + ++ (NSColor *)colorWithHexString:(NSString *)hex +{ + if ([hex length]!=6 && [hex length]!=3) + { + return nil; + } + + NSUInteger digits = [hex length]/3; + CGFloat maxValue = (digits==1)?15.0:255.0; + + CGFloat red = [[hex substringWithRange:NSMakeRange(0, digits)] integerValueFromHex]/maxValue; + CGFloat green = [[hex substringWithRange:NSMakeRange(digits, digits)] integerValueFromHex]/maxValue; + CGFloat blue = [[hex substringWithRange:NSMakeRange(2*digits, digits)] integerValueFromHex]/maxValue; + + return [NSColor colorWithDeviceRed:red green:green blue:blue alpha:1.0]; +} + + +// Source: http://www.w3schools.com/html/html_colornames.asp ++ (NSColor *)colorWithHTMLName:(NSString *)name +{ + if ([name hasPrefix:@"#"]) + { + return [NSColor colorWithHexString:[name substringFromIndex:1]]; + } + + if ([name hasPrefix:@"rgba"]) { + NSString *rgbaName = [name stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"rgba() "]]; + NSArray *rgba = [rgbaName componentsSeparatedByString:@","]; + + if ([rgba count] != 4) { + // Incorrect syntax + return nil; + } + + return [NSColor colorWithDeviceRed:[[rgba objectAtIndex:0] floatValue] / 255 + green:[[rgba objectAtIndex:1] floatValue] / 255 + blue:[[rgba objectAtIndex:2] floatValue] / 255 + alpha:[[rgba objectAtIndex:3] floatValue]]; + } + + if([name hasPrefix:@"rgb"]) + { + NSString * rgbName = [name stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"rbg() "]]; + NSArray* rbg = [rgbName componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@","]]; + if ([rbg count] != 3) { + // Incorrect syntax + return nil; + } + return [NSColor colorWithDeviceRed:[[rbg objectAtIndex:0]floatValue]/255 + green:[[rbg objectAtIndex:1]floatValue] /255 + blue:[[rbg objectAtIndex:2]floatValue] /255 + alpha:1.0]; + } + + static dispatch_once_t predicate; + dispatch_once(&predicate, ^{ + colorLookup = [[NSDictionary alloc] initWithObjectsAndKeys: + @"F0F8FF", @"aliceblue", + @"FAEBD7", @"antiquewhite", + @"00FFFF", @"aqua", + @"7FFFD4", @"aquamarine", + @"F0FFFF", @"azure", + @"F5F5DC", @"beige", + @"FFE4C4", @"bisque", + @"000000", @"black", + @"FFEBCD", @"blanchedalmond", + @"0000FF", @"blue", + @"8A2BE2", @"blueviolet", + @"A52A2A", @"brown", + @"DEB887", @"burlywood", + @"5F9EA0", @"cadetblue", + @"7FFF00", @"chartreuse", + @"D2691E", @"chocolate", + @"FF7F50", @"coral", + @"6495ED", @"cornflowerblue", + @"FFF8DC", @"cornsilk", + @"DC143C", @"crimson", + @"00FFFF", @"cyan", + @"00008B", @"darkblue", + @"008B8B", @"darkcyan", + @"B8860B", @"darkgoldenrod", + @"A9A9A9", @"darkgray", + @"A9A9A9", @"darkgrey", + @"006400", @"darkgreen", + @"BDB76B", @"darkkhaki", + @"8B008B", @"darkmagenta", + @"556B2F", @"darkolivegreen", + @"FF8C00", @"darkorange", + @"9932CC", @"darkorchid", + @"8B0000", @"darkred", + @"E9967A", @"darksalmon", + @"8FBC8F", @"darkseagreen", + @"483D8B", @"darkslateblue", + @"2F4F4F", @"darkslategray", + @"2F4F4F", @"darkslategrey", + @"00CED1", @"darkturquoise", + @"9400D3", @"darkviolet", + @"FF1493", @"deeppink", + @"00BFFF", @"deepskyblue", + @"696969", @"dimgray", + @"696969", @"dimgrey", + @"1E90FF", @"dodgerblue", + @"B22222", @"firebrick", + @"FFFAF0", @"floralwhite", + @"228B22", @"forestgreen", + @"FF00FF", @"fuchsia", + @"DCDCDC", @"gainsboro", + @"F8F8FF", @"ghostwhite", + @"FFD700", @"gold", + @"DAA520", @"goldenrod", + @"808080", @"gray", + @"808080", @"grey", + @"008000", @"green", + @"ADFF2F", @"greenyellow", + @"F0FFF0", @"honeydew", + @"FF69B4", @"hotpink", + @"CD5C5C", @"indianred", + @"4B0082", @"indigo", + @"FFFFF0", @"ivory", + @"F0E68C", @"khaki", + @"E6E6FA", @"lavender", + @"FFF0F5", @"lavenderblush", + @"7CFC00", @"lawngreen", + @"FFFACD", @"lemonchiffon", + @"ADD8E6", @"lightblue", + @"F08080", @"lightcoral", + @"E0FFFF", @"lightcyan", + @"FAFAD2", @"lightgoldenrodyellow", + @"D3D3D3", @"lightgray", + @"D3D3D3", @"lightgrey", + @"90EE90", @"lightgreen", + @"FFB6C1", @"lightpink", + @"FFA07A", @"lightsalmon", + @"20B2AA", @"lightseagreen", + @"87CEFA", @"lightskyblue", + @"778899", @"lightslategray", + @"778899", @"lightslategrey", + @"B0C4DE", @"lightsteelblue", + @"FFFFE0", @"lightyellow", + @"00FF00", @"lime", + @"32CD32", @"limegreen", + @"FAF0E6", @"linen", + @"FF00FF", @"magenta", + @"800000", @"maroon", + @"66CDAA", @"mediumaquamarine", + @"0000CD", @"mediumblue", + @"BA55D3", @"mediumorchid", + @"9370D8", @"mediumpurple", + @"3CB371", @"mediumseagreen", + @"7B68EE", @"mediumslateblue", + @"00FA9A", @"mediumspringgreen", + @"48D1CC", @"mediumturquoise", + @"C71585", @"mediumvioletred", + @"191970", @"midnightblue", + @"F5FFFA", @"mintcream", + @"FFE4E1", @"mistyrose", + @"FFE4B5", @"moccasin", + @"FFDEAD", @"navajowhite", + @"000080", @"navy", + @"FDF5E6", @"oldlace", + @"808000", @"olive", + @"6B8E23", @"olivedrab", + @"FFA500", @"orange", + @"FF4500", @"orangered", + @"DA70D6", @"orchid", + @"EEE8AA", @"palegoldenrod", + @"98FB98", @"palegreen", + @"AFEEEE", @"paleturquoise", + @"D87093", @"palevioletred", + @"FFEFD5", @"papayawhip", + @"FFDAB9", @"peachpuff", + @"CD853F", @"peru", + @"FFC0CB", @"pink", + @"DDA0DD", @"plum", + @"B0E0E6", @"powderblue", + @"800080", @"purple", + @"FF0000", @"red", + @"BC8F8F", @"rosybrown", + @"4169E1", @"royalblue", + @"8B4513", @"saddlebrown", + @"FA8072", @"salmon", + @"F4A460", @"sandybrown", + @"2E8B57", @"seagreen", + @"FFF5EE", @"seashell", + @"A0522D", @"sienna", + @"C0C0C0", @"silver", + @"87CEEB", @"skyblue", + @"6A5ACD", @"slateblue", + @"708090", @"slategray", + @"708090", @"slategrey", + @"FFFAFA", @"snow", + @"00FF7F", @"springgreen", + @"4682B4", @"steelblue", + @"D2B48C", @"tan", + @"008080", @"teal", + @"D8BFD8", @"thistle", + @"FF6347", @"tomato", + @"40E0D0", @"turquoise", + @"EE82EE", @"violet", + @"F5DEB3", @"wheat", + @"FFFFFF", @"white", + @"F5F5F5", @"whitesmoke", + @"FFFF00", @"yellow", + @"9ACD32", @"yellowgreen", + nil]; + }); + + NSString *hexString = [colorLookup objectForKey:[name lowercaseString]]; + + return [NSColor colorWithHexString:hexString]; +} + +- (NSString *)htmlHexString +{ + CGFloat red = self.redComponent; + CGFloat blue = self.blueComponent; + CGFloat green = self.greenComponent; + + static NSString *stringFormat = @"%02x%02x%02x"; + + return [NSString stringWithFormat:stringFormat, (NSUInteger)(red * (CGFloat)255), + (NSUInteger)(green * (CGFloat)255), (NSUInteger)(blue * (CGFloat)255)]; +} + +#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_7 ++ (NSColor *)colorWithCGColor:(CGColorRef)cgColor +{ + size_t count = CGColorGetNumberOfComponents(cgColor); + const CGFloat *components = CGColorGetComponents(cgColor); + + // Grayscale + if (count == 2) + { + return [NSColor colorWithDeviceWhite:components[0] alpha:components[1]]; + } + + // RGB + else if (count == 4) + { + return [NSColor colorWithDeviceRed:components[0] green:components[1] blue:components[2] alpha:components[3]]; + } + + // neigher grayscale nor rgba + return nil; +} + +// From https://gist.github.com/1593255 +- (CGColorRef)CGColor +{ + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + + NSColor *selfCopy = [self colorUsingColorSpaceName:NSDeviceRGBColorSpace]; + + CGFloat colorValues[4]; + [selfCopy getRed:&colorValues[0] green:&colorValues[1] blue:&colorValues[2] alpha:&colorValues[3]]; + + CGColorRef color = CGColorCreate(colorSpace, colorValues); + + CGColorSpaceRelease(colorSpace); + + return color; +} +#endif + +@end + +#endif diff --git a/Pods/DTCoreText/Core/Source/DTCompatibility.h b/Pods/DTCoreText/Core/Source/DTCompatibility.h new file mode 100644 index 00000000..0b57b393 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCompatibility.h @@ -0,0 +1,61 @@ +// +// DTCompatibility.h +// DTCoreText +// +// Created by Oliver Letterer on 09.04.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +// DTColor is UIColor on iOS, NSColor on Mac +#if TARGET_OS_IPHONE +@compatibility_alias DTColor UIColor; +#else +@compatibility_alias DTColor NSColor; +#endif + +// DTImage is UIImage on iOS, NSImage on Mac +#if TARGET_OS_IPHONE +@compatibility_alias DTImage UIImage; +#else +@compatibility_alias DTImage NSImage; +#endif + +// DTFont is UIFont on iOS, NSFont on Mac +#if TARGET_OS_IPHONE +@compatibility_alias DTFont UIFont; +#else +@compatibility_alias DTFont NSFont; +#endif + +// DTEdgeInsets is UIEdgeInsets on iOS, NSEdgeInsets on Mac +#if TARGET_OS_IPHONE +#define DTEdgeInsets UIEdgeInsets +#define DTEdgeInsetsMake(a, b, c, d) UIEdgeInsetsMake(a, b, c, d) +#else +#define DTEdgeInsets NSEdgeInsets +#define DTEdgeInsetsMake(a, b, c, d) NSEdgeInsetsMake(a, b, c, d) + +// These may be out of place here. Feel free to move them! +// Sourced from https://github.com/andrep/RMModelObject +static inline NSString* NSStringFromCGRect(const CGRect rect) +{ + return NSStringFromRect(NSRectFromCGRect(rect)); +} + +static inline NSString* NSStringFromCGSize(const CGSize size) +{ + return NSStringFromSize(NSSizeFromCGSize(size)); +} + +static inline NSString* NSStringFromCGPoint(const CGPoint point) +{ + return NSStringFromPoint(NSPointFromCGPoint(point)); +} + +#define NSTextAlignmentLeft NSLeftTextAlignment +#define NSTextAlignmentRight NSRightTextAlignment +#define NSTextAlignmentCenter NSCenterTextAlignment +#define NSTextAlignmentJustified NSJustifiedTextAlignment +#define NSTextAlignmentNatural NSNaturalTextAlignment + +#endif diff --git a/Pods/DTCoreText/Core/Source/DTCoreText.h b/Pods/DTCoreText/Core/Source/DTCoreText.h new file mode 100644 index 00000000..448a08a6 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCoreText.h @@ -0,0 +1,67 @@ +#if TARGET_OS_IPHONE +#import +#elif TARGET_OS_MAC +#import +#endif + +// global constants +#import "DTCoreTextConstants.h" +#import "DTCompatibility.h" + +#import "DTColor+HTML.h" +#import "DTImage+HTML.h" + +// common utilities +#import "CGUtils.h" +#if TARGET_OS_IPHONE +#import "DTCoreTextFunctions.h" +#endif + +// common classes +#import "DTCSSListStyle.h" +#import "DTTextBlock.h" +#import "DTCSSStylesheet.h" +#import "DTCoreTextFontDescriptor.h" +#import "DTHTMLElement.h" +#import "DTTextAttachment.h" +#import "NSCharacterSet+HTML.h" +#import "NSScanner+HTML.h" +#import "NSMutableString+HTML.h" +#import "NSString+CSS.h" +#import "NSString+HTML.h" +#import "NSString+Paragraphs.h" +#import "DTCoreTextParagraphStyle.h" +#import "NSMutableAttributedString+HTML.h" +#import "NSAttributedString+HTML.h" +#import "NSAttributedString+SmallCaps.h" +#import "NSAttributedString+DTCoreText.h" +#import "DTHTMLAttributedStringBuilder.h" + +// parsing classes +#import "DTHTMLParserNode.h" +#import "DTHTMLParserTextNode.h" + + +// These classes only work with UIKit on iOS +#if TARGET_OS_IPHONE + +#import "DTLazyImageView.h" +#import "DTLinkButton.h" +#import "DTWebVideoView.h" +#import "NSAttributedStringRunDelegates.h" + +#import "DTAttributedTextCell.h" +#import "DTAttributedTextContentView.h" +#import "DTAttributedTextView.h" +#import "DTCoreTextFontCollection.h" +#import "DTCoreTextGlyphRun.h" +#import "DTCoreTextLayoutFrame.h" +#import "DTCoreTextLayoutLine.h" +#import "DTCoreTextLayouter.h" + +#import "UIFont+DTCoreText.h" + +#endif + + +#define DT_ADD_FONT_ON_ATTACHMENTS diff --git a/Pods/DTCoreText/Core/Source/DTCoreTextConstants.h b/Pods/DTCoreText/Core/Source/DTCoreTextConstants.h new file mode 100644 index 00000000..39dca26e --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCoreTextConstants.h @@ -0,0 +1,79 @@ +// unicode characters + +#define UNICODE_OBJECT_PLACEHOLDER @"\ufffc" +#define UNICODE_LINE_FEED @"\u2028" +#define UNICODE_NON_BREAKING_SPACE @"\u00a0" + +// standard options + +#if TARGET_OS_IPHONE +extern NSString * const NSBaseURLDocumentOption; +extern NSString * const NSTextEncodingNameDocumentOption; +extern NSString * const NSTextSizeMultiplierDocumentOption; +extern NSString * const NSAttachmentAttributeName; +#endif + +// custom options + +extern NSString * const DTMaxImageSize; +extern NSString * const DTDefaultFontFamily; +extern NSString * const DTDefaultTextColor; +extern NSString * const DTDefaultLinkColor; +extern NSString * const DTDefaultLinkDecoration; +extern NSString * const DTDefaultTextAlignment; +extern NSString * const DTDefaultLineHeightMultiplier; +extern NSString * const DTDefaultLineHeightMultiplier; +extern NSString * const DTDefaultFirstLineHeadIndent; +extern NSString * const DTDefaultHeadIndent; +extern NSString * const DTDefaultListIndent; +extern NSString * const DTDefaultStyleSheet; +extern NSString * const DTUseiOS6Attributes; +extern NSString * const DTWillFlushBlockCallBack; + +// attributed string attribute constants + +extern NSString * const DTTextListsAttribute; +extern NSString * const DTAttachmentParagraphSpacingAttribute; +extern NSString * const DTLinkAttribute; +extern NSString * const DTAnchorAttribute; +extern NSString * const DTGUIDAttribute; +extern NSString * const DTHeaderLevelAttribute; +extern NSString * const DTPreserveNewlinesAttribute; +extern NSString * const DTStrikeOutAttribute; +extern NSString * const DTBackgroundColorAttribute; +extern NSString * const DTShadowsAttribute; +extern NSString * const DTHorizontalRuleStyleAttribute; +extern NSString * const DTTextBlocksAttribute; +extern NSString * const DTFieldAttribute; + +// iOS 6 compatibility +extern BOOL ___useiOS6Attributes; + +// macros + +#define IS_WHITESPACE(_c) (_c == ' ' || _c == '\t' || _c == 0xA || _c == 0xB || _c == 0xC || _c == 0xD || _c == 0x85) + +// types + +typedef enum +{ + DTHTMLElementDisplayStyleInline = 0, // default + DTHTMLElementDisplayStyleNone, + DTHTMLElementDisplayStyleBlock, + DTHTMLElementDisplayStyleListItem, + DTHTMLElementDisplayStyleTable, +} DTHTMLElementDisplayStyle; + +typedef enum +{ + DTHTMLElementFloatStyleNone = 0, + DTHTMLElementFloatStyleLeft, + DTHTMLElementFloatStyleRight +} DTHTMLElementFloatStyle; + +typedef enum +{ + DTHTMLElementFontVariantInherit = 0, + DTHTMLElementFontVariantNormal, + DTHTMLElementFontVariantSmallCaps +} DTHTMLElementFontVariant; \ No newline at end of file diff --git a/Pods/DTCoreText/Core/Source/DTCoreTextConstants.m b/Pods/DTCoreText/Core/Source/DTCoreTextConstants.m new file mode 100644 index 00000000..cd499746 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCoreTextConstants.m @@ -0,0 +1,46 @@ +#import "DTCoreTextConstants.h" + +// standard options + +#if TARGET_OS_IPHONE +NSString * const NSBaseURLDocumentOption = @"NSBaseURLDocumentOption"; +NSString * const NSTextEncodingNameDocumentOption = @"NSTextEncodingNameDocumentOption"; +NSString * const NSTextSizeMultiplierDocumentOption = @"NSTextSizeMultiplierDocumentOption"; +NSString * const NSAttachmentAttributeName = @"NSAttachmentAttributeName"; +#endif + +// custom options + +NSString * const DTMaxImageSize = @"DTMaxImageSize"; +NSString * const DTDefaultFontFamily = @"DTDefaultFontFamily"; +NSString * const DTDefaultTextColor = @"DTDefaultTextColor"; +NSString * const DTDefaultLinkColor = @"DTDefaultLinkColor"; +NSString * const DTDefaultLinkDecoration = @"DTDefaultLinkDecoration"; +NSString * const DTDefaultTextAlignment = @"DTDefaultTextAlignment"; +NSString * const DTDefaultLineHeightMultiplier = @"DTDefaultLineHeightMultiplier"; +NSString * const DTDefaultFirstLineHeadIndent = @"DTDefaultFirstLineHeadIndent"; +NSString * const DTDefaultHeadIndent = @"DTDefaultHeadIndent"; +NSString * const DTDefaultListIndent = @"DTDefaultListIndent"; +NSString * const DTDefaultStyleSheet = @"DTDefaultStyleSheet"; +NSString * const DTUseiOS6Attributes = @"DTUseiOS6Attributes"; +NSString * const DTWillFlushBlockCallBack = @"DTWillFlushBlockCallBack"; + +// attributed string attribute constants + +NSString * const DTTextListsAttribute = @"DTTextLists"; +NSString * const DTAttachmentParagraphSpacingAttribute = @"DTAttachmentParagraphSpacing"; +NSString * const DTLinkAttribute = @"DTLink"; +NSString * const DTAnchorAttribute = @"DTAnchor"; +NSString * const DTGUIDAttribute = @"DTGUID"; +NSString * const DTHeaderLevelAttribute = @"DTHeaderLevel"; +NSString * const DTPreserveNewlinesAttribute = @"DTPreserveNewlines"; +NSString * const DTStrikeOutAttribute = @"NSStrikethrough"; +NSString * const DTBackgroundColorAttribute = @"DTBackgroundColor"; +NSString * const DTShadowsAttribute = @"DTShadows"; +NSString * const DTHorizontalRuleStyleAttribute = @"DTHorizontalRuleStyle"; +NSString * const DTTextBlocksAttribute = @"DTTextBlocks"; +NSString * const DTFieldAttribute = @"DTField"; + +// iOS 6 compatibility + +BOOL ___useiOS6Attributes = NO; // this gets set globally by DTHTMLAttributedStringBuilder \ No newline at end of file diff --git a/Pods/DTCoreText/Core/Source/DTCoreTextFontCollection.h b/Pods/DTCoreText/Core/Source/DTCoreTextFontCollection.h new file mode 100644 index 00000000..2f9062e6 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCoreTextFontCollection.h @@ -0,0 +1,26 @@ +// +// DTCoreTextFontCollection.h +// CoreTextExtensions +// +// Created by Oliver Drobnik on 5/23/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + + + +@class DTCoreTextFontDescriptor; + + +@interface DTCoreTextFontCollection : NSObject + ++ (DTCoreTextFontCollection *)availableFontsCollection; + +- (id)initWithAvailableFonts; + +- (NSArray *)fontFamilyNames; +- (NSArray *)fontDescriptors; + +- (DTCoreTextFontDescriptor *)matchingFontDescriptorForFontDescriptor:(DTCoreTextFontDescriptor *)descriptor; + + +@end diff --git a/Pods/DTCoreText/Core/Source/DTCoreTextFontCollection.m b/Pods/DTCoreText/Core/Source/DTCoreTextFontCollection.m new file mode 100644 index 00000000..0665f29c --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCoreTextFontCollection.m @@ -0,0 +1,171 @@ +// +// DTCoreTextFontCollection.m +// CoreTextExtensions +// +// Created by Oliver Drobnik on 5/23/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import "DTCoreTextFontCollection.h" +#import "DTCoreTextFontDescriptor.h" + +#if TARGET_OS_IPHONE +#import +#elif TARGET_OS_MAC +#import +#endif + +@interface DTCoreTextFontCollection () + +@property (nonatomic, strong) NSArray *fontDescriptors; +@property (nonatomic, strong) NSCache *fontMatchCache; + +@end + +static DTCoreTextFontCollection *_availableFontsCollection = nil; + + +@implementation DTCoreTextFontCollection +{ + NSArray *_fontDescriptors; + NSCache *_fontMatchCache; +} + ++ (DTCoreTextFontCollection *)availableFontsCollection +{ + static dispatch_once_t predicate; + + dispatch_once(&predicate, ^{ + _availableFontsCollection = [[DTCoreTextFontCollection alloc] initWithAvailableFonts]; + }); + + return _availableFontsCollection; +} + +- (id)initWithAvailableFonts +{ + self = [super init]; + + if (self) + { + + } + + return self; +} + + +- (DTCoreTextFontDescriptor *)matchingFontDescriptorForFontDescriptor:(DTCoreTextFontDescriptor *)descriptor +{ + DTCoreTextFontDescriptor *firstMatch = nil; + NSNumber *cacheKey = [NSString stringWithFormat:@"fontFamily BEGINSWITH[cd] %@ and boldTrait == %d and italicTrait == %d", descriptor.fontFamily, descriptor.boldTrait, descriptor.italicTrait]; + + // try cache + firstMatch = [self.fontMatchCache objectForKey:cacheKey]; + + if (firstMatch) + { + DTCoreTextFontDescriptor *retMatch = [firstMatch copy]; + retMatch.pointSize = descriptor.pointSize; + return retMatch; + } + + // need to search + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"fontFamily BEGINSWITH[cd] %@ and boldTrait == %d and italicTrait == %d", descriptor.fontFamily, descriptor.boldTrait, descriptor.italicTrait]; + + NSArray *matchingDescriptors = [self.fontDescriptors filteredArrayUsingPredicate:predicate]; + + //NSLog(@"%@", matchingDescriptors); + + if ([matchingDescriptors count]) + { + firstMatch = [matchingDescriptors objectAtIndex:0]; + [self.fontMatchCache setObject:firstMatch forKey:cacheKey]; + + DTCoreTextFontDescriptor *retMatch = [firstMatch copy]; + + retMatch.pointSize = descriptor.pointSize; + return retMatch; + } + + return nil; +} + +#pragma mark Properties + +- (NSArray *)fontDescriptors +{ + if (!_fontDescriptors) + { + // try caches + + NSString *cachesPath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"FontDescriptors.cache"]; + + self.fontDescriptors = nil;//[NSKeyedUnarchiver unarchiveObjectWithFile:cachesPath]; + + if (!_fontDescriptors) + { + CTFontCollectionRef fonts = CTFontCollectionCreateFromAvailableFonts(NULL); + + CFArrayRef matchingFonts = CTFontCollectionCreateMatchingFontDescriptors(fonts); + + if (matchingFonts) + { + + // convert all to our objects + NSMutableArray *tmpArray = [[NSMutableArray alloc] init]; + + for (NSInteger i=0; i + +// sets the font face name to use for a specific font family ++ (void)setSmallCapsFontName:(NSString *)fontName forFontFamily:(NSString *)fontFamily bold:(BOOL)bold italic:(BOOL)italic; ++ (NSString *)smallCapsFontNameforFontFamily:(NSString *)fontFamily bold:(BOOL)bold italic:(BOOL)italic; + +// overriding typefaces for families ++ (void)setOverrideFontName:(NSString *)fontName forFontFamily:(NSString *)fontFamily bold:(BOOL)bold italic:(BOOL)italic; ++ (NSString *)overrideFontNameforFontFamily:(NSString *)fontFamily bold:(BOOL)bold italic:(BOOL)italic; + ++ (DTCoreTextFontDescriptor *)fontDescriptorWithFontAttributes:(NSDictionary *)attributes; ++ (DTCoreTextFontDescriptor *)fontDescriptorForCTFont:(CTFontRef)ctFont; + +- (id)initWithFontAttributes:(NSDictionary *)attributes; +- (id)initWithCTFontDescriptor:(CTFontDescriptorRef)ctFontDescriptor; +- (id)initWithCTFont:(CTFontRef)ctFont; + +- (void)setFontAttributes:(NSDictionary *)newAttributes; + +- (CTFontSymbolicTraits)symbolicTraits; +- (NSDictionary *)fontAttributes; + +- (CTFontRef)newMatchingFont; + +- (BOOL)supportsNativeSmallCaps; + +- (NSString *)cssStyleRepresentation; + +@property (nonatomic, copy) NSString *fontFamily; +@property (nonatomic, copy) NSString *fontName; + +@property (nonatomic) CGFloat pointSize; + +@property (nonatomic) BOOL boldTrait; +@property (nonatomic) BOOL italicTrait; +@property (nonatomic) BOOL expandedTrait; +@property (nonatomic) BOOL condensedTrait; +@property (nonatomic) BOOL monospaceTrait; +@property (nonatomic) BOOL verticalTrait; +@property (nonatomic) BOOL UIoptimizedTrait; + +@property (nonatomic) CTFontSymbolicTraits symbolicTraits; + +@property (nonatomic) CTFontStylisticClass stylisticClass; + +@property (nonatomic) BOOL smallCapsFeature; + + +@end diff --git a/Pods/DTCoreText/Core/Source/DTCoreTextFontDescriptor.m b/Pods/DTCoreText/Core/Source/DTCoreTextFontDescriptor.m new file mode 100644 index 00000000..84c01f6c --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCoreTextFontDescriptor.m @@ -0,0 +1,809 @@ +// +// DTCoreTextFontDescriptor.m +// CoreTextExtensions +// +// Created by Oliver Drobnik on 1/26/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import "DTCoreTextFontDescriptor.h" +#import "DTVersion.h" + +static NSCache *_fontCache = nil; +static NSMutableDictionary *_fontOverrides = nil; +static dispatch_queue_t _fontQueue; + +// adds "STHeitiSC-Light" font for cascading fix on iOS 5 +static BOOL _needsChineseFontCascadeFix = NO; + +@implementation DTCoreTextFontDescriptor +{ + NSString *_fontFamily; + NSString *_fontName; + + CGFloat _pointSize; + + CTFontSymbolicTraits _stylisticTraits; + CTFontStylisticClass _stylisticClass; + + BOOL _smallCapsFeature; +} + ++ (void)initialize +{ + // only this class (and not subclasses) do this + if (self == [DTCoreTextFontDescriptor class]) + { + _fontCache = [[NSCache alloc] init]; + + _fontQueue = dispatch_queue_create("DTCoreTextFontDescriptor", 0); + + // init/load of overrides + _fontOverrides = [[NSMutableDictionary alloc] init]; + + NSString *path = [[NSBundle mainBundle] pathForResource:@"DTCoreTextFontOverrides" ofType:@"plist"]; + NSArray *fileArray = [NSArray arrayWithContentsOfFile:path]; + + for (NSDictionary *oneOverride in fileArray) + { + NSString *fontFamily = [oneOverride objectForKey:@"FontFamily"]; + NSString *overrideFontName = [oneOverride objectForKey:@"OverrideFontName"]; + BOOL bold = [[oneOverride objectForKey:@"Bold"] boolValue]; + BOOL italic = [[oneOverride objectForKey:@"Italic"] boolValue]; + BOOL smallcaps = [[oneOverride objectForKey:@"SmallCaps"] boolValue]; + + if (smallcaps) + { + [DTCoreTextFontDescriptor setSmallCapsFontName:overrideFontName forFontFamily:fontFamily bold:bold italic:italic]; + } + else + { + [DTCoreTextFontDescriptor setOverrideFontName:overrideFontName forFontFamily:fontFamily bold:bold italic:italic]; + } + } + } + +#if TARGET_OS_IPHONE + // workaround for iOS 5.x bug: global font cascade table has incorrect bold font for Chinese characters in Chinese locale + + + DTVersion *version = [DTVersion osVersion]; + + // seems to be fixed in iOS 6 + if (version.major<6) + { + _needsChineseFontCascadeFix = YES; + } +#endif +} + ++ (void)setSmallCapsFontName:(NSString *)fontName forFontFamily:(NSString *)fontFamily bold:(BOOL)bold italic:(BOOL)italic +{ + NSString *key = [NSString stringWithFormat:@"%@-%d-%d-smallcaps", fontFamily, bold, italic]; + [_fontOverrides setObject:fontName forKey:key]; +} + ++ (NSString *)smallCapsFontNameforFontFamily:(NSString *)fontFamily bold:(BOOL)bold italic:(BOOL)italic +{ + NSString *key = [NSString stringWithFormat:@"%@-%d-%d-smallcaps", fontFamily, bold, italic]; + return [_fontOverrides objectForKey:key]; +} + ++ (void)setOverrideFontName:(NSString *)fontName forFontFamily:(NSString *)fontFamily bold:(BOOL)bold italic:(BOOL)italic +{ + NSString *key = [NSString stringWithFormat:@"%@-%d-%d-override", fontFamily, bold, italic]; + [_fontOverrides setObject:fontName forKey:key]; +} + ++ (NSString *)overrideFontNameforFontFamily:(NSString *)fontFamily bold:(BOOL)bold italic:(BOOL)italic +{ + NSString *key = [NSString stringWithFormat:@"%@-%d-%d-override", fontFamily, bold, italic]; + return [_fontOverrides objectForKey:key]; +} + +#pragma mark Initializing + ++ (DTCoreTextFontDescriptor *)fontDescriptorWithFontAttributes:(NSDictionary *)attributes +{ + return [[DTCoreTextFontDescriptor alloc] initWithFontAttributes:attributes]; +} + ++ (DTCoreTextFontDescriptor *)fontDescriptorForCTFont:(CTFontRef)ctFont +{ + return [[DTCoreTextFontDescriptor alloc] initWithCTFont:ctFont]; +} + +- (id)initWithFontAttributes:(NSDictionary *)attributes +{ + self = [super init]; + if (self) + { + [self setFontAttributes:attributes]; + } + + return self; +} + +- (id)initWithCTFontDescriptor:(CTFontDescriptorRef)ctFontDescriptor +{ + self = [super init]; + if (self) + { + CFDictionaryRef dict = CTFontDescriptorCopyAttributes(ctFontDescriptor); + + CFDictionaryRef traitsDict = CTFontDescriptorCopyAttribute(ctFontDescriptor, kCTFontTraitsAttribute); + CTFontSymbolicTraits traitsValue = [[(__bridge NSDictionary *)traitsDict objectForKey:(id)kCTFontSymbolicTrait] unsignedIntValue]; + CFRelease(traitsDict); + + self.symbolicTraits = traitsValue; + + [self setFontAttributes:CFBridgingRelease(dict)]; + + // also get family name + + CFStringRef familyName = CTFontDescriptorCopyAttribute(ctFontDescriptor, kCTFontFamilyNameAttribute); + self.fontFamily = CFBridgingRelease(familyName); + } + + return self; +} + +- (id)initWithCTFont:(CTFontRef)ctFont +{ + self = [super init]; + if (self) + { + CTFontDescriptorRef fd = CTFontCopyFontDescriptor(ctFont); + CFDictionaryRef dict = CTFontDescriptorCopyAttributes(fd); + + CFDictionaryRef traitsDict = CTFontDescriptorCopyAttribute(fd, kCTFontTraitsAttribute); + CTFontSymbolicTraits traitsValue = [[(__bridge NSDictionary *)traitsDict objectForKey:(id)kCTFontSymbolicTrait] unsignedIntValue]; + CFRelease(traitsDict); + CFRelease(fd); + + self.symbolicTraits = traitsValue; + + [self setFontAttributes:CFBridgingRelease(dict)]; + //CFRelease(dict); + + // also get the family while we're at it + CFStringRef cfStr = CTFontCopyFamilyName(ctFont); + + if (cfStr) + { + self.fontFamily = CFBridgingRelease(cfStr); + } + } + + return self; +} + +- (NSString *)description +{ + NSMutableString *string = [NSMutableString string]; + + [string appendFormat:@"<%@ ", [self class]]; + + + if (self.fontName) + { + [string appendFormat:@"name:\'%@\' ", self.fontName]; + } + + if (_fontFamily) + { + [string appendFormat:@"family:\'%@\' ", _fontFamily]; + } + + NSMutableArray *tmpTraits = [NSMutableArray array]; + + if (_stylisticTraits & kCTFontBoldTrait) + { + [tmpTraits addObject:@"bold"]; + } + + if (_stylisticTraits & kCTFontItalicTrait) + { + [tmpTraits addObject:@"italic"]; + } + + if (_stylisticTraits & kCTFontMonoSpaceTrait) + { + [tmpTraits addObject:@"monospace"]; + } + + if (_stylisticTraits & kCTFontCondensedTrait) + { + [tmpTraits addObject:@"condensed"]; + } + + if (_stylisticTraits & kCTFontExpandedTrait) + { + [tmpTraits addObject:@"expanded"]; + } + + if (_stylisticTraits & kCTFontVerticalTrait) + { + [tmpTraits addObject:@"vertical"]; + } + + if (_stylisticTraits & kCTFontUIOptimizedTrait) + { + [tmpTraits addObject:@"UI optimized"]; + } + + + if ([tmpTraits count]) + { + [string appendString:@"attributes:"]; + [string appendString:[tmpTraits componentsJoinedByString:@", "]]; + } + + [string appendString:@">"]; + + return string; +} + +- (NSDictionary *)fontAttributes +{ + NSMutableDictionary *tmpDict = [NSMutableDictionary dictionary]; + + NSMutableDictionary *traitsDict = [NSMutableDictionary dictionary]; + + CTFontSymbolicTraits theSymbolicTraits = _stylisticTraits | _stylisticClass; + + if (theSymbolicTraits) + { + [traitsDict setObject:[NSNumber numberWithUnsignedInt:theSymbolicTraits] forKey:(id)kCTFontSymbolicTrait]; + } + + if ([traitsDict count]) + { + [tmpDict setObject:traitsDict forKey:(id)kCTFontTraitsAttribute]; + } + + if (_fontFamily) + { + [tmpDict setObject:_fontFamily forKey:(id)kCTFontFamilyNameAttribute]; + } + + if (_fontName) + { + [tmpDict setObject:_fontName forKey:(id)kCTFontNameAttribute]; + } + + // we need size because that's what makes a font unique, for searching it's ignored anyway + [tmpDict setObject:[NSNumber numberWithFloat:_pointSize] forKey:(id)kCTFontSizeAttribute]; + + + if (_smallCapsFeature) + { + NSNumber *typeNum = [NSNumber numberWithInteger:3]; + NSNumber *selNum = [NSNumber numberWithInteger:3]; + + NSDictionary *setting = [NSDictionary dictionaryWithObjectsAndKeys:selNum, (id)kCTFontFeatureSelectorIdentifierKey, + typeNum, (id)kCTFontFeatureTypeIdentifierKey, nil]; + + NSArray *featureSettings = [NSArray arrayWithObject:setting]; + + [tmpDict setObject:featureSettings forKey:(id)kCTFontFeatureSettingsAttribute]; + } + + if (!self.boldTrait && _needsChineseFontCascadeFix) + { + CTFontDescriptorRef desc = CTFontDescriptorCreateWithNameAndSize(CFSTR("STHeitiSC-Light"), self.pointSize); + + [tmpDict setObject:[NSArray arrayWithObject:(__bridge_transfer id) desc] forKey:(id)kCTFontCascadeListAttribute]; + } + + //return [NSDictionary dictionaryWithDictionary:tmpDict]; + // converting to non-mutable costs 42% of entire method + return tmpDict; +} + +- (NSDictionary *)fontAttributesWithOverrideFontName:(NSString *)overrideFontName +{ + NSMutableDictionary *tmpAttributes = [[self fontAttributes] mutableCopy]; + + // remove family + [tmpAttributes removeObjectForKey:(id)kCTFontFamilyNameAttribute]; + + // replace font name + [tmpAttributes setObject:overrideFontName forKey:(id)kCTFontNameAttribute]; + + return tmpAttributes; +} + +- (BOOL)supportsNativeSmallCaps +{ + if ([DTCoreTextFontDescriptor smallCapsFontNameforFontFamily:_fontFamily bold:self.boldTrait italic:self.italicTrait]) + { + return YES; + } + + CTFontRef tmpFont = [self newMatchingFont]; + + BOOL smallCapsSupported = NO; + + // check if this font supports small caps + CFArrayRef fontFeatures = CTFontCopyFeatures(tmpFont); + + if (fontFeatures) + { + for (NSDictionary *oneFeature in (__bridge NSArray *)fontFeatures) + { + NSInteger featureTypeIdentifier = [[oneFeature objectForKey:(id)kCTFontFeatureTypeIdentifierKey] integerValue]; + + if (featureTypeIdentifier == 3) // Letter Case + { + NSArray *featureSelectors = [oneFeature objectForKey:(id)kCTFontFeatureTypeSelectorsKey]; + + for (NSDictionary *oneFeatureSelector in featureSelectors) + { + NSInteger featureSelectorIdentifier = [[oneFeatureSelector objectForKey:(id)kCTFontFeatureSelectorIdentifierKey] integerValue]; + + if (featureSelectorIdentifier == 3) // Small Caps + { + // hooray, small caps supported! + smallCapsSupported = YES; + + break; + } + } + + break; + } + } + + CFRelease(fontFeatures); + } + + if (tmpFont) + { + CFRelease(tmpFont); + } + + return smallCapsSupported; +} + +#pragma mark Finding Font + +- (BOOL)_fontIsOblique:(CTFontRef)font +{ + NSDictionary *traits = (__bridge_transfer NSDictionary *)CTFontCopyTraits(font); + + CGFloat slant = [[traits objectForKey:(id)kCTFontSlantTrait] floatValue]; + BOOL hasItalicTrait = ([[traits objectForKey:(id)kCTFontSymbolicTrait] unsignedIntValue] & kCTFontItalicTrait) ==kCTFontItalicTrait; + + if (!hasItalicTrait || slant<0.01) + { + return NO; + } + + // font HAS italic trait AND sufficient slant angle + return YES; + +} + +- (CTFontRef)_findOrMakeMatchingFont +{ + NSNumber *cacheKey = [NSNumber numberWithUnsignedInteger:[self hash]]; + + CTFontRef cachedFont = (__bridge_retained CTFontRef)[_fontCache objectForKey:cacheKey]; + + if (cachedFont) + { + return cachedFont; + } + + CTFontDescriptorRef fontDesc = NULL; + + CTFontRef matchingFont; + + NSString *overrideName = nil; + + // override fontName if a small caps or regular override is registered + if (_fontFamily) + { + if (_smallCapsFeature) + { + overrideName = [DTCoreTextFontDescriptor smallCapsFontNameforFontFamily:_fontFamily bold:self.boldTrait italic:self.italicTrait]; + } + else + { + overrideName = [DTCoreTextFontDescriptor overrideFontNameforFontFamily:_fontFamily bold:self.boldTrait italic:self.italicTrait]; + } + } + + // if we use the chinese font cascade fix we cannot use fast method as it does not allow specifying the custom cascade list + BOOL useFastFontCreation = !(_needsChineseFontCascadeFix && !self.boldTrait); + + if (useFastFontCreation && (_fontName || overrideName)) + { + NSString *usedName = overrideName?overrideName:_fontName; + + matchingFont = CTFontCreateWithName((__bridge CFStringRef)usedName, _pointSize, NULL); + } + else + { + NSDictionary *attributes; + + if (overrideName) + { + attributes = [self fontAttributesWithOverrideFontName:overrideName]; + } + else + { + attributes = [self fontAttributes]; + } + + fontDesc = CTFontDescriptorCreateWithAttributes((__bridge CFDictionaryRef)attributes); + + // we need font family, font name or an overridden font name for fast font creation + if (_fontFamily||_fontName||overrideName) + { + // fast font creation + matchingFont = CTFontCreateWithFontDescriptor(fontDesc, _pointSize, NULL); + } + else + { + // without font name or family we need to do expensive search + // otherwise we always get Helvetica + + NSMutableSet *set = [NSMutableSet setWithObject:(id)kCTFontTraitsAttribute]; + + if (_fontFamily) + { + [set addObject:(id)kCTFontFamilyNameAttribute]; + } + + if (_smallCapsFeature) + { + [set addObject:(id)kCTFontFeaturesAttribute]; + } + + CTFontDescriptorRef matchingDesc = CTFontDescriptorCreateMatchingFontDescriptor(fontDesc, (__bridge CFSetRef)set); + + if (matchingDesc) + { + matchingFont = CTFontCreateWithFontDescriptor(matchingDesc, _pointSize, NULL); + CFRelease(matchingDesc); + } + else + { + matchingFont = nil; + } + } + + // check if we indeed got an oblique font if we wanted one + + if (matchingFont) + { + if (self.italicTrait) + { + if (![self _fontIsOblique:matchingFont]) + { + // need to synthesize slant + CGAffineTransform slantMatrix = { 1, 0, 0.25, 1, 0, 0 }; + + CFRelease(matchingFont); + matchingFont = CTFontCreateWithFontDescriptor(fontDesc, _pointSize, &slantMatrix); + } + } + } + + CFRelease(fontDesc); + } + + if (matchingFont) + { + // cache it + [_fontCache setObject:(__bridge id)(matchingFont) forKey:cacheKey]; + } + + return matchingFont; // returns a +1 reference +} + +- (CTFontRef)newMatchingFont +{ + __block CTFontRef retFont; + + // all calls get queued + dispatch_sync(_fontQueue, ^{ + retFont = [self _findOrMakeMatchingFont]; + }); + + return retFont; +} + +// two font descriptors are equal if their attributes has identical hash codes +- (NSUInteger)hash +{ + NSUInteger calcHash = 7; + + calcHash = calcHash*31 + _pointSize; + calcHash = calcHash*31 + (_stylisticClass | _stylisticTraits); + calcHash = calcHash*31 + [_fontName hash]; + calcHash = calcHash*31 + [_fontFamily hash]; + + return calcHash; +} + +- (BOOL)isEqual:(id)object +{ + return (([object isKindOfClass:[DTCoreTextFontDescriptor class]]) && ([self hash] == [object hash])); +} + + +#pragma mark NSCoding + +- (void)encodeWithCoder:(NSCoder *)encoder +{ + [encoder encodeObject:self.fontName forKey:@"FontName"]; + [encoder encodeObject:self.fontFamily forKey:@"FontFamily"]; + [encoder encodeBool:self.boldTrait forKey:@"BoldTrait"]; + [encoder encodeBool:self.italicTrait forKey:@"ItalicTrait"]; +} + +- (id)initWithCoder:(NSCoder *)decoder +{ + self = [super init]; + + if (self) + { + self.fontName = [decoder decodeObjectForKey:@"FontName"]; + self.fontFamily = [decoder decodeObjectForKey:@"FontFamily"]; + self.boldTrait = [decoder decodeBoolForKey:@"BoldTrait"]; + self.italicTrait = [decoder decodeBoolForKey:@"ItalicTrait"]; + } + + return self; +} + + +#pragma mark Copying + +- (id)copyWithZone:(NSZone *)zone +{ + DTCoreTextFontDescriptor *newDesc = [[DTCoreTextFontDescriptor allocWithZone:zone] initWithFontAttributes:[self fontAttributes]]; + newDesc.pointSize = self.pointSize; + if (_stylisticClass) + { + newDesc.stylisticClass = self.stylisticClass; + } + + return newDesc; +} + + +#pragma mark Properties +- (void)setStylisticClass:(CTFontStylisticClass)newClass +{ + self.fontFamily = nil; + + _stylisticClass = newClass; +} + + +- (void)setFontAttributes:(NSDictionary *)attributes +{ + if (!attributes) + { + self.fontFamily = nil; + self.fontName = nil; + self.pointSize = 12; + + _stylisticTraits = 0; + _stylisticClass = 0; + } + + NSDictionary *traitsDict = [attributes objectForKey:(id)kCTFontTraitsAttribute]; + + if (traitsDict) + { + CTFontSymbolicTraits traitsValue = [[traitsDict objectForKey:(id)kCTFontSymbolicTrait ] unsignedIntValue]; + self.symbolicTraits = traitsValue; + } + + NSNumber *pointNum = [attributes objectForKey:(id)kCTFontSizeAttribute]; + if (pointNum) + { + _pointSize = [pointNum floatValue]; + } + + NSString *family = [attributes objectForKey:(id)kCTFontFamilyNameAttribute]; + + if (family) + { + self.fontFamily = family; + } + + NSString *name = [attributes objectForKey:(id)kCTFontNameAttribute]; + + if (name) + { + self.fontName = name; + } +} + +- (CTFontSymbolicTraits)symbolicTraits +{ + // symbolic traits include both stylistic traits as well as stylistic class + return _stylisticTraits | _stylisticClass; +} + +- (void)setSymbolicTraits:(CTFontSymbolicTraits)theSymbolicTraits +{ + // symbolic traits include both stylistic traits as well as stylistic class + _stylisticTraits = theSymbolicTraits & ~kCTFontClassMaskTrait; + _stylisticClass = theSymbolicTraits & kCTFontClassMaskTrait; +} + +// a representation of this font in CSS style +- (NSString *)cssStyleRepresentation +{ + NSMutableString *retString = [NSMutableString string]; + + if (_fontFamily) + { + [retString appendFormat:@"font-family:'%@';", _fontFamily]; + } + + [retString appendFormat:@"font-size:%.0fpx;", _pointSize]; + + if (self.italicTrait) + { + [retString appendString:@"font-style:italic;"]; + } + + if (self.boldTrait) + { + [retString appendString:@"font-weight:bold;"]; + } + + // return nil if no content + if ([retString length]) + { + return retString; + } + else + { + return nil; + } +} + +- (void)setBoldTrait:(BOOL)boldTrait +{ + if (boldTrait) + { + _stylisticTraits |= kCTFontBoldTrait; + } + else + { + _stylisticTraits &= ~kCTFontBoldTrait; + } +} + +- (BOOL)boldTrait +{ + return (_stylisticTraits & kCTFontBoldTrait)!=0; +} + +- (void)setItalicTrait:(BOOL)italicTrait +{ + if (italicTrait) + { + _stylisticTraits |= kCTFontItalicTrait; + } + else + { + _stylisticTraits &= ~kCTFontItalicTrait; + } +} + +- (BOOL)italicTrait +{ + return (_stylisticTraits & kCTFontItalicTrait)!=0; +} + +- (void)setExpandedTrait:(BOOL)expandedTrait +{ + if (expandedTrait) + { + _stylisticTraits |= kCTFontExpandedTrait; + } + else + { + _stylisticTraits &= ~kCTFontExpandedTrait; + } +} + +- (BOOL)expandedTrait +{ + return (_stylisticTraits & kCTFontExpandedTrait)!=0; +} + +- (void)setCondensedTrait:(BOOL)condensedTrait +{ + if (condensedTrait) + { + _stylisticTraits |= kCTFontCondensedTrait; + } + else + { + _stylisticTraits &= ~kCTFontCondensedTrait; + } +} + +- (BOOL)condensedTrait +{ + return (_stylisticTraits & kCTFontCondensedTrait)!=0; +} + +- (void)setMonospaceTrait:(BOOL)monospaceTrait +{ + if (monospaceTrait) + { + _stylisticTraits |= kCTFontMonoSpaceTrait; + } + else + { + _stylisticTraits &= ~kCTFontMonoSpaceTrait; + } +} + +- (BOOL)monospaceTrait +{ + return (_stylisticTraits & kCTFontMonoSpaceTrait)!=0; +} + +- (void)setVerticalTrait:(BOOL)verticalTrait +{ + if (verticalTrait) + { + _stylisticTraits |= kCTFontVerticalTrait; + } + else + { + _stylisticTraits &= ~kCTFontVerticalTrait; + } +} + +- (BOOL)verticalTrait +{ + return (_stylisticTraits & kCTFontVerticalTrait)!=0; +} + +- (void)setUIoptimizedTrait:(BOOL)UIoptimizedTrait +{ + if (UIoptimizedTrait) + { + _stylisticTraits |= kCTFontUIOptimizedTrait; + } + else + { + _stylisticTraits &= ~kCTFontUIOptimizedTrait; + } +} + +- (BOOL)UIoptimizedTrait +{ + return (_stylisticTraits & kCTFontUIOptimizedTrait)!=0; +} + +- (void)setPointSize:(CGFloat)pointSize +{ + _pointSize = roundf(pointSize); +} + +@synthesize fontFamily = _fontFamily; +@synthesize fontName = _fontName; +@synthesize pointSize = _pointSize; + +@synthesize symbolicTraits; + +@synthesize stylisticClass = _stylisticClass; +@synthesize smallCapsFeature = _smallCapsFeature; + +@end + diff --git a/Pods/DTCoreText/Core/Source/DTCoreTextFunctions.h b/Pods/DTCoreText/Core/Source/DTCoreTextFunctions.h new file mode 100644 index 00000000..efb12365 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCoreTextFunctions.h @@ -0,0 +1,14 @@ +// +// DTCoreTextFunctions.h +// DTCoreText +// +// Created by Oliver Drobnik on 21.12.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +/** + Creates a CTFont from a UIFont + @param font The `UIFont` + @returns The matching CTFont + */ +CTFontRef DTCTFontCreateWithUIFont(UIFont *font); \ No newline at end of file diff --git a/Pods/DTCoreText/Core/Source/DTCoreTextFunctions.m b/Pods/DTCoreText/Core/Source/DTCoreTextFunctions.m new file mode 100644 index 00000000..fab73b04 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCoreTextFunctions.m @@ -0,0 +1,14 @@ +// +// DTCoreTextFunctions.m +// DTCoreText +// +// Created by Oliver Drobnik on 21.12.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import "DTCoreTextFunctions.h" + +CTFontRef DTCTFontCreateWithUIFont(UIFont *font) +{ + return CTFontCreateWithName((__bridge CFStringRef)font.fontName, font.pointSize, NULL); +} \ No newline at end of file diff --git a/Pods/DTCoreText/Core/Source/DTCoreTextGlyphRun.h b/Pods/DTCoreText/Core/Source/DTCoreTextGlyphRun.h new file mode 100644 index 00000000..52183b86 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCoreTextGlyphRun.h @@ -0,0 +1,48 @@ +// +// DTCoreTextGlyphRun.h +// CoreTextExtensions +// +// Created by Oliver Drobnik on 1/25/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + + + +#if TARGET_OS_IPHONE +#import +#elif TARGET_OS_MAC +#import +#endif + +@class DTCoreTextLayoutLine; +@class DTTextAttachment; + +@interface DTCoreTextGlyphRun : NSObject +{ + NSRange _stringRange; +} + +- (id)initWithRun:(CTRunRef)run layoutLine:(DTCoreTextLayoutLine *)layoutLine offset:(CGFloat)offset; + +- (CGRect)frameOfGlyphAtIndex:(NSInteger)index; +- (CGRect)imageBoundsInContext:(CGContextRef)context; +- (NSRange)stringRange; +- (NSArray *)stringIndices; + +- (void)drawInContext:(CGContextRef)context; + +- (void)fixMetricsFromAttachment; + +@property (nonatomic, assign, readonly) CGRect frame; +@property (nonatomic, assign, readonly) NSInteger numberOfGlyphs; +@property (nonatomic, unsafe_unretained, readonly) NSDictionary *attributes; // subtle simulator bug - use assign not __unsafe_unretained in 4.2 +@property (nonatomic, assign, readonly, getter=isHyperlink) BOOL hyperlink; + +@property (nonatomic, assign, readonly) CGFloat ascent; +@property (nonatomic, assign, readonly) CGFloat descent; +@property (nonatomic, assign, readonly) CGFloat leading; +@property (nonatomic, assign, readonly) CGFloat width; + +@property (nonatomic, strong) DTTextAttachment *attachment; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTCoreTextGlyphRun.m b/Pods/DTCoreText/Core/Source/DTCoreTextGlyphRun.m new file mode 100644 index 00000000..d68cb56c --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCoreTextGlyphRun.m @@ -0,0 +1,326 @@ +// +// DTCoreTextGlyphRun.m +// CoreTextExtensions +// +// Created by Oliver Drobnik on 1/25/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import "DTCoreTextGlyphRun.h" +#import "DTCoreTextLayoutLine.h" +#import "DTTextAttachment.h" +#import "DTCoreTextConstants.h" + +#ifndef __IPHONE_4_3 + #define __IPHONE_4_3 40300 +#endif + +#define SYNCHRONIZE_START(obj) dispatch_semaphore_wait(runLock, DISPATCH_TIME_FOREVER); +#define SYNCHRONIZE_END(obj) dispatch_semaphore_signal(runLock); + +@interface DTCoreTextGlyphRun () +@property (nonatomic, assign) CGRect frame; +@property (nonatomic, assign) NSInteger numberOfGlyphs; +@property (nonatomic, unsafe_unretained, readwrite) NSDictionary *attributes; +@property (nonatomic, assign) dispatch_semaphore_t runLock; + +@end + + +@implementation DTCoreTextGlyphRun +{ + CTRunRef _run; + + CGRect _frame; + + CGFloat _offset; // x distance from line origin + CGFloat _ascent; + CGFloat _descent; + CGFloat _leading; + CGFloat _width; + + NSInteger _numberOfGlyphs; + + const CGPoint *_glyphPositionPoints; + //BOOL needToFreeGlyphPositionPoints; + + __unsafe_unretained DTCoreTextLayoutLine *_line; // retain cycle, since these objects are retained by the _line + __unsafe_unretained NSDictionary *_attributes; + NSArray *_stringIndices; + + DTTextAttachment *_attachment; + BOOL _hyperlink; + + BOOL _didCheckForAttachmentInAttributes; + BOOL _didCheckForHyperlinkInAttributes; + BOOL _didCalculateMetrics; +} + +@synthesize runLock; + +- (id)initWithRun:(CTRunRef)run layoutLine:(DTCoreTextLayoutLine *)layoutLine offset:(CGFloat)offset +{ + self = [super init]; + + if (self) + { + _run = run; + CFRetain(_run); + + _offset = offset; + _line = layoutLine; + runLock = dispatch_semaphore_create(1); + } + + return self; +} + +- (void)dealloc +{ + if (_run) + { + CFRelease(_run); + } + + dispatch_release(runLock); +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@ glyphs=%d %@>", [self class], [self numberOfGlyphs], NSStringFromCGRect(_frame)]; +} + +#pragma mark Calculations +- (void)calculateMetrics +{ + // calculate metrics + SYNCHRONIZE_START(self) + { + if (!_didCalculateMetrics) + { + _width = (CGFloat)CTRunGetTypographicBounds((CTRunRef)_run, CFRangeMake(0, 0), &_ascent, &_descent, &_leading); + _didCalculateMetrics = YES; + } + } + SYNCHRONIZE_END(self) +} + +- (CGRect)frameOfGlyphAtIndex:(NSInteger)index +{ + if (!_didCalculateMetrics) { + [self calculateMetrics]; + } + if (!_glyphPositionPoints) + { + // this is a pointer to the points inside the run, thus no retain necessary + _glyphPositionPoints = CTRunGetPositionsPtr(_run); + } + + if (!_glyphPositionPoints || index >= self.numberOfGlyphs) + { + return CGRectNull; + } + + CGPoint glyphPosition = _glyphPositionPoints[index]; + + CGRect rect = CGRectMake(_line.baselineOrigin.x + glyphPosition.x, _line.baselineOrigin.y - _ascent, _offset + _width - glyphPosition.x, _ascent + _descent); + if (index < self.numberOfGlyphs-1) + { + rect.size.width = _glyphPositionPoints[index+1].x - glyphPosition.x; + } + + return rect; +} + +// TODO: fix indices if the stringRange is modified +- (NSArray *)stringIndices +{ + if (!_stringIndices) + { + const CFIndex *indices = CTRunGetStringIndicesPtr(_run); + NSInteger count = self.numberOfGlyphs; + NSMutableArray *array = [NSMutableArray arrayWithCapacity:count]; + NSInteger i; + for (i = 0; i < count; i++) + { + [array addObject:[NSNumber numberWithInteger:indices[i]]]; + } + _stringIndices = array; + } + return _stringIndices; +} + +// bounds of an image encompassing the entire run +- (CGRect)imageBoundsInContext:(CGContextRef)context +{ + return CTRunGetImageBounds(_run, context, CFRangeMake(0, 0)); +} + +// range of the characters from the original string +- (NSRange)stringRange +{ + if (!_stringRange.length) + { + CFRange range = CTRunGetStringRange(_run); + + _stringRange = NSMakeRange(range.location, range.length); + } + + return _stringRange; +} + +- (void)drawInContext:(CGContextRef)context +{ + if (!_run || !context) + { + return; + } + + CGAffineTransform textMatrix = CTRunGetTextMatrix(_run); + + if (CGAffineTransformIsIdentity(textMatrix)) + { + CTRunDraw(_run, context, CFRangeMake(0, 0)); + } + else + { + CGPoint pos = CGContextGetTextPosition(context); + + // set tx and ty to current text pos according to docs + textMatrix.tx = pos.x; + textMatrix.ty = pos.y; + + CGContextSetTextMatrix(context, textMatrix); + + CTRunDraw(_run, context, CFRangeMake(0, 0)); + + // restore identity + CGContextSetTextMatrix(context, CGAffineTransformIdentity); + } +} + +- (void)fixMetricsFromAttachment +{ + if (self.attachment) + { + if (!_didCalculateMetrics) + { + [self calculateMetrics]; + } + + _descent = 0; + _ascent = self.attachment.displaySize.height; + } +} + +#pragma mark Properites +- (NSInteger)numberOfGlyphs +{ + if (!_numberOfGlyphs) + { + _numberOfGlyphs = CTRunGetGlyphCount(_run); + } + + return _numberOfGlyphs; +} + +- (NSDictionary *)attributes +{ + if (!_attributes) + { + _attributes = (__bridge NSDictionary *)CTRunGetAttributes(_run); + } + + return _attributes; +} + +- (DTTextAttachment *)attachment +{ + if (!_attachment) + { + if (!_didCheckForAttachmentInAttributes) + { + _attachment = [self.attributes objectForKey:NSAttachmentAttributeName]; + + _didCheckForAttachmentInAttributes = YES; + } + } + + return _attachment; +} + +- (BOOL)isHyperlink +{ + if (!_hyperlink) + { + if (!_didCheckForHyperlinkInAttributes) + { + _hyperlink = [self.attributes objectForKey:DTLinkAttribute]!=nil; + + _didCheckForHyperlinkInAttributes = YES; + } + } + + return _hyperlink; +} + +- (CGRect)frame +{ + if (!_didCalculateMetrics) + { + [self calculateMetrics]; + } + + return CGRectMake(_line.baselineOrigin.x + _offset, _line.baselineOrigin.y - _ascent, _width, _ascent + _descent); +} + +- (CGFloat)width +{ + if (!_didCalculateMetrics) + { + [self calculateMetrics]; + } + + return _width; +} + +- (CGFloat)ascent +{ + if (!_didCalculateMetrics) + { + [self calculateMetrics]; + } + + return _ascent; +} + +- (CGFloat)descent +{ + if (!_didCalculateMetrics) + { + [self calculateMetrics]; + } + + return _descent; +} + +- (CGFloat)leading +{ + if (!_didCalculateMetrics) + { + [self calculateMetrics]; + } + + return _leading; +} + +@synthesize frame = _frame; +@synthesize numberOfGlyphs = _numberOfGlyphs; +@synthesize attributes = _attributes; + +@synthesize ascent = _ascent; +@synthesize descent = _descent; +@synthesize leading = _leading; +@synthesize attachment = _attachment; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTCoreTextLayoutFrame.h b/Pods/DTCoreText/Core/Source/DTCoreTextLayoutFrame.h new file mode 100644 index 00000000..a8412b87 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCoreTextLayoutFrame.h @@ -0,0 +1,285 @@ +// +// DTCoreTextLayoutFrame.h +// CoreTextExtensions +// +// Created by Oliver Drobnik on 1/24/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + + +#if TARGET_OS_IPHONE +#import +#elif TARGET_OS_MAC +#import +#endif + +@class DTCoreTextLayoutLine; +@class DTTextBlock; + + +// the value to use if the height is unknown +#define CGFLOAT_OPEN_HEIGHT 16777215.0f + +typedef void (^DTCoreTextLayoutFrameTextBlockHandler)(DTTextBlock *textBlock, CGRect frame, CGContextRef context, BOOL *shouldDrawDefaultBackground); + + +@class DTCoreTextLayouter; + +/** + This class represents a single frame of text and basically wraps CTFrame. It provides an array of text lines that fit in the given rectangle. + + Both styles of layouting are supported: open ended (suitable for scroll views) and limited to a given rectangle. To use the open-ended style specify `CGFLOAT_OPEN_HEIGHT` for the height when creating a layout frame. + + The array of lines is built lazily the first time it is accessed or - for open-ended frames - when the frame property is being queried. + */ +@interface DTCoreTextLayoutFrame : NSObject +{ + CGRect _frame; + + NSArray *_lines; + NSArray *_paragraphRanges; + + NSArray *_textAttachments; + NSAttributedString *_attributedStringFragment; +} + + +/** + @name Creating Layout Frames + */ + + +/** + Creates a Layout Frame with the given frame using the attributed string loaded into the layouter. + + @param frame The rectangle specifying origin and size of available for text. Specify `CGFLOAT_OPEN_HEIGHT` to not limit the height. + @param layouter A reference to the layouter for this text box. + */ +- (id)initWithFrame:(CGRect)frame layouter:(DTCoreTextLayouter *)layouter; + + +/** + Creates a Layout Frame with the given frame using the attributed string loaded into the layouter. + + @param frame The rectangle specifying origin and size of available for text. Specify `CGFLOAT_OPEN_HEIGHT` to not limit the height. + @param layouter A reference to the layouter for the receiver. Note: The layouter owns the attributed string. + @param range The range within the attributed string to layout into the receiver. + */ +- (id)initWithFrame:(CGRect)frame layouter:(DTCoreTextLayouter *)layouter range:(NSRange)range; + + +/** + @name Getting Information + */ + + +/** + The string range that is visible i.e. fits into the given rectangle. For open-ended frames this is typically the entire string. For frame-contrained layout frames it is the substring that fits. + */ +- (NSRange)visibleStringRange; + + +/** + This is a copy of the attributed string owned by the layouter of the receiver. +*/ +- (NSAttributedString *)attributedStringFragment; + + +/** + An array that maps glyphs with string indices. + */ +- (NSArray *)stringIndices; + + +/** + The frame rectangle for the layout frame. + */ + @property (nonatomic, assign, readonly) CGRect frame; + + +/** + @name Drawing + */ + + +/** + Draws the entire layout frame into the given graphics context. + + @param context A graphics context to draw into + @param drawImages Whether images should be drawn together with the text. If you specify `NO` then space is left blank where images would go and you have to add your own views to display these images. + @param drawImages Whether hyperlinks should be drawn together with the text. If you specify `NO` then space is left blank where links would go and you have to add your own views to display these links. + */ +- (void)drawInContext:(CGContextRef)context drawImages:(BOOL)drawImages drawLinks:(BOOL)drawLinks; + + +/** + Set a custom handler to be executed before text belonging to a text block is drawn. + + @param handler A DTCoreTextLayoutFrameTextBlockHandler block. +*/ +@property (nonatomic, copy) DTCoreTextLayoutFrameTextBlockHandler textBlockHandler; + + +/** + @name Working with Glyphs + */ + + +/** + Retrieves the index of the text line that contains the given glyph index. + + @param index The index of the glyph + @returns The index of the line containing this glyph + */ +- (NSInteger)lineIndexForGlyphIndex:(NSInteger)index; + + +/** + Retrieves the frame of the glyph at the given glyph index. + + @param index The index of the glyph + @returns The frame of this glyph + */ +- (CGRect)frameOfGlyphAtIndex:(NSInteger)index; + + +/** + @name Working with Text Lines + */ + + +/** + The text lines that belong to the receiver. + */ +@property (nonatomic, strong, readonly) NSArray *lines; + + +/** + The text lines that are visible inside the given rectangle. Also incomplete lines are included. + + @param rect The rectangle + @returns An array, sorted from top to bottom, of lines at least partially visible + */ +- (NSArray *)linesVisibleInRect:(CGRect)rect; + + +/** + The text lines that are visible inside the given rectangle. Only fully visible lines are included. + + @param rect The rectangle + @returns An array, sorted from top to bottom, of lines fully visible + */ +- (NSArray *)linesContainedInRect:(CGRect)rect; + + +/** + The layout line that contains the given string index. + + @param index The string index + @returns The layout line that this index belongs to + */ +- (DTCoreTextLayoutLine *)lineContainingIndex:(NSUInteger)index; + + +/** + Determins if the given line is the first in a paragraph. + + This is needed for example to determine whether paragraphSpaceBefore needs to be applied before it. + @param line The Line + @returns `YES` if the given line is the first in a paragraph + */ +- (BOOL)isLineFirstInParagraph:(DTCoreTextLayoutLine *)line; + + +/** + Determins if the given line is the last in a paragraph. + + This is needed for example to determine whether paragraph spacing needs to be applied after it. + @param line The Line + @returns `YES` if the given line is the last in a paragraph + */ +- (BOOL)isLineLastInParagraph:(DTCoreTextLayoutLine *)line; + + +/** + Finds the appropriate baseline origin for a line to position it at the correct distance from a previous line. + + @param line The line + @param previousLine The line after which to position the line. + @returns The correct baseline origin for the line. + */ +- (CGPoint)baselineOriginToPositionLine:(DTCoreTextLayoutLine *)line afterLine:(DTCoreTextLayoutLine *)previousLine; + + +/** + @name Text Attachments + */ + + +/** + The array of all instances that belong to the receiver. + @returns All text attachments of the receiver. + */ +- (NSArray *)textAttachments; + + +/** + The array of all DTTextAttachment instances that belong to the receiver which also match the specified predicate. + + @param predicate A predicate that uses properties of to reduce the returned array + @returns A filtered array of text attachments. + */ +- (NSArray *)textAttachmentsWithPredicate:(NSPredicate *)predicate; + + +/** + @name Getting Paragraph Info + */ + + +/** + Finding which paragraph a given string index belongs to. + + @param stringIndex The index in the string to look for + @returns The index of the paragraph, numbered from 0 + */ +- (NSUInteger)paragraphIndexContainingStringIndex:(NSUInteger)stringIndex; + + +/** + Determines the paragraph range (of paragraph indexes) that encompass the entire given string Range. + + @param stringRange The string range for which the paragraph range is sought for + @returns The range of paragraphs that fully enclose the string range + */ +- (NSRange)paragraphRangeContainingStringRange:(NSRange)stringRange; + + +/** + The text lines that belong to the specified paragraph. + + @param index The index of the paragraph + @returns An array, sorted from top to bottom, of lines in this paragraph + */ +- (NSArray *)linesInParagraphAtIndex:(NSUInteger)index; + + +/** + An array of `NSRange` values encapsulated in `NSValue` instances. Each range is the string range contained in the corresponding paragraph. +*/ +@property (nonatomic, strong, readonly) NSArray *paragraphRanges; + + +/** + @name Debugging + */ + + +/** + Switches on the debug drawing mode where individual glph runs, baselines, et ceter get individually marked. + + @param debugFrames if the debug drawing should occur + */ ++ (void)setShouldDrawDebugFrames:(BOOL)debugFrames; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTCoreTextLayoutFrame.m b/Pods/DTCoreText/Core/Source/DTCoreTextLayoutFrame.m new file mode 100644 index 00000000..74cae444 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCoreTextLayoutFrame.m @@ -0,0 +1,1514 @@ +// +// DTCoreTextLayoutFrame.m +// CoreTextExtensions +// +// Created by Oliver Drobnik on 1/24/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import "DTCoreText.h" +#import "DTCoreTextLayoutFrame.h" +#import "DTVersion.h" + +// global flag that shows debug frames +static BOOL _DTCoreTextLayoutFramesShouldDrawDebugFrames = NO; + + +// two correction methods used by the deprecated way of layouting to work around Core Text bugs +@interface DTCoreTextLayoutFrame () + +- (void)_correctAttachmentHeights; +- (void)_correctLineOrigins; + +@end + +@implementation DTCoreTextLayoutFrame +{ + CTFrameRef _textFrame; + CTFramesetterRef _framesetter; + + NSRange _requestedStringRange; + NSRange _stringRange; + + //NSInteger _tag; + + DTCoreTextLayoutFrameTextBlockHandler _textBlockHandler; +} + +// makes a frame for a specific part of the attributed string of the layouter +- (id)initWithFrame:(CGRect)frame layouter:(DTCoreTextLayouter *)layouter range:(NSRange)range +{ + self = [super init]; + if (self) + { + _frame = frame; + + _attributedStringFragment = [layouter.attributedString mutableCopy]; + + // determine correct target range + _requestedStringRange = range; + NSUInteger stringLength = [_attributedStringFragment length]; + + if (_requestedStringRange.location >= stringLength) + { + return nil; + } + + if (_requestedStringRange.length==0 || NSMaxRange(_requestedStringRange) > stringLength) + { + _requestedStringRange.length = stringLength - _requestedStringRange.location; + } + + CFRange cfRange = CFRangeMake(_requestedStringRange.location, _requestedStringRange.length); + _framesetter = layouter.framesetter; + + if (_framesetter) + { + CFRetain(_framesetter); + + CGMutablePathRef path = CGPathCreateMutable(); + CGPathAddRect(path, NULL, frame); + + _textFrame = CTFramesetterCreateFrame(_framesetter, cfRange, path, NULL); + + CGPathRelease(path); + } + else + { + // Strange, should have gotten a valid framesetter + return nil; + } + } + + return self; +} + +// makes a frame for the entire attributed string of the layouter +- (id)initWithFrame:(CGRect)frame layouter:(DTCoreTextLayouter *)layouter +{ + return [self initWithFrame:frame layouter:layouter range:NSMakeRange(0, 0)]; +} + +- (void)dealloc +{ + if (_textFrame) + { + CFRelease(_textFrame); + } + + if (_framesetter) + { + CFRelease(_framesetter); + } +} + +- (NSString *)description +{ + return [self.lines description]; +} + +#pragma mark Building the Lines +/* + Builds the array of lines with the internal typesetter of our framesetter. No need to correct line origins in this case because they are placed correctly in the first place. This version supports text boxes. + */ +- (void)_buildLinesWithTypesetter +{ + // framesetter keeps internal reference, no need to retain + CTTypesetterRef typesetter = CTFramesetterGetTypesetter(_framesetter); + + NSMutableArray *typesetLines = [NSMutableArray array]; + + CGPoint lineOrigin = _frame.origin; + + DTCoreTextLayoutLine *previousLine = nil; + + // need the paragraph ranges to know if a line is at the beginning of paragraph + NSMutableArray *paragraphRanges = [[self paragraphRanges] mutableCopy]; + + NSRange currentParagraphRange = [[paragraphRanges objectAtIndex:0] rangeValue]; + + // we start out in the requested range, length will be set by the suggested line break function + NSRange lineRange = _requestedStringRange; + + // maximum values for abort of loop + CGFloat maxY = CGRectGetMaxY(_frame); + NSUInteger maxIndex = NSMaxRange(_requestedStringRange); + NSUInteger fittingLength = 0; + + typedef struct + { + CGFloat ascent; + CGFloat descent; + CGFloat width; + CGFloat leading; + CGFloat trailingWhitespaceWidth; + } lineMetrics; + + typedef struct + { + CGFloat paragraphSpacingBefore; + CGFloat paragraphSpacing; + CGFloat lineHeightMultiplier; + } paragraphMetrics; + + paragraphMetrics currentParaMetrics = {0,0,0}; + paragraphMetrics previousParaMetrics = {0,0,0}; + + lineMetrics currentLineMetrics; + + DTTextBlock *currentTextBlock = nil; + DTTextBlock *previousTextBlock = nil; + + do + { + while (lineRange.location >= (currentParagraphRange.location+currentParagraphRange.length)) + { + // we are outside of this paragraph, so we go to the next + [paragraphRanges removeObjectAtIndex:0]; + + currentParagraphRange = [[paragraphRanges objectAtIndex:0] rangeValue]; + } + + BOOL isAtBeginOfParagraph = (currentParagraphRange.location == lineRange.location); + + CGFloat headIndent = 0; + CGFloat tailIndent = 0; + + // get the paragraph style at this index + CTParagraphStyleRef paragraphStyle = (__bridge CTParagraphStyleRef)[_attributedStringFragment attribute:(id)kCTParagraphStyleAttributeName atIndex:lineRange.location effectiveRange:NULL]; + + currentTextBlock = [[_attributedStringFragment attribute:DTTextBlocksAttribute atIndex:lineRange.location effectiveRange:NULL] lastObject]; + + if (previousTextBlock != currentTextBlock) + { + lineOrigin.y += previousTextBlock.padding.bottom; + lineOrigin.y += currentTextBlock.padding.top; + + previousTextBlock = currentTextBlock; + } + + if (isAtBeginOfParagraph) + { + CTParagraphStyleGetValueForSpecifier(paragraphStyle, kCTParagraphStyleSpecifierFirstLineHeadIndent, sizeof(headIndent), &headIndent); + + // save prev paragraph + previousParaMetrics = currentParaMetrics; + + // Save the paragraphSpacingBefore to currentParaMetrics. This should be done after saving previousParaMetrics. + CTParagraphStyleGetValueForSpecifier(paragraphStyle, kCTParagraphStyleSpecifierParagraphSpacingBefore, sizeof(currentParaMetrics.paragraphSpacingBefore), ¤tParaMetrics.paragraphSpacingBefore); + } + else + { + CTParagraphStyleGetValueForSpecifier(paragraphStyle, kCTParagraphStyleSpecifierHeadIndent, sizeof(headIndent), &headIndent); + } + + CTParagraphStyleGetValueForSpecifier(paragraphStyle, kCTParagraphStyleSpecifierTailIndent, sizeof(tailIndent), &tailIndent); + + // add left padding to offset + lineOrigin.x = _frame.origin.x + headIndent + currentTextBlock.padding.left; + + CGFloat availableSpace; + CGFloat offset = headIndent + currentTextBlock.padding.left; + + if (tailIndent<=0) + { + // negative tail indent is measured from trailing margin (we assume LTR here) + availableSpace = _frame.size.width - offset - currentTextBlock.padding.right + tailIndent; + } + else + { + availableSpace = tailIndent - offset - currentTextBlock.padding.right; + } + + // find how many characters we get into this line + lineRange.length = CTTypesetterSuggestLineBreak(typesetter, lineRange.location, availableSpace); + + if (NSMaxRange(lineRange) > maxIndex) + { + // only layout as much as was requested + lineRange.length = maxIndex - lineRange.location; + } + + if (NSMaxRange(lineRange) == NSMaxRange(currentParagraphRange)) + { + // at end of paragraph, record the spacing + CTParagraphStyleGetValueForSpecifier(paragraphStyle, kCTParagraphStyleSpecifierParagraphSpacing, sizeof(currentParaMetrics.paragraphSpacing), ¤tParaMetrics.paragraphSpacing); + } + + // create a line to fit + CTLineRef line = CTTypesetterCreateLine(typesetter, CFRangeMake(lineRange.location, lineRange.length)); + + // we need all metrics so get the at once + currentLineMetrics.width = CTLineGetTypographicBounds(line, ¤tLineMetrics.ascent, ¤tLineMetrics.descent, ¤tLineMetrics.leading); + + // get line height in px if it is specified for this line + CGFloat lineHeight = 0; + CGFloat minLineHeight = 0; + CGFloat maxLineHeight = 0; + + BOOL usesSyntheticLeading = NO; + BOOL usesForcedLineHeight = NO; + + if (currentLineMetrics.leading == 0.0f) + { + // font has no leading, so we fake one (e.g. Helvetica) + CGFloat tmpHeight = currentLineMetrics.ascent + currentLineMetrics.descent; + currentLineMetrics.leading = ceilf(0.2f * tmpHeight); + + if (currentLineMetrics.leading>20) + { + // we have a large image increasing the ascender too much for this calc to work + currentLineMetrics.leading = 0; + } + + usesSyntheticLeading = YES; + } + else + { + // make sure that we don't have less than 10% of line height as leading + currentLineMetrics.leading = ceilf(MAX((currentLineMetrics.ascent + currentLineMetrics.descent)*0.1f, currentLineMetrics.leading)); + } + + if (CTParagraphStyleGetValueForSpecifier(paragraphStyle, kCTParagraphStyleSpecifierMinimumLineHeight, sizeof(minLineHeight), &minLineHeight)) + { + if (lineHeight0) + { + lineHeight -= currentLineMetrics.descent; + } + else + { + lineHeight = currentLineMetrics.ascent + currentLineMetrics.leading - currentLineMetrics.descent/2.0f; + } + + // leading is included in the lineHeight + lineHeight += currentLineMetrics.leading; + + if (isAtBeginOfParagraph) + { + lineOrigin.y += currentParaMetrics.paragraphSpacingBefore; + } + } + + if (CTParagraphStyleGetValueForSpecifier(paragraphStyle, kCTParagraphStyleSpecifierLineHeightMultiple, sizeof(currentParaMetrics.lineHeightMultiplier), ¤tParaMetrics.lineHeightMultiplier)) + { + if (currentParaMetrics.lineHeightMultiplier>0.0f) + { + lineHeight *= currentParaMetrics.lineHeightMultiplier; + } + } + + if (CTParagraphStyleGetValueForSpecifier(paragraphStyle, kCTParagraphStyleSpecifierMaximumLineHeight, sizeof(maxLineHeight), &maxLineHeight)) + { + if (maxLineHeight>0 && lineHeight>maxLineHeight) + { + usesForcedLineHeight = YES; + lineHeight = maxLineHeight; + } + } + + lineOrigin.y += lineHeight; + + // adjust lineOrigin based on paragraph text alignment + CTTextAlignment textAlignment; + + if (!CTParagraphStyleGetValueForSpecifier(paragraphStyle, kCTParagraphStyleSpecifierAlignment, sizeof(textAlignment), &textAlignment)) + { + textAlignment = kCTNaturalTextAlignment; + } + + switch (textAlignment) + { + case kCTLeftTextAlignment: + { + lineOrigin.x = _frame.origin.x + offset; + // nothing to do + break; + } + + case kCTNaturalTextAlignment: + { + // depends on the text direction + CTWritingDirection baseWritingDirection; + CTParagraphStyleGetValueForSpecifier(paragraphStyle, kCTParagraphStyleSpecifierBaseWritingDirection, sizeof(baseWritingDirection), &baseWritingDirection); + + if (baseWritingDirection != kCTWritingDirectionRightToLeft) + { + break; + } + + // right alignment falls through + } + + case kCTRightTextAlignment: + { + lineOrigin.x = _frame.origin.x + offset + CTLineGetPenOffsetForFlush(line, 1.0, availableSpace); + + break; + } + + case kCTCenterTextAlignment: + { + lineOrigin.x = _frame.origin.x + offset + CTLineGetPenOffsetForFlush(line, 0.5, availableSpace); + + break; + } + + case kCTJustifiedTextAlignment: + { + BOOL isAtEndOfParagraph = (currentParagraphRange.location+currentParagraphRange.length <= lineRange.location+lineRange.length || // JTL 28/June/2012 + [[_attributedStringFragment string] characterAtIndex:lineRange.location+lineRange.length-1]==0x2028); // JTL 28/June/2012 + + // only justify if not last line, not
, and if the line width is longer than 60% of the frame + // avoids over-stretching + if( !isAtEndOfParagraph && (currentLineMetrics.width > 0.60 * _frame.size.width) ) + { + // create a justified line and replace the current one with it + CTLineRef justifiedLine = CTLineCreateJustifiedLine(line, 1.0f, availableSpace); + CFRelease(line); + line = justifiedLine; + } + + lineOrigin.x = _frame.origin.x + offset; + + break; + } + } + + CGFloat lineBottom = lineOrigin.y + currentLineMetrics.descent; + + // abort layout if we left the configured frame + if (lineBottom>maxY) + { + // doesn't fit any more + CFRelease(line); + break; + } + + // wrap it + DTCoreTextLayoutLine *newLine = [[DTCoreTextLayoutLine alloc] initWithLine:line]; + CFRelease(line); + + // baseline origin is rounded + lineOrigin.y = ceilf(lineOrigin.y); + + newLine.baselineOrigin = lineOrigin; + + [typesetLines addObject:newLine]; + fittingLength += lineRange.length; + + lineRange.location += lineRange.length; + + // if there is a custom line height we need to adjust the ascender too + if (usesForcedLineHeight) + { + // causes the line frame to encompass also the extra space + newLine.ascent = lineHeight; + } + + previousLine = newLine; + //previousLineMetrics = currentLineMetrics; + } + while (lineRange.location < maxIndex); + + _lines = typesetLines; + + if (![_lines count]) + { + // no lines fit + _stringRange = NSMakeRange(0, 0); + + return; + } + + // now we know how many characters fit + _stringRange.location = _requestedStringRange.location; + _stringRange.length = fittingLength; + + // at this point we can correct the frame if it is open-ended + if (_frame.size.height == CGFLOAT_OPEN_HEIGHT) + { + // actual frame is spanned between first and last lines + DTCoreTextLayoutLine *lastLine = [_lines lastObject]; + + _frame.size.height = ceilf((CGRectGetMaxY(lastLine.frame) - _frame.origin.y + 1.5f + currentTextBlock.padding.bottom)); + + // need to add bottom padding if in text block + } +} + +/** + DEPRECATED: this was the original way of getting the lines + */ + +- (void)_buildLinesWithStandardFramesetter +{ + // get lines (don't own it so no release) + CFArrayRef cflines = CTFrameGetLines(_textFrame); + + if (!cflines) + { + // probably no string set + return; + } + + CGPoint *origins = malloc(sizeof(CGPoint)*CFArrayGetCount(cflines)); + CTFrameGetLineOrigins(_textFrame, CFRangeMake(0, 0), origins); + + NSMutableArray *tmpLines = [[NSMutableArray alloc] initWithCapacity:CFArrayGetCount(cflines)]; + + NSInteger lineIndex = 0; + + for (id oneLine in (__bridge NSArray *)cflines) + { + CGPoint lineOrigin = origins[lineIndex]; + + lineOrigin.y = _frame.size.height - lineOrigin.y + _frame.origin.y; + lineOrigin.x += _frame.origin.x; + + DTCoreTextLayoutLine *newLine = [[DTCoreTextLayoutLine alloc] initWithLine:(__bridge CTLineRef)oneLine]; newLine.baselineOrigin = lineOrigin; + + [tmpLines addObject:newLine]; + + lineIndex++; + } + free(origins); + + _lines = tmpLines; + + // need to get the visible range here + CFRange fittingRange = CTFrameGetStringRange(_textFrame); + _stringRange.location = fittingRange.location; + _stringRange.length = fittingRange.length; + + // line origins are wrong on last line of paragraphs + //[self _correctLineOrigins]; + + // --- begin workaround for image squishing bug in iOS < 4.2 + if ([DTVersion osVersionIsLessThen:@"4.2"]) + { + [self _correctAttachmentHeights]; + } + + // at this point we can correct the frame if it is open-ended + if ([_lines count] && _frame.size.height == CGFLOAT_OPEN_HEIGHT) + { + // actual frame is spanned between first and last lines + DTCoreTextLayoutLine *lastLine = [_lines lastObject]; + + _frame.size.height = ceilf((CGRectGetMaxY(lastLine.frame) - _frame.origin.y + 1.5f)); + } +} + +- (void)_buildLines +{ + // only build lines if frame is legal + if (_frame.size.width<=0) + { + return; + } + + // note: building line by line with typesetter + [self _buildLinesWithTypesetter]; + + //[self _buildLinesWithStandardFramesetter]; +} + +- (NSArray *)lines +{ + if (!_lines) + { + [self _buildLines]; + } + + return _lines; +} + +- (NSArray *)linesVisibleInRect:(CGRect)rect +{ + NSMutableArray *tmpArray = [NSMutableArray arrayWithCapacity:[self.lines count]]; + + CGFloat minY = CGRectGetMinY(rect); + CGFloat maxY = CGRectGetMaxY(rect); + + for (DTCoreTextLayoutLine *oneLine in self.lines) + { + CGRect lineFrame = oneLine.frame; + + // lines before the rect + if (CGRectGetMaxY(lineFrame) maxY) + { + break; + } + + // CGRectIntersectsRect returns false if the frame has 0 width, which + // lines that consist only of line-breaks have. Set the min-width + // to one to work-around. + lineFrame.size.width = lineFrame.size.width>1?lineFrame.size.width:1; + + if (CGRectIntersectsRect(rect, lineFrame)) + { + [tmpArray addObject:oneLine]; + } + } + + return tmpArray; +} + +- (NSArray *)linesContainedInRect:(CGRect)rect +{ + NSMutableArray *tmpArray = [NSMutableArray arrayWithCapacity:[self.lines count]]; + + CGFloat minY = CGRectGetMinY(rect); + CGFloat maxY = CGRectGetMaxY(rect); + + for (DTCoreTextLayoutLine *oneLine in self.lines) + { + CGRect lineFrame = oneLine.frame; + + // lines before the rect + if (CGRectGetMaxY(lineFrame) maxY) + { + break; + } + + if (CGRectContainsRect(rect, lineFrame)) + { + [tmpArray addObject:oneLine]; + } + } + + return tmpArray; +} + +#pragma mark Drawing + +- (void)_setShadowInContext:(CGContextRef)context fromDictionary:(NSDictionary *)dictionary +{ + DTColor *color = [dictionary objectForKey:@"Color"]; + CGSize offset = [[dictionary objectForKey:@"Offset"] CGSizeValue]; + CGFloat blur = [[dictionary objectForKey:@"Blur"] floatValue]; + + CGFloat scaleFactor = 1.0; + if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) + { + scaleFactor = [[UIScreen mainScreen] scale]; + } + + + // workaround for scale 1: strangely offset (1,1) with blur 0 does not draw any shadow, (1.01,1.01) does + if (scaleFactor==1.0) + { + if (fabs(offset.width)==1.0) + { + offset.width *= 1.50; + } + + if (fabs(offset.height)==1.0) + { + offset.height *= 1.50; + } + } + + CGContextSetShadowWithColor(context, offset, blur, color.CGColor); +} + +- (CGRect)_frameForTextBlock:(DTTextBlock *)textBlock atIndex:(NSUInteger)location +{ + NSRange blockRange = [_attributedStringFragment rangeOfTextBlock:textBlock atIndex:location]; + + DTCoreTextLayoutLine *firstBlockLine = [self lineContainingIndex:blockRange.location]; + DTCoreTextLayoutLine *lastBlockLine = [self lineContainingIndex:NSMaxRange(blockRange)-1]; + + CGRect frame; + frame.origin = firstBlockLine.frame.origin; + frame.origin.x = _frame.origin.x; // currently all boxes are full with + frame.origin.y -= textBlock.padding.top; + + CGFloat maxWidth = 0; + + for (NSUInteger index = blockRange.location; index __IPHONE_5_1 + if (!backgroundColor && ___useiOS6Attributes) + { + UIColor *uiColor = [oneRun.attributes objectForKey:NSBackgroundColorAttributeName]; + backgroundColor = uiColor.CGColor; + } +#endif + + NSDictionary *ruleStyle = [oneRun.attributes objectForKey:DTHorizontalRuleStyleAttribute]; + + if (ruleStyle) + { + if (backgroundColor) + { + CGContextSetStrokeColorWithColor(context, backgroundColor); + } + else + { + CGContextSetGrayStrokeColor(context, 0, 1.0f); + } + + CGRect nrect = self.frame; + nrect.origin = oneLine.frame.origin; + nrect.size.height = oneRun.frame.size.height; + nrect.origin.y = roundf(nrect.origin.y + oneRun.frame.size.height/2.0f)+0.5f; + + DTTextBlock *textBlock = [[oneRun.attributes objectForKey:DTTextBlocksAttribute] lastObject]; + + if (textBlock) + { + // apply horizontal padding + nrect.size.width = _frame.size.width - textBlock.padding.left - textBlock.padding.right; + } + + CGContextMoveToPoint(context, nrect.origin.x, nrect.origin.y); + CGContextAddLineToPoint(context, nrect.origin.x + nrect.size.width, nrect.origin.y); + + CGContextStrokePath(context); + + continue; + } + + // don't draw decorations on images + if (oneRun.attachment) + { + continue; + } + + // -------------- Line-Out, Underline, Background-Color + BOOL lastRunInLine = (oneRun == [oneLine.glyphRuns lastObject]); + + BOOL drawStrikeOut = [[oneRun.attributes objectForKey:DTStrikeOutAttribute] boolValue]; + BOOL drawUnderline = [[oneRun.attributes objectForKey:(id)kCTUnderlineStyleAttributeName] boolValue]; + + if (drawStrikeOut||drawUnderline||backgroundColor) + { + // get text color or use black + id color = [oneRun.attributes objectForKey:(id)kCTForegroundColorAttributeName]; + + if (color) + { + CGContextSetStrokeColorWithColor(context, (__bridge CGColorRef)color); + } + else + { + CGContextSetGrayStrokeColor(context, 0, 1.0); + } + + CGRect runStrokeBounds = oneRun.frame; + + NSInteger superscriptStyle = [[oneRun.attributes objectForKey:(id)kCTSuperscriptAttributeName] integerValue]; + + switch (superscriptStyle) + { + case 1: + { + runStrokeBounds.origin.y -= oneRun.ascent * 0.47f; + break; + } + case -1: + { + runStrokeBounds.origin.y += oneRun.ascent * 0.25f; + break; + } + default: + break; + } + + + if (lastRunInLine) + { + runStrokeBounds.size.width -= [oneLine trailingWhitespaceWidth]; + } + + if (backgroundColor) + { + CGContextSetFillColorWithColor(context, backgroundColor); + CGContextFillRect(context, runStrokeBounds); + } + + if (drawStrikeOut) + { + runStrokeBounds.origin.y = roundf(runStrokeBounds.origin.y + oneRun.frame.size.height/2.0f + 1)+0.5f; + + CGContextMoveToPoint(context, runStrokeBounds.origin.x, runStrokeBounds.origin.y); + CGContextAddLineToPoint(context, runStrokeBounds.origin.x + runStrokeBounds.size.width, runStrokeBounds.origin.y); + + CGContextStrokePath(context); + } + + if (drawUnderline) + { + runStrokeBounds.origin.y = roundf(runStrokeBounds.origin.y + oneRun.frame.size.height - oneRun.descent + 1)+0.5f; + + CGContextMoveToPoint(context, runStrokeBounds.origin.x, runStrokeBounds.origin.y); + CGContextAddLineToPoint(context, runStrokeBounds.origin.x + runStrokeBounds.size.width, runStrokeBounds.origin.y); + + CGContextStrokePath(context); + } + } + } + } + + // Flip the coordinate system + CGContextSetTextMatrix(context, CGAffineTransformIdentity); + CGContextScaleCTM(context, 1.0, -1.0); + CGContextTranslateCTM(context, 0, -self.frame.size.height); + + // instead of using the convenience method to draw the entire frame, we draw individual glyph runs + + for (DTCoreTextLayoutLine *oneLine in visibleLines) + { + for (DTCoreTextGlyphRun *oneRun in oneLine.glyphRuns) + { + if (!CGRectIntersectsRect(rect, oneRun.frame)) + { + continue; + } + + if (!drawLinks && oneRun.isHyperlink) + { + continue; + } + + CGPoint textPosition = CGPointMake(oneLine.frame.origin.x, self.frame.size.height - oneRun.frame.origin.y - oneRun.ascent); + + NSInteger superscriptStyle = [[oneRun.attributes objectForKey:(id)kCTSuperscriptAttributeName] integerValue]; + + switch (superscriptStyle) + { + case 1: + { + textPosition.y += oneRun.ascent * 0.47f; + break; + } + case -1: + { + textPosition.y -= oneRun.ascent * 0.25f; + break; + } + default: + break; + } + + CGContextSetTextPosition(context, textPosition.x, textPosition.y); + + DTTextAttachment *attachment = oneRun.attachment; + + if (attachment) + { + if (drawImages) + { + if (attachment.contentType == DTTextAttachmentTypeImage) + { + DTImage *image = (id)attachment.contents; + + // frame might be different due to image vertical alignment + CGFloat ascender = [attachment ascentForLayout]; + CGFloat descender = [attachment descentForLayout]; + + CGPoint origin = oneRun.frame.origin; + origin.y = self.frame.size.height - origin.y - ascender - descender; + CGRect flippedRect = CGRectMake(roundf(origin.x), roundf(origin.y), attachment.displaySize.width, attachment.displaySize.height); + + CGContextDrawImage(context, flippedRect, image.CGImage); + } + } + } + else + { + NSArray *shadows = [oneRun.attributes objectForKey:DTShadowsAttribute]; + + if (shadows) + { + CGContextSaveGState(context); + + for (NSDictionary *shadowDict in shadows) + { + [self _setShadowInContext:context fromDictionary:shadowDict]; + + // draw once per shadow + [oneRun drawInContext:context]; + } + + CGContextRestoreGState(context); + } + + // regular text + [oneRun drawInContext:context]; + } + } + } + + if (_textFrame) + { + CFRelease(_textFrame); + } + + UIGraphicsPopContext(); + CGContextRestoreGState(context); +} + +#pragma mark Text Attachments + +- (NSArray *)textAttachments +{ + if (!_textAttachments) + { + NSMutableArray *tmpAttachments = [NSMutableArray array]; + + for (DTCoreTextLayoutLine *oneLine in self.lines) + { + for (DTCoreTextGlyphRun *oneRun in oneLine.glyphRuns) + { + DTTextAttachment *attachment = [oneRun attachment]; + + if (attachment) + { + [tmpAttachments addObject:attachment]; + } + } + } + + _textAttachments = [[NSArray alloc] initWithArray:tmpAttachments]; + } + + + return _textAttachments; +} + +- (NSArray *)textAttachmentsWithPredicate:(NSPredicate *)predicate +{ + return [[self textAttachments] filteredArrayUsingPredicate:predicate]; +} + +#pragma mark Calculations + +- (NSRange)visibleStringRange +{ + if (!_textFrame) + { + return NSMakeRange(0, 0); + } + + // need to build lines to know range + if (!_lines) + { + [self _buildLines]; + } + + return _stringRange; +} + +- (NSArray *)stringIndices +{ + NSMutableArray *array = [NSMutableArray arrayWithCapacity:[self.lines count]]; + + for (DTCoreTextLayoutLine *oneLine in self.lines) + { + [array addObjectsFromArray:[oneLine stringIndices]]; + } + + return array; +} + +- (NSInteger)lineIndexForGlyphIndex:(NSInteger)index +{ + NSInteger retIndex = 0; + for (DTCoreTextLayoutLine *oneLine in self.lines) + { + NSInteger count = [oneLine numberOfGlyphs]; + if (index >= count) + { + index -= count; + } + else + { + return retIndex; + } + + retIndex++; + } + + return retIndex; +} + +- (CGRect)frameOfGlyphAtIndex:(NSInteger)index +{ + for (DTCoreTextLayoutLine *oneLine in self.lines) + { + NSInteger count = [oneLine numberOfGlyphs]; + if (index >= count) + { + index -= count; + } + else + { + return [oneLine frameOfGlyphAtIndex:index]; + } + } + + return CGRectNull; +} + +- (CGRect)frame +{ + if (_frame.size.height == CGFLOAT_OPEN_HEIGHT && !_lines) + { + [self _buildLines]; // corrects frame if open-ended + } + + if (![self.lines count]) + { + return CGRectZero; + } + + return _frame; +} + +- (DTCoreTextLayoutLine *)lineContainingIndex:(NSUInteger)index +{ + for (DTCoreTextLayoutLine *oneLine in self.lines) + { + if (NSLocationInRange(index, [oneLine stringRange])) + { + return oneLine; + } + } + + return nil; +} + +- (NSArray *)linesInParagraphAtIndex:(NSUInteger)index +{ + NSArray *paragraphRanges = self.paragraphRanges; + + NSAssert(index < [paragraphRanges count], @"index parameter out of range"); + + NSRange range = [[paragraphRanges objectAtIndex:index] rangeValue]; + + NSMutableArray *tmpArray = [NSMutableArray array]; + + // find lines that are in this range + + BOOL insideParagraph = NO; + + for (DTCoreTextLayoutLine *oneLine in self.lines) + { + if (NSLocationInRange([oneLine stringRange].location, range)) + { + insideParagraph = YES; + [tmpArray addObject:oneLine]; + } + else + { + if (insideParagraph) + { + // that means we left the range + + break; + } + } + } + + // return array only if there is something in it + if ([tmpArray count]) + { + return tmpArray; + } + else + { + return nil; + } +} + +// returns YES if the given line is the first in a paragraph +- (BOOL)isLineFirstInParagraph:(DTCoreTextLayoutLine *)line +{ + NSRange lineRange = line.stringRange; + + if (lineRange.location == 0) + { + return YES; + } + + NSInteger prevLineLastUnicharIndex =lineRange.location - 1; + unichar prevLineLastUnichar = [[_attributedStringFragment string] characterAtIndex:prevLineLastUnicharIndex]; + + return [[NSCharacterSet newlineCharacterSet] characterIsMember:prevLineLastUnichar]; +} + +// returns YES if the given line is the last in a paragraph +- (BOOL)isLineLastInParagraph:(DTCoreTextLayoutLine *)line +{ + NSString *lineString = [[_attributedStringFragment string] substringWithRange:line.stringRange]; + + if ([lineString hasSuffix:@"\n"]) + { + return YES; + } + + return NO; +} + +// finds the appropriate baseline origin for a line to position it at the correct distance from a previous line +- (CGPoint)baselineOriginToPositionLine:(DTCoreTextLayoutLine *)line afterLine:(DTCoreTextLayoutLine *)previousLine +{ + + CGPoint lineOrigin = previousLine.baselineOrigin; + + NSInteger lineStartIndex = line.stringRange.location; + + CTParagraphStyleRef lineParagraphStyle = (__bridge CTParagraphStyleRef)[_attributedStringFragment + attribute:(id)kCTParagraphStyleAttributeName + atIndex:lineStartIndex effectiveRange:NULL]; + + //Meet the first line in this frame + if (!previousLine) + { + // The first line may or may not be the start of paragraph. It depends on the the range passing to + // - (DTCoreTextLayoutFrame *)layoutFrameWithRect:(CGRect)frame range:(NSRange)range; + // So Check it in a safe way: + if ([self isLineFirstInParagraph:line]) + { + + CGFloat paraSpacingBefore = 0; + + if (CTParagraphStyleGetValueForSpecifier(lineParagraphStyle, kCTParagraphStyleSpecifierParagraphSpacingBefore, sizeof(paraSpacingBefore), ¶SpacingBefore)) + { + lineOrigin.y += paraSpacingBefore; + } + + // preserve own baseline x + lineOrigin.x = line.baselineOrigin.x; + + // origins are rounded + lineOrigin.y = ceilf(lineOrigin.y); + + return lineOrigin; + + } + + } + + // get line height in px if it is specified for this line + CGFloat lineHeight = 0; + CGFloat minLineHeight = 0; + CGFloat maxLineHeight = 0; + + CGFloat usedLeading = line.leading; + + if (usedLeading == 0.0f) + { + // font has no leading, so we fake one (e.g. Helvetica) + CGFloat tmpHeight = line.ascent + line.descent; + usedLeading = ceilf(0.2f * tmpHeight); + + if (usedLeading>20) + { + // we have a large image increasing the ascender too much for this calc to work + usedLeading = 0; + } + } + else + { + // make sure that we don't have less than 10% of line height as leading + usedLeading = ceilf(MAX((line.ascent + line.descent)*0.1f, usedLeading)); + } + + if (CTParagraphStyleGetValueForSpecifier(lineParagraphStyle, kCTParagraphStyleSpecifierMinimumLineHeight, sizeof(minLineHeight), &minLineHeight)) + { + if (lineHeight0.0f) + { + lineHeight *= lineHeightMultiplier; + } + } + + if (CTParagraphStyleGetValueForSpecifier(lineParagraphStyle, kCTParagraphStyleSpecifierMaximumLineHeight, sizeof(maxLineHeight), &maxLineHeight)) + { + if (maxLineHeight>0 && lineHeight>maxLineHeight) + { + lineHeight = maxLineHeight; + } + } + + lineOrigin.y += lineHeight; + + // preserve own baseline x + lineOrigin.x = line.baselineOrigin.x; + + // origins are rounded + lineOrigin.y = ceilf(lineOrigin.y); + + return lineOrigin; +} + +#pragma mark Paragraphs +- (NSUInteger)paragraphIndexContainingStringIndex:(NSUInteger)stringIndex +{ + for (NSValue *oneValue in self.paragraphRanges) + { + NSRange range = [oneValue rangeValue]; + + if (NSLocationInRange(stringIndex, range)) + { + return [self.paragraphRanges indexOfObject:oneValue]; + } + } + + return NSNotFound; +} + +- (NSRange)paragraphRangeContainingStringRange:(NSRange)stringRange +{ + NSUInteger firstParagraphIndex = [self paragraphIndexContainingStringIndex:stringRange.location]; + NSUInteger lastParagraphIndex; + + if (stringRange.length) + { + lastParagraphIndex = [self paragraphIndexContainingStringIndex:NSMaxRange(stringRange)-1]; + } + else + { + // range is in a single position, i.e. last paragraph has to be same as first + lastParagraphIndex = firstParagraphIndex; + } + + return NSMakeRange(firstParagraphIndex, lastParagraphIndex - firstParagraphIndex + 1); +} + +#pragma mark Debugging ++ (void)setShouldDrawDebugFrames:(BOOL)debugFrames +{ + _DTCoreTextLayoutFramesShouldDrawDebugFrames = debugFrames; +} + +#pragma mark Corrections +- (void)_correctAttachmentHeights +{ + CGFloat downShiftSoFar = 0; + + for (DTCoreTextLayoutLine *oneLine in self.lines) + { + CGFloat lineShift = 0; + if ([oneLine correctAttachmentHeights:&lineShift]) + { + downShiftSoFar += lineShift; + } + + if (downShiftSoFar>0) + { + // shift the frame baseline down for the total shift so far + CGPoint origin = oneLine.baselineOrigin; + origin.y += downShiftSoFar; + oneLine.baselineOrigin = origin; + + // increase the ascent by the extend needed for this lines attachments + oneLine.ascent += lineShift; + } + } +} + + +// a bug in CoreText shifts the last line of paragraphs slightly down +- (void)_correctLineOrigins +{ + DTCoreTextLayoutLine *previousLine = nil; + for (DTCoreTextLayoutLine *currentLine in self.lines) + { + // Since paragraphSpaceBefore can affect the first line in self.lines, (previousLine == nil) needs to be allowed. + currentLine.baselineOrigin = [self baselineOriginToPositionLine:currentLine afterLine:previousLine]; + + previousLine = currentLine; + } +} + +#pragma mark Properties +- (NSAttributedString *)attributedStringFragment +{ + return _attributedStringFragment; +} + +// builds an array +- (NSArray *)paragraphRanges +{ + if (!_paragraphRanges) + { + NSString *plainString = [[self attributedStringFragment] string]; + + NSArray *paragraphs = [plainString componentsSeparatedByString:@"\n"]; + NSRange range = NSMakeRange(0, 0); + NSMutableArray *tmpArray = [NSMutableArray array]; + + for (NSString *oneString in paragraphs) + { + range.length = [oneString length]+1; + + NSValue *value = [NSValue valueWithRange:range]; + [tmpArray addObject:value]; + + range.location += range.length; + } + + // prevent counting a paragraph after a final newline + if ([plainString hasSuffix:@"\n"]) + { + [tmpArray removeLastObject]; + } + + _paragraphRanges = [tmpArray copy]; + } + + return _paragraphRanges; +} + +@synthesize frame = _frame; +@synthesize lines = _lines; +@synthesize paragraphRanges = _paragraphRanges; +@synthesize textBlockHandler = _textBlockHandler; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTCoreTextLayoutLine.h b/Pods/DTCoreText/Core/Source/DTCoreTextLayoutLine.h new file mode 100644 index 00000000..35ff3a70 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCoreTextLayoutLine.h @@ -0,0 +1,72 @@ +// +// DTCoreTextLayoutLine.h +// CoreTextExtensions +// +// Created by Oliver Drobnik on 1/24/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + + +#if TARGET_OS_IPHONE +#import +#elif TARGET_OS_MAC +#import +#endif + +@class DTCoreTextLayoutFrame; + +@interface DTCoreTextLayoutLine : NSObject +{ + NSInteger _stringLocationOffset; // offset to modify internal string location to get actual location +} + +- (id)initWithLine:(CTLineRef)line; + +- (NSRange)stringRange; +- (NSInteger)numberOfGlyphs; +- (CGRect)frameOfGlyphAtIndex:(NSInteger)index; +- (NSArray *)glyphRunsWithRange:(NSRange)range; +- (CGRect)frameOfGlyphsWithRange:(NSRange)range; +- (CGRect)imageBoundsInContext:(CGContextRef)context; +- (NSArray *)stringIndices; +- (CGFloat)offsetForStringIndex:(NSInteger)index; +- (NSInteger)stringIndexForPosition:(CGPoint)position; + + +/** + @name Creating Variants + */ + +/** + Creates a version of the receiver that is justified to the given width. + + @param justificationFactor Full or partial justification. When set to `1.0` or greater, full justification is performed. If this parameter is set to less than `1.0`, varying degrees of partial justification are performed. If it is set to `0` or less, no justification is performed. + @param justificationWidth The width to which the resultant line is justified. If justificationWidth is less than the actual width of the line, then negative justification is performed (that is, glyphs are squeezed together). + */ +- (DTCoreTextLayoutLine *)justifiedLineWithFactor:(CGFloat)justificationFactor justificationWidth:(CGFloat)justificationWidth; + + + +- (void)drawInContext:(CGContextRef)context; + + +/** Adjust the baselines of all lines in this layout frame to fit the heights of text attachments. + + This is used to work around a CoreText bug that was fixed in iOS 4.2 + + @returns `YES` if the line needed an adjustment, `NO` if no adjustment was carried out + */ +- (BOOL)correctAttachmentHeights:(CGFloat *)downShift; + + +@property (nonatomic, assign) CGRect frame; +@property (nonatomic, strong, readonly) NSArray *glyphRuns; + +@property (nonatomic, assign) CGFloat ascent; // needs to be modifyable +@property (nonatomic, assign, readonly) CGFloat descent; +@property (nonatomic, assign, readonly) CGFloat leading; +@property (nonatomic, assign, readonly) CGFloat trailingWhitespaceWidth; + +@property (nonatomic, assign) CGPoint baselineOrigin; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTCoreTextLayoutLine.m b/Pods/DTCoreText/Core/Source/DTCoreTextLayoutLine.m new file mode 100644 index 00000000..b3d1ed6c --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCoreTextLayoutLine.m @@ -0,0 +1,444 @@ +// +// DTCoreTextLayoutLine.m +// CoreTextExtensions +// +// Created by Oliver Drobnik on 1/24/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import "DTCoreTextLayoutLine.h" +#import "DTCoreTextGlyphRun.h" +#import "DTCoreTextLayoutFrame.h" +#import "DTCoreTextLayouter.h" +#import "DTTextAttachment.h" + +@interface DTCoreTextLayoutLine () + +@property (nonatomic, strong) NSArray *glyphRuns; + +@end + +@implementation DTCoreTextLayoutLine +{ + CGRect _frame; + CTLineRef _line; + + CGPoint _baselineOrigin; + + CGFloat _ascent; + CGFloat _descent; + CGFloat _leading; + CGFloat _width; + CGFloat _trailingWhitespaceWidth; + + NSArray *_glyphRuns; + + BOOL _didCalculateMetrics; + dispatch_queue_t _syncQueue; +} + +- (id)initWithLine:(CTLineRef)line +{ + if ((self = [super init])) + { + _line = line; + CFRetain(_line); + + // get a global queue + _syncQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + } + return self; +} + +- (void)dealloc +{ + CFRelease(_line); +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@ origin=%@ frame=%@ range=%@", [self class], NSStringFromCGPoint(_baselineOrigin), NSStringFromCGRect(self.frame), NSStringFromRange([self stringRange])]; +} + +- (NSRange)stringRange +{ + CFRange range = CTLineGetStringRange(_line); + + // add offset if there is one, i.e. from merged lines + range.location += _stringLocationOffset; + + return NSMakeRange(range.location, range.length); +} + +- (NSInteger)numberOfGlyphs +{ + NSInteger ret = 0; + for (DTCoreTextGlyphRun *oneRun in self.glyphRuns) + { + ret += [oneRun numberOfGlyphs]; + } + + return ret; +} + +#pragma mark Creating Variants + +- (DTCoreTextLayoutLine *)justifiedLineWithFactor:(CGFloat)justificationFactor justificationWidth:(CGFloat)justificationWidth +{ + // make this line justified + CTLineRef justifiedLine = CTLineCreateJustifiedLine(_line, justificationFactor, justificationWidth); + + DTCoreTextLayoutLine *newLine = [[DTCoreTextLayoutLine alloc] initWithLine:justifiedLine]; + + CFRelease(justifiedLine); + + return newLine; +} + + +#pragma mark Calculations +- (NSArray *)stringIndices +{ + NSMutableArray *array = [NSMutableArray array]; + for (DTCoreTextGlyphRun *oneRun in self.glyphRuns) { + [array addObjectsFromArray:[oneRun stringIndices]]; + } + return array; +} + +- (CGRect)frameOfGlyphAtIndex:(NSInteger)index +{ + for (DTCoreTextGlyphRun *oneRun in self.glyphRuns) + { + NSInteger count = [oneRun numberOfGlyphs]; + if (index >= count) + { + index -= count; + } + else + { + return [oneRun frameOfGlyphAtIndex:index]; + } + } + + return CGRectZero; +} + +- (NSArray *)glyphRunsWithRange:(NSRange)range +{ + NSMutableArray *tmpArray = [NSMutableArray arrayWithCapacity:[self numberOfGlyphs]]; + + for (DTCoreTextGlyphRun *oneRun in self.glyphRuns) + { + NSRange runRange = [oneRun stringRange]; + + // we only care about locations, assume that number of glyphs >= indexes + if (NSLocationInRange(runRange.location, range)) + { + [tmpArray addObject:oneRun]; + } + } + + return tmpArray; +} + +- (CGRect)frameOfGlyphsWithRange:(NSRange)range +{ + NSArray *glyphRuns = [self glyphRunsWithRange:range]; + + CGRect tmpRect = CGRectMake(CGFLOAT_MAX, CGFLOAT_MAX, 0, 0); + + for (DTCoreTextGlyphRun *oneRun in glyphRuns) + { + CGRect glyphFrame = oneRun.frame; + + if (glyphFrame.origin.x < tmpRect.origin.x) + { + tmpRect.origin.x = glyphFrame.origin.x; + } + + if (glyphFrame.origin.y < tmpRect.origin.y) + { + tmpRect.origin.y = glyphFrame.origin.y; + } + + if (glyphFrame.size.height > tmpRect.size.height) + { + tmpRect.size.height = glyphFrame.size.height; + } + + tmpRect.size.width = glyphFrame.origin.x + glyphFrame.size.width - tmpRect.origin.x; + } + + CGFloat maxX = CGRectGetMaxX(self.frame) - _trailingWhitespaceWidth; + if (CGRectGetMaxX(tmpRect) > maxX) + { + tmpRect.size.width = maxX - tmpRect.origin.x; + } + + return tmpRect; +} + +// bounds of an image encompassing the entire run +- (CGRect)imageBoundsInContext:(CGContextRef)context +{ + return CTLineGetImageBounds(_line, context); +} + +- (CGFloat)offsetForStringIndex:(NSInteger)index +{ + // subtract offset if there is one, i.e. from merged lines + index -= _stringLocationOffset; + + return CTLineGetOffsetForStringIndex(_line, index, NULL); +} + +- (NSInteger)stringIndexForPosition:(CGPoint)position +{ + // position is in same coordinate system as frame + CGPoint adjustedPosition = position; + CGRect frame = self.frame; + adjustedPosition.x -= frame.origin.x; + adjustedPosition.y -= frame.origin.y; + + NSInteger index = CTLineGetStringIndexForPosition(_line, adjustedPosition); + + // add offset if there is one, i.e. from merged lines + index += _stringLocationOffset; + + return index; +} + +- (void)drawInContext:(CGContextRef)context +{ + CTLineDraw(_line, context); +} + +// fix for image squishing bug < iOS 4.2 +- (BOOL)correctAttachmentHeights:(CGFloat *)downShift +{ + // get the glyphRuns with attachments + NSArray *glyphRuns = [self glyphRuns]; + + CGFloat necessaryDownShift = 0; + BOOL didShift = NO; + + NSMutableSet *correctedRuns = [[NSMutableSet alloc] init]; + + + for (DTCoreTextGlyphRun *oneRun in glyphRuns) + { + DTTextAttachment *attachment = oneRun.attachment; + + if (attachment) + { + CGFloat currentGlyphHeight = oneRun.ascent; + CGFloat neededGlyphHeight = attachment.displaySize.height; + + if (neededGlyphHeight > currentGlyphHeight) + { + CGFloat ndownShift = neededGlyphHeight - currentGlyphHeight; + + if (ndownShift > necessaryDownShift) + { + necessaryDownShift = ndownShift; + didShift = YES; + + [correctedRuns addObject:oneRun]; + } + } + } + } + + // now fix the ascent of these runs + for (DTCoreTextGlyphRun *oneRun in correctedRuns) + { + [oneRun fixMetricsFromAttachment]; + } + + + // return executed shift + if (downShift) + { + *downShift = necessaryDownShift; + } + + return didShift; +} + +- (void)_calculateMetrics +{ + dispatch_sync(_syncQueue, ^{ + if (!_didCalculateMetrics) + { + _width = (CGFloat)CTLineGetTypographicBounds(_line, &_ascent, &_descent, &_leading); + _trailingWhitespaceWidth = (CGFloat)CTLineGetTrailingWhitespaceWidth(_line); + + _didCalculateMetrics = YES; + } + }); +} + + +// calculates the extra space that is before every line even though the leading is zero +// http://stackoverflow.com/questions/5511830/how-does-line-spacing-work-in-core-text-and-why-is-it-different-from-nslayoutm +- (CGFloat)calculatedLeading +{ + CGFloat maxLeading = 0; + + NSArray *glyphRuns = self.glyphRuns; + DTCoreTextGlyphRun *lastRunInLine = [glyphRuns lastObject]; + + for (DTCoreTextGlyphRun *oneRun in glyphRuns) + { + CGFloat runLeading = 0; + + if (oneRun.leading>0) + { + // take actual leading + runLeading = oneRun.leading; + } + else + { + // calculate a run leading as 20% from line height + + // for attachments the ascent equals the image height + // so we don't add the 20% + if (!oneRun.attachment) + { + if (oneRun == lastRunInLine && (oneRun.width==self.trailingWhitespaceWidth)) + { + // a whitespace glyph, e.g. \n + } + else + { + // calculate a leading as 20% of the line height + CGFloat lineHeight = roundf(oneRun.ascent) + roundf(oneRun.descent); + runLeading = roundf(0.2f * lineHeight); + } + } + } + + // remember the max + maxLeading = MAX(maxLeading, runLeading); + } + + return maxLeading; +} + + +#pragma mark Properties +- (NSArray *)glyphRuns +{ + dispatch_sync(_syncQueue, ^{ + if (!_glyphRuns) + { + // run array is owned by line + NSArray *runs = (__bridge NSArray *)CTLineGetGlyphRuns(_line); + + if (runs) + { + CGFloat offset = 0; + + NSMutableArray *tmpArray = [[NSMutableArray alloc] initWithCapacity:[runs count]]; + + for (id oneRun in runs) + { + DTCoreTextGlyphRun *glyphRun = [[DTCoreTextGlyphRun alloc] initWithRun:(__bridge CTRunRef)oneRun layoutLine:self offset:offset]; + [tmpArray addObject:glyphRun]; + + offset += glyphRun.frame.size.width; + } + + _glyphRuns = tmpArray; + } + } + }); + + return _glyphRuns; +} + +- (CGRect)frame +{ + if (!_didCalculateMetrics) + { + [self _calculateMetrics]; + } + + return CGRectMake(_baselineOrigin.x, _baselineOrigin.y - _ascent, _width, _ascent + _descent); +} + +- (CGFloat)width +{ + if (!_didCalculateMetrics) + { + [self _calculateMetrics]; + } + + return _width; +} + +- (CGFloat)ascent +{ + if (!_didCalculateMetrics) + { + [self _calculateMetrics]; + } + + return _ascent; +} + +- (void)setAscent:(CGFloat)ascent +{ + // need to get metrics because otherwise ascent gets overwritten + if (!_didCalculateMetrics) + { + [self _calculateMetrics]; + } + + _ascent = ascent; +} + + +- (CGFloat)descent +{ + if (!_didCalculateMetrics) + { + [self _calculateMetrics]; + } + + return _descent; +} + +- (CGFloat)leading +{ + if (!_didCalculateMetrics) + { + [self _calculateMetrics]; + } + + return _leading; +} + +- (CGFloat)trailingWhitespaceWidth +{ + if (!_didCalculateMetrics) + { + [self _calculateMetrics]; + } + + return _trailingWhitespaceWidth; +} + + +@synthesize frame =_frame; +@synthesize glyphRuns = _glyphRuns; + +@synthesize ascent = _ascent; +@synthesize descent = _descent; +@synthesize leading = _leading; +@synthesize trailingWhitespaceWidth = _trailingWhitespaceWidth; + +@synthesize baselineOrigin = _baselineOrigin; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTCoreTextLayouter.h b/Pods/DTCoreText/Core/Source/DTCoreTextLayouter.h new file mode 100644 index 00000000..d2212106 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCoreTextLayouter.h @@ -0,0 +1,39 @@ +// +// DTCoreTextLayouter.h +// CoreTextExtensions +// +// Created by Oliver Drobnik on 1/24/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + + + +#if TARGET_OS_IPHONE +#import +#elif TARGET_OS_MAC +#import +#endif + +#import "DTCoreTextLayoutFrame.h" +#import "DTCoreTextLayoutLine.h" +#import "DTCoreTextGlyphRun.h" + + +@interface DTCoreTextLayouter : NSObject + +- (id)initWithAttributedString:(NSAttributedString *)attributedString; + +- (CGSize)suggestedFrameSizeToFitEntireStringConstraintedToWidth:(CGFloat)width; + +- (NSInteger)numberOfFrames; +- (void)addTextFrameWithFrame:(CGRect)frame; + +- (DTCoreTextLayoutFrame *)layoutFrameWithRect:(CGRect)frame range:(NSRange)range; + +- (DTCoreTextLayoutFrame *)layoutFrameAtIndex:(NSInteger)index; + +@property (nonatomic, strong) NSAttributedString *attributedString; + +@property (nonatomic, readonly) CTFramesetterRef framesetter; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTCoreTextLayouter.m b/Pods/DTCoreText/Core/Source/DTCoreTextLayouter.m new file mode 100644 index 00000000..c3ccb8e7 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCoreTextLayouter.m @@ -0,0 +1,172 @@ +// +// DTCoreTextLayouter.m +// CoreTextExtensions +// +// Created by Oliver Drobnik on 1/24/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import "DTCoreTextLayouter.h" + +@interface DTCoreTextLayouter () + +@property (nonatomic, strong) NSMutableArray *frames; +@property (nonatomic, assign) dispatch_semaphore_t selfLock; + +- (CTFramesetterRef)framesetter; +- (void)discardFramesetter; + +@end + +#define SYNCHRONIZE_START(obj) dispatch_semaphore_wait(selfLock, DISPATCH_TIME_FOREVER); +#define SYNCHRONIZE_END(obj) dispatch_semaphore_signal(selfLock); + +@implementation DTCoreTextLayouter +{ + CTFramesetterRef _framesetter; + + NSAttributedString *_attributedString; + + NSMutableArray *frames; +} +@synthesize selfLock; + +- (id)initWithAttributedString:(NSAttributedString *)attributedString +{ + if ((self = [super init])) + { + if (!attributedString) + { + return nil; + } + + selfLock = dispatch_semaphore_create(1); + self.attributedString = attributedString; + } + + return self; +} + +- (void)dealloc +{ + SYNCHRONIZE_START(self) // just to be sure + [self discardFramesetter]; + SYNCHRONIZE_END(self) + + dispatch_release(selfLock); +} + +- (NSString *)description +{ + return [self.frames description]; +} + +- (NSInteger)numberOfFrames +{ + return [self.frames count]; +} + +- (CGSize)suggestedFrameSizeToFitEntireStringConstraintedToWidth:(CGFloat)width +{ + // Note: this returns an unreliable measure prior to 4.2 for very long documents + CGSize neededSize = CTFramesetterSuggestFrameSizeWithConstraints(self.framesetter, CFRangeMake(0, 0), NULL, + CGSizeMake(width, CGFLOAT_MAX), + NULL); + + // round up because generally we don't want non-integer view sizes + neededSize.width = ceilf(neededSize.width); + neededSize.height = ceilf(neededSize.height); + + return neededSize; +} + + +// a temporary frame +- (DTCoreTextLayoutFrame *)layoutFrameWithRect:(CGRect)frame range:(NSRange)range +{ + DTCoreTextLayoutFrame *newFrame; + @autoreleasepool { + newFrame = [[DTCoreTextLayoutFrame alloc] initWithFrame:frame layouter:self range:range]; + }; + return newFrame; +} + +// reusable frame +- (void)addTextFrameWithFrame:(CGRect)frame +{ + DTCoreTextLayoutFrame *newFrame = [self layoutFrameWithRect:frame range:NSMakeRange(0, 0)]; + [self.frames addObject:newFrame]; +} + +- (DTCoreTextLayoutFrame *)layoutFrameAtIndex:(NSInteger)index +{ + return [self.frames objectAtIndex:index]; + +} + +#pragma mark Properties +- (CTFramesetterRef)framesetter +{ + if (!_framesetter) // Race condition, could be null now but set when we get into the SYNCHRONIZE block - so do the test twice + { + SYNCHRONIZE_START(self) + { + if (!_framesetter) + { + _framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)self.attributedString); + } + } + SYNCHRONIZE_END(self) + } + return _framesetter; +} + + +- (void)discardFramesetter +{ + { + // framesetter needs to go + if (_framesetter) + { + CFRelease(_framesetter); + _framesetter = NULL; + } + } +} + +- (void)setAttributedString:(NSAttributedString *)attributedString +{ + SYNCHRONIZE_START(self) + { + if (_attributedString != attributedString) + { + _attributedString = attributedString; + + [self discardFramesetter]; + } + } + SYNCHRONIZE_END(self) +} + +- (NSAttributedString *)attributedString +{ + return _attributedString; +} + +- (NSMutableArray *)frames +{ + if (!frames) + { + frames = [[NSMutableArray alloc] init]; + } + + return frames; +} + + + +@synthesize attributedString = _attributedString; +@synthesize frames = _frames; +@synthesize framesetter = _framesetter; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTCoreTextParagraphStyle.h b/Pods/DTCoreText/Core/Source/DTCoreTextParagraphStyle.h new file mode 100644 index 00000000..1d5065d7 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCoreTextParagraphStyle.h @@ -0,0 +1,214 @@ +// +// DTCoreTextParagraphStyle.h +// CoreTextExtensions +// +// Created by Oliver Drobnik on 4/14/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +/** + `DTCoreTextParagraphStyle` encapsulates the paragraph or ruler attributes used by the NSAttributedString classes on iOS. It is a replacement for `NSParagraphStyle` which is not implemented on iOS. + + Since `NSAttributedString` instances use CTParagraphStyle object there are methods to bridge from and to these. Because of this distinction there is no need for a mutable variant of this class. + */ +@interface DTCoreTextParagraphStyle : NSObject + +/** + @name Creating a DTCoreTextParagraphStyle + */ + +/** + Returns the default paragraph style. + */ ++ (DTCoreTextParagraphStyle *)defaultParagraphStyle; + + + +/** + @name Bridging to and from CTParagraphStyle + */ + +/** + Create a new paragraph style instance from a `CTParagraphStyle`. + + @param ctParagraphStyle the `CTParagraphStyle` from which to copy this new style's attributes. + */ ++ (DTCoreTextParagraphStyle *)paragraphStyleWithCTParagraphStyle:(CTParagraphStyleRef)ctParagraphStyle; + + +/** + Create a new paragraph style instance from a `CTParagraphStyle`. + + @param ctParagraphStyle the `CTParagraphStyle` from which to copy this new style's attributes. + */ +- (id)initWithCTParagraphStyle:(CTParagraphStyleRef)ctParagraphStyle; + +/** + Create a new `CTParagraphStyle` from the receiver for use as attribute in `NSAttributedString` + + @returns The `CTParagraphStyle` based on the receiver's attributes. + */ +- (CTParagraphStyleRef)createCTParagraphStyle; + +/** + @name Bridging to and from NSParagraphStyle + */ + +#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_5_1 +/** + Create a new paragraph style instance from an `NSParagraphStyle`. + + Note: on iOS no tab stops are supported. + @param paragraphStyle the `NSParagraphStyle` from which to copy this new style's attributes. + */ ++ (DTCoreTextParagraphStyle *)paragraphStyleWithNSParagraphStyle:(NSParagraphStyle *)paragraphStyle; + +/** + Create a new `NSParagraphStyle` from the receiver for use as attribute in `NSAttributedString`. + + Note: This method is requires iOS 6 or greater. This does not support tab stops. + + @returns The `NSParagraphStyle` based on the receiver's attributes. + */ +- (NSParagraphStyle *)NSParagraphStyle; +#endif + + +/**------------------------------------------------------------------------------------- + @name Accessing Style Information + --------------------------------------------------------------------------------------- + */ + +/** + The indentation of the first line of the receiver. + */ +@property (nonatomic, assign) CGFloat firstLineHeadIndent; + + +/** + The document-wide default tab interval. + + The default tab interval in points. Tabs after the last specified in tabStops are placed at integer multiples of this distance (if positive). Default return value is 0.0. + */ +@property (nonatomic, assign) CGFloat defaultTabInterval; + + +/** + The distance between the paragraph’s top and the beginning of its text content. + */ +@property (nonatomic, assign) CGFloat paragraphSpacingBefore; + + +/** + The space after the end of the paragraph. + */ +@property (nonatomic, assign) CGFloat paragraphSpacing; + + +/** + The line height multiple. + + Internally line height multiples get converted into minimum and maximum line height. + */ +@property (nonatomic, assign) CGFloat lineHeightMultiple; + + +/** + The minimum height in points that any line in the receiver will occupy, regardless of the font size or size of any attached graphic. This value is always nonnegative. + */ +@property (nonatomic, assign) CGFloat minimumLineHeight; + + +/** + The maximum height in points that any line in the receiver will occupy, regardless of the font size or size of any attached graphic. This value is always nonnegative. The default value is 0. + */ +@property (nonatomic, assign) CGFloat maximumLineHeight; + + +/** +The distance in points from the margin of a text container to the end of lines. + */ +@property (nonatomic, assign) CGFloat tailIndent; + +/** + The distance in points from the leading margin of a text container to the beginning of lines other than the first. This value is always nonnegative. + */ +@property (nonatomic, assign) CGFloat headIndent; + +/** + The text alignment of the receiver. + + Natural text alignment is realized as left or right alignment depending on the line sweep direction of the first script contained in the paragraph. + */ +@property (nonatomic, assign) CTTextAlignment alignment; + + +/** + The base writing direction for the receiver. + +*/ +@property (nonatomic, assign) CTWritingDirection baseWritingDirection; + + +/**------------------------------------------------------------------------------------- + @name Setting Tab Stops + --------------------------------------------------------------------------------------- + */ + +/** + The CTTextTab objects, sorted by location, that define the tab stops for the paragraph style. + */ +@property (nonatomic, copy) NSArray *tabStops; + + +/** + Adds a tab stop to the receiver. + + @param position the tab stop position + @param alignment the tab alignment for this tab stop + */ +- (void)addTabStopAtPosition:(CGFloat)position alignment:(CTTextAlignment)alignment; + + +/**------------------------------------------------------------------------------------- + @name Interacting with CSS + --------------------------------------------------------------------------------------- + */ + +/** + Create a representation suitable for CSS. + + @returns A string with the receiver's style encoded as CSS. + */ +- (NSString *)cssStyleRepresentation; + + +/**------------------------------------------------------------------------------------- + @name Setting Text Lists + --------------------------------------------------------------------------------------- + */ + +/** + Text lists containing the paragraph, nested from outermost to innermost, to array. +*/ +@property (nonatomic, copy) NSArray *textLists; + + +/** + The amount by which each list level is indented from the previous. NOTE: about to be replaced by textLists property. + */ +@property (nonatomic, assign) CGFloat listIndent; + + +/**------------------------------------------------------------------------------------- + @name Setting Text Blocks + --------------------------------------------------------------------------------------- + */ + +/** + Text lists containing the paragraph, nested from outermost to innermost, to array. + */ +@property (nonatomic, copy) NSArray *textBlocks; + + +@end diff --git a/Pods/DTCoreText/Core/Source/DTCoreTextParagraphStyle.m b/Pods/DTCoreText/Core/Source/DTCoreTextParagraphStyle.m new file mode 100644 index 00000000..2b5f1868 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTCoreTextParagraphStyle.m @@ -0,0 +1,410 @@ +// +// DTCoreTextParagraphStyle.m +// CoreTextExtensions +// +// Created by Oliver Drobnik on 4/14/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import "DTCoreTextParagraphStyle.h" + +@implementation DTCoreTextParagraphStyle +{ + CGFloat _firstLineHeadIndent; + CGFloat _defaultTabInterval; + CGFloat _paragraphSpacingBefore; + CGFloat _paragraphSpacing; + CGFloat _headIndent; + CGFloat _tailIndent; + CGFloat _listIndent; + CGFloat _lineHeightMultiple; + CGFloat _minimumLineHeight; + CGFloat _maximumLineHeight; + + CTTextAlignment _alignment; + CTWritingDirection _baseWritingDirection; + + NSMutableArray *_tabStops; +} + ++ (DTCoreTextParagraphStyle *)defaultParagraphStyle +{ + return [[DTCoreTextParagraphStyle alloc] init]; +} + ++ (DTCoreTextParagraphStyle *)paragraphStyleWithCTParagraphStyle:(CTParagraphStyleRef)ctParagraphStyle +{ + return [[DTCoreTextParagraphStyle alloc] initWithCTParagraphStyle:ctParagraphStyle]; +} + +#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_5_1 ++ (DTCoreTextParagraphStyle *)paragraphStyleWithNSParagraphStyle:(NSParagraphStyle *)paragraphStyle +{ + DTCoreTextParagraphStyle *retStyle = [[DTCoreTextParagraphStyle alloc] init]; + + retStyle.firstLineHeadIndent = paragraphStyle.firstLineHeadIndent; + retStyle.headIndent = paragraphStyle.headIndent; + + retStyle.paragraphSpacing = paragraphStyle.paragraphSpacing; + retStyle.paragraphSpacingBefore = paragraphStyle.paragraphSpacingBefore; + + retStyle.lineHeightMultiple = paragraphStyle.lineHeightMultiple; + retStyle.minimumLineHeight = paragraphStyle.minimumLineHeight; + retStyle.maximumLineHeight = paragraphStyle.maximumLineHeight; + + switch(paragraphStyle.alignment) + { + case NSTextAlignmentLeft: + retStyle.alignment = kCTLeftTextAlignment; + break; + case NSTextAlignmentRight: + retStyle.alignment = kCTRightTextAlignment; + break; + case NSTextAlignmentCenter: + retStyle.alignment = kCTCenterTextAlignment; + break; + case NSTextAlignmentJustified: + retStyle.alignment = kCTJustifiedTextAlignment; + break; + case NSTextAlignmentNatural: + retStyle.alignment = kCTNaturalTextAlignment; + break; + } + + switch (paragraphStyle.baseWritingDirection) + { + case NSWritingDirectionNatural: + retStyle.baseWritingDirection = kCTWritingDirectionNatural; + break; + case NSWritingDirectionLeftToRight: + retStyle.baseWritingDirection = kCTWritingDirectionLeftToRight; + break; + case NSWritingDirectionRightToLeft: + retStyle.baseWritingDirection = kCTWritingDirectionRightToLeft; + break; + } + + return retStyle; +} +#endif + +- (id)init +{ + if ((self = [super init])) + { + // defaults + _firstLineHeadIndent = 0.0; + _defaultTabInterval = 36.0; + _baseWritingDirection = kCTWritingDirectionNatural; + _alignment = kCTNaturalTextAlignment; + _lineHeightMultiple = 0.0; + _minimumLineHeight = 0.0; + _maximumLineHeight = 0.0; + _paragraphSpacing = 0.0; + _listIndent = 0; + } + + return self; +} + + +- (id)initWithCTParagraphStyle:(CTParagraphStyleRef)ctParagraphStyle +{ + if ((self = [super init])) + { + // text alignment + CTParagraphStyleGetValueForSpecifier(ctParagraphStyle, kCTParagraphStyleSpecifierAlignment,sizeof(_alignment), &_alignment); + + // indents + CTParagraphStyleGetValueForSpecifier(ctParagraphStyle, kCTParagraphStyleSpecifierFirstLineHeadIndent, sizeof(_firstLineHeadIndent), &_firstLineHeadIndent); + CTParagraphStyleGetValueForSpecifier(ctParagraphStyle, kCTParagraphStyleSpecifierHeadIndent, sizeof(_headIndent), &_headIndent); + CTParagraphStyleGetValueForSpecifier(ctParagraphStyle, kCTParagraphStyleSpecifierTailIndent, sizeof(_tailIndent), &_tailIndent); + + // paragraph spacing + CTParagraphStyleGetValueForSpecifier(ctParagraphStyle, kCTParagraphStyleSpecifierParagraphSpacing, sizeof(_paragraphSpacing), &_paragraphSpacing); + CTParagraphStyleGetValueForSpecifier(ctParagraphStyle, kCTParagraphStyleSpecifierParagraphSpacingBefore,sizeof(_paragraphSpacingBefore), &_paragraphSpacingBefore); + + + // tab stops + CTParagraphStyleGetValueForSpecifier(ctParagraphStyle, kCTParagraphStyleSpecifierDefaultTabInterval, sizeof(_defaultTabInterval), &_defaultTabInterval); + + __unsafe_unretained NSArray *stops; // Could use a CFArray too, leave as a reminder how to do this in the future + if (CTParagraphStyleGetValueForSpecifier(ctParagraphStyle, kCTParagraphStyleSpecifierTabStops, sizeof(stops), &stops)) + { + self.tabStops = stops; + } + + + CTParagraphStyleGetValueForSpecifier(ctParagraphStyle, kCTParagraphStyleSpecifierBaseWritingDirection, sizeof(_baseWritingDirection), &_baseWritingDirection); + + // line height + CTParagraphStyleGetValueForSpecifier(ctParagraphStyle, kCTParagraphStyleSpecifierMinimumLineHeight, sizeof(_minimumLineHeight), &_minimumLineHeight); + CTParagraphStyleGetValueForSpecifier(ctParagraphStyle, kCTParagraphStyleSpecifierMaximumLineHeight, sizeof(_maximumLineHeight), &_maximumLineHeight); + + + CTParagraphStyleGetValueForSpecifier(ctParagraphStyle, kCTParagraphStyleSpecifierLineHeightMultiple, sizeof(_lineHeightMultiple), &_lineHeightMultiple); + + if (_lineHeightMultiple) + { + // paragraph space is pre-multiplied + if (_paragraphSpacing) + { + _paragraphSpacing /= _lineHeightMultiple; + } + + if (_paragraphSpacingBefore) + { + _paragraphSpacingBefore /= _lineHeightMultiple; + } + } + } + + return self; +} + +- (CTParagraphStyleRef)createCTParagraphStyle +{ + // need to multiple paragraph spacing with line height multiplier + float tmpParagraphSpacing = _paragraphSpacing; + float tmpParagraphSpacingBefore = _paragraphSpacingBefore; + + if (_lineHeightMultiple&&(_lineHeightMultiple!=1.0)) + { + tmpParagraphSpacing *= _lineHeightMultiple; + tmpParagraphSpacingBefore *= _lineHeightMultiple; + } + + // This just makes it that much easier to track down memory issues with tabstops + CFArrayRef stops = _tabStops ? CFArrayCreateCopy (NULL, (__bridge CFArrayRef)_tabStops) : NULL; + + CTParagraphStyleSetting settings[] = + { + {kCTParagraphStyleSpecifierAlignment, sizeof(_alignment), &_alignment}, + {kCTParagraphStyleSpecifierFirstLineHeadIndent, sizeof(_firstLineHeadIndent), &_firstLineHeadIndent}, + {kCTParagraphStyleSpecifierDefaultTabInterval, sizeof(_defaultTabInterval), &_defaultTabInterval}, + + {kCTParagraphStyleSpecifierTabStops, sizeof(stops), &stops}, + + {kCTParagraphStyleSpecifierParagraphSpacing, sizeof(tmpParagraphSpacing), &tmpParagraphSpacing}, + {kCTParagraphStyleSpecifierParagraphSpacingBefore, sizeof(tmpParagraphSpacingBefore), &tmpParagraphSpacingBefore}, + + {kCTParagraphStyleSpecifierHeadIndent, sizeof(_headIndent), &_headIndent}, + {kCTParagraphStyleSpecifierTailIndent, sizeof(_tailIndent), &_tailIndent}, + {kCTParagraphStyleSpecifierBaseWritingDirection, sizeof(_baseWritingDirection), &_baseWritingDirection}, + {kCTParagraphStyleSpecifierLineHeightMultiple, sizeof(_lineHeightMultiple), &_lineHeightMultiple}, + + {kCTParagraphStyleSpecifierMinimumLineHeight, sizeof(_minimumLineHeight), &_minimumLineHeight}, + {kCTParagraphStyleSpecifierMaximumLineHeight, sizeof(_maximumLineHeight), &_maximumLineHeight} + }; + + CTParagraphStyleRef ret = CTParagraphStyleCreate(settings, 12); + if (stops) CFRelease(stops); + + return ret; +} + +#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_5_1 +- (NSParagraphStyle *)NSParagraphStyle +{ + NSMutableParagraphStyle *mps = [[NSMutableParagraphStyle alloc] init]; + + [mps setFirstLineHeadIndent:_firstLineHeadIndent]; + + // _defaultTabInterval not supported + + [mps setParagraphSpacing:_paragraphSpacing]; + [mps setParagraphSpacingBefore:_paragraphSpacingBefore]; + + [mps setHeadIndent:_headIndent]; + [mps setTailIndent:_tailIndent]; + + // _listIndent not supported + + [mps setMinimumLineHeight:_minimumLineHeight]; + [mps setMaximumLineHeight:_maximumLineHeight]; + + switch(_alignment) + { + case kCTLeftTextAlignment: + [mps setAlignment:NSTextAlignmentLeft]; + break; + case kCTRightTextAlignment: + [mps setAlignment:NSTextAlignmentRight]; + break; + case kCTCenterTextAlignment: + [mps setAlignment:NSTextAlignmentCenter]; + break; + case kCTJustifiedTextAlignment: + [mps setAlignment:NSTextAlignmentJustified]; + break; + case kCTNaturalTextAlignment: + [mps setAlignment:NSTextAlignmentNatural]; + break; + } + + switch (_baseWritingDirection) { + case kCTWritingDirectionNatural: + [mps setBaseWritingDirection:NSWritingDirectionNatural]; + break; + case kCTWritingDirectionLeftToRight: + [mps setBaseWritingDirection:NSWritingDirectionLeftToRight]; + break; + case kCTWritingDirectionRightToLeft: + [mps setBaseWritingDirection:NSWritingDirectionRightToLeft]; + break; + } + + // _tap stops not supported + return (NSParagraphStyle *)mps; +} +#endif + +- (void)addTabStopAtPosition:(CGFloat)position alignment:(CTTextAlignment)alignment +{ + CTTextTabRef tab = CTTextTabCreate(alignment, position, NULL); + if(tab) + { + if (!_tabStops) + { + _tabStops = [[NSMutableArray alloc] init]; + } + [_tabStops addObject:CFBridgingRelease(tab)]; + } +} + +#pragma mark HTML Encoding + +// representation of this paragraph style in css (as far as possible) +- (NSString *)cssStyleRepresentation +{ + NSMutableString *retString = [NSMutableString string]; + + switch (_alignment) + { + case kCTLeftTextAlignment: + [retString appendString:@"text-align:left;"]; + break; + case kCTRightTextAlignment: + [retString appendString:@"text-align:right;"]; + break; + case kCTCenterTextAlignment: + [retString appendString:@"text-align:center;"]; + break; + case kCTJustifiedTextAlignment: + [retString appendString:@"text-align:justify;"]; + break; + case kCTNaturalTextAlignment: + // no output, this is default + break; + } + + if (_lineHeightMultiple && _lineHeightMultiple!=1.0f) + { + NSNumber *number = [NSNumber numberWithFloat:_lineHeightMultiple]; + [retString appendFormat:@"line-height:%@em;", number]; + } + + switch (_baseWritingDirection) + { + case kCTWritingDirectionRightToLeft: + [retString appendString:@"direction:rtl;"]; + break; + case kCTWritingDirectionLeftToRight: + [retString appendString:@"direction:ltr;"]; + break; + case kCTWritingDirectionNatural: + // no output, this is default + break; + } + + // Spacing at the bottom + if (_paragraphSpacing!=0.0f) + { + NSNumber *number = [NSNumber numberWithFloat:_paragraphSpacing]; + [retString appendFormat:@"margin-bottom:%@px;", number]; + } + + // Spacing at the top + if (_paragraphSpacingBefore!=0.0f) + { + NSNumber *number = [NSNumber numberWithFloat:_paragraphSpacingBefore]; + [retString appendFormat:@"margin-top:%@px;", number]; + } + + // Spacing at the left + if (_headIndent!=0.0f) + { + NSNumber *number = [NSNumber numberWithFloat:_headIndent]; + [retString appendFormat:@"margin-left:%@px;", number]; + } + + // Spacing at the right + if (_tailIndent!=0.0f) + { + NSNumber *number = [NSNumber numberWithFloat:_tailIndent]; + [retString appendFormat:@"margin-right:%@px;", number]; + } + + // return nil if no content + if ([retString length]) + { + return retString; + } + else + { + return nil; + } +} + +#pragma mark Copying + +- (id)copyWithZone:(NSZone *)zone +{ + DTCoreTextParagraphStyle *newObject = [[DTCoreTextParagraphStyle allocWithZone:zone] init]; + + newObject.firstLineHeadIndent = self.firstLineHeadIndent; + newObject.tailIndent = self.tailIndent; + newObject.defaultTabInterval = self.defaultTabInterval; + newObject.paragraphSpacing = self.paragraphSpacing; + newObject.paragraphSpacingBefore = self.paragraphSpacingBefore; + newObject.lineHeightMultiple = self.lineHeightMultiple; + newObject.minimumLineHeight = self.minimumLineHeight; + newObject.maximumLineHeight = self.maximumLineHeight; + newObject.headIndent = self.headIndent; + newObject.listIndent = self.listIndent; + newObject.alignment = self.alignment; + newObject.baseWritingDirection = self.baseWritingDirection; + newObject.tabStops = self.tabStops; // copy + newObject.textLists = self.textLists; //copy + newObject.textBlocks = self.textBlocks; //copy + + return newObject; +} + +#pragma mark Properties + +- (void)setTabStops:(NSArray *)tabStops +{ + if (tabStops != _tabStops) + { + _tabStops = [tabStops mutableCopy]; // keep mutability + } +} + +@synthesize firstLineHeadIndent = _firstLineHeadIndent; +@synthesize defaultTabInterval = _defaultTabInterval; +@synthesize paragraphSpacingBefore = _paragraphSpacingBefore; +@synthesize paragraphSpacing = _paragraphSpacing; +@synthesize lineHeightMultiple = _lineHeightMultiple; +@synthesize minimumLineHeight = _minimumLineHeight; +@synthesize maximumLineHeight = _maximumLineHeight; +@synthesize headIndent = _headIndent; +@synthesize tailIndent = _tailIndent; +@synthesize listIndent = _listIndent; +@synthesize alignment = _alignment; +@synthesize textLists = _textLists; +@synthesize textBlocks = _textBlocks; +@synthesize baseWritingDirection = _baseWritingDirection; +@synthesize tabStops = _tabStops; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTHTMLAttributedStringBuilder.h b/Pods/DTCoreText/Core/Source/DTHTMLAttributedStringBuilder.h new file mode 100644 index 00000000..29edb341 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTHTMLAttributedStringBuilder.h @@ -0,0 +1,56 @@ +// +// DTHTMLAttributedStringBuilder.h +// DTCoreText +// +// Created by Oliver Drobnik on 21.01.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import "DTHTMLParser.h" + +@class DTHTMLElement; + +typedef void(^DTHTMLAttributedStringBuilderWillFlushCallback)(DTHTMLElement *); + + +/** + Class for building an `NSAttributedString` from an HTML document. + */ +@interface DTHTMLAttributedStringBuilder : NSObject + +/** + @name Creating an Attributed String Builder + */ + +/** + Initializes and returns a new `NSAttributedString` object from the HTML contained in the given object and base URL. + @param data The data in HTML format from which to create the attributed string. + @param options Specifies how the document should be loaded. Contains values described in “Option keys for importing documents.” + @param docAttributes Currently not in used. + @returns Returns an initialized object, or `nil` if the data can’t be decoded. + */ +- (id)initWithHTML:(NSData *)data options:(NSDictionary *)options documentAttributes:(NSDictionary **)docAttributes; + + +/** + @name Generating Attributed Strings + */ + +/** + Creates the attributed string when called the first time. + @returns An `NSAttributedString` representing the HTML document passed in the initializer. + */ +- (NSAttributedString *)generatedAttributedString; + + +/** + This block is called before the element is written to the output attributed string + */ +@property (nonatomic, copy) DTHTMLAttributedStringBuilderWillFlushCallback willFlushCallback; + +/** + Setting this property to `YES` causes the tree of parse nodes to be preserved until the end of the generation process. This allows to output the HTML structure of the document for debugging. + */ +@property (nonatomic, assign) BOOL shouldKeepDocumentNodeTree; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTHTMLAttributedStringBuilder.m b/Pods/DTCoreText/Core/Source/DTHTMLAttributedStringBuilder.m new file mode 100644 index 00000000..29a9eaf2 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTHTMLAttributedStringBuilder.m @@ -0,0 +1,771 @@ +// +// DTHTMLAttributedStringBuilder.m +// DTCoreText +// +// Created by Oliver Drobnik on 21.01.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import "DTCoreText.h" +#import "DTHTMLAttributedStringBuilder.h" +#import "DTFoundation.h" + +#import "DTHTMLElementText.h" +#import "DTHTMLElementBR.h" +#import "DTHTMLElementStylesheet.h" + +@interface DTHTMLAttributedStringBuilder () + +- (void)_registerTagStartHandlers; +- (void)_registerTagEndHandlers; + +@end + + +@implementation DTHTMLAttributedStringBuilder +{ + NSData *_data; + NSDictionary *_options; + + // settings for parsing + CGFloat _textScale; + DTColor *_defaultLinkColor; + DTCSSStylesheet *_globalStyleSheet; + NSURL *_baseURL; + DTCoreTextFontDescriptor *_defaultFontDescriptor; + DTCoreTextParagraphStyle *_defaultParagraphStyle; + + // parsing state, accessed from inside blocks + NSMutableAttributedString *_tmpString; + + // GCD + dispatch_queue_t _stringAssemblyQueue; + dispatch_group_t _stringAssemblyGroup; + dispatch_queue_t _stringParsingQueue; + dispatch_group_t _stringParsingGroup; + + // lookup table for blocks that deal with begin and end tags + NSMutableDictionary *_tagStartHandlers; + NSMutableDictionary *_tagEndHandlers; + + DTHTMLAttributedStringBuilderWillFlushCallback _willFlushCallback; + + // new parsing + DTHTMLElement *_rootNode; + DTHTMLElement *_bodyElement; + DTHTMLElement *_currentTag; + NSMutableArray *_outputQueue; + + DTHTMLElement *_defaultTag; // root node inherits these defaults + BOOL _shouldKeepDocumentNodeTree; +} + +- (id)initWithHTML:(NSData *)data options:(NSDictionary *)options documentAttributes:(NSDictionary **)docAttributes +{ + self = [super init]; + if (self) + { + _data = data; + _options = options; + + // documentAttributes ignored for now + + //GCD setup + _stringAssemblyQueue = dispatch_queue_create("DTHTMLAttributedStringBuilder", 0); + _stringAssemblyGroup = dispatch_group_create(); + _stringParsingQueue = dispatch_queue_create("DTHTMLAttributedStringBuilderParser", 0); + _stringParsingGroup = dispatch_group_create(); + } + + return self; +} + +- (void)dealloc +{ +#if TARGET_API_MAC_OSX +#if MAC_OS_X_VERSION_MIN_REQUIRED < 1080 + dispatch_release(_stringAssemblyQueue); + dispatch_release(_stringAssemblyGroup); + dispatch_release(_stringParsingQueue); + dispatch_release(_stringParsingGroup); +#endif +#endif +} + +- (BOOL)_buildString +{ + // only with valid data + if (![_data length]) + { + return NO; + } + + // register default handlers + [self _registerTagStartHandlers]; + [self _registerTagEndHandlers]; + + // Specify the appropriate text encoding for the passed data, default is UTF8 + NSString *textEncodingName = [_options objectForKey:NSTextEncodingNameDocumentOption]; + NSStringEncoding encoding = NSUTF8StringEncoding; // default + + if (textEncodingName) + { + CFStringEncoding cfEncoding = CFStringConvertIANACharSetNameToEncoding((__bridge CFStringRef)textEncodingName); + encoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding); + } + + // custom option to use iOS 6 attributes if running on iOS 6 + if ([[_options objectForKey:DTUseiOS6Attributes] boolValue]) + { + if (![DTVersion osVersionIsLessThen:@"6.0"]) + { + ___useiOS6Attributes = YES; + } + else + { + ___useiOS6Attributes = NO; + } + } + else + { + // default is not to use them because many features are not supported + ___useiOS6Attributes = NO; + } + + // custom option to scale text + _textScale = [[_options objectForKey:NSTextSizeMultiplierDocumentOption] floatValue]; + if (!_textScale) + { + _textScale = 1.0f; + } + + // use baseURL from options if present + _baseURL = [_options objectForKey:NSBaseURLDocumentOption]; + + // the combined style sheet for entire document + _globalStyleSheet = [[DTCSSStylesheet defaultStyleSheet] copy]; + + // do we have a default style sheet passed as option? + DTCSSStylesheet *defaultStylesheet = [_options objectForKey:DTDefaultStyleSheet]; + if (defaultStylesheet) + { + // merge the default styles to the combined style sheet + [_globalStyleSheet mergeStylesheet:defaultStylesheet]; + } + + // for performance reasons we will return this mutable string + _tmpString = [[NSMutableAttributedString alloc] init]; + + // base tag with font defaults + _defaultFontDescriptor = [[DTCoreTextFontDescriptor alloc] initWithFontAttributes:nil]; + _defaultFontDescriptor.pointSize = 12.0f * _textScale; + + NSString *defaultFontFamily = [_options objectForKey:DTDefaultFontFamily]; + if (defaultFontFamily) + { + _defaultFontDescriptor.fontFamily = defaultFontFamily; + } + else + { + _defaultFontDescriptor.fontFamily = @"Times New Roman"; + } + + _defaultLinkColor = [_options objectForKey:DTDefaultLinkColor]; + + if (_defaultLinkColor) + { + if ([_defaultLinkColor isKindOfClass:[NSString class]]) + { + // convert from string to color + _defaultLinkColor = [DTColor colorWithHTMLName:(NSString *)_defaultLinkColor]; + } + + // get hex code for the passed color + NSString *colorHex = [_defaultLinkColor htmlHexString]; + + // overwrite the style + NSString *styleBlock = [NSString stringWithFormat:@"a {color:#%@;}", colorHex]; + [_globalStyleSheet parseStyleBlock:styleBlock]; + } + + // default is to have A underlined + NSNumber *linkDecorationDefault = [_options objectForKey:DTDefaultLinkDecoration]; + + if (linkDecorationDefault) + { + if (![linkDecorationDefault boolValue]) + { + // remove default decoration + [_globalStyleSheet parseStyleBlock:@"a {text-decoration:none;}"]; + } + } + + // default paragraph style + _defaultParagraphStyle = [DTCoreTextParagraphStyle defaultParagraphStyle]; + + NSNumber *defaultLineHeightMultiplierNum = [_options objectForKey:DTDefaultLineHeightMultiplier]; + + if (defaultLineHeightMultiplierNum) + { + CGFloat defaultLineHeightMultiplier = [defaultLineHeightMultiplierNum floatValue]; + _defaultParagraphStyle.lineHeightMultiple = defaultLineHeightMultiplier; + } + + NSNumber *defaultTextAlignmentNum = [_options objectForKey:DTDefaultTextAlignment]; + + if (defaultTextAlignmentNum) + { + _defaultParagraphStyle.alignment = (CTTextAlignment)[defaultTextAlignmentNum integerValue]; + } + + NSNumber *defaultFirstLineHeadIndent = [_options objectForKey:DTDefaultFirstLineHeadIndent]; + if (defaultFirstLineHeadIndent) + { + _defaultParagraphStyle.firstLineHeadIndent = [defaultFirstLineHeadIndent integerValue]; + } + + NSNumber *defaultHeadIndent = [_options objectForKey:DTDefaultHeadIndent]; + if (defaultHeadIndent) + { + _defaultParagraphStyle.headIndent = [defaultHeadIndent integerValue]; + } + + NSNumber *defaultListIndent = [_options objectForKey:DTDefaultListIndent]; + if (defaultListIndent) + { + _defaultParagraphStyle.listIndent = [defaultListIndent integerValue]; + } + + _defaultTag = [[DTHTMLElement alloc] init]; + _defaultTag.fontDescriptor = _defaultFontDescriptor; + _defaultTag.paragraphStyle = _defaultParagraphStyle; + _defaultTag.textScale = _textScale; + + id defaultColor = [_options objectForKey:DTDefaultTextColor]; + if (defaultColor) + { + if ([defaultColor isKindOfClass:[DTColor class]]) + { + // already a DTColor + _defaultTag.textColor = defaultColor; + } + else + { + // need to convert first + _defaultTag.textColor = [DTColor colorWithHTMLName:defaultColor]; + } + } + + _outputQueue = [[NSMutableArray alloc] init]; + + // create a parser + DTHTMLParser *parser = [[DTHTMLParser alloc] initWithData:_data encoding:encoding]; + parser.delegate = (id)self; + + __block BOOL result; + dispatch_group_async(_stringParsingGroup, _stringParsingQueue, ^{ result = [parser parse]; }); + + // wait until all string assembly is complete + dispatch_group_wait(_stringParsingGroup, DISPATCH_TIME_FOREVER); + dispatch_group_wait(_stringAssemblyGroup, DISPATCH_TIME_FOREVER); + + // clean up handlers because they retained self + _tagStartHandlers = nil; + _tagEndHandlers = nil; + + return result; +} + +- (NSAttributedString *)generatedAttributedString +{ + if (!_tmpString) + { + [self _buildString]; + } + + return _tmpString; +} + +#pragma mark GCD + +- (void)_registerTagStartHandlers +{ + if (_tagStartHandlers) + { + return; + } + + _tagStartHandlers = [[NSMutableDictionary alloc] init]; + + + void (^blockquoteBlock)(void) = ^ + { + _currentTag.paragraphStyle.headIndent += 25.0 * _textScale; + _currentTag.paragraphStyle.firstLineHeadIndent = _currentTag.paragraphStyle.headIndent; + _currentTag.paragraphStyle.paragraphSpacing = _defaultFontDescriptor.pointSize; + }; + + [_tagStartHandlers setObject:[blockquoteBlock copy] forKey:@"blockquote"]; + + + void (^aBlock)(void) = ^ + { + if (_currentTag.isColorInherited || !_currentTag.textColor) + { + _currentTag.textColor = _defaultLinkColor; + _currentTag.isColorInherited = NO; + } + + // remove line breaks and whitespace in links + NSString *cleanString = [[_currentTag attributeForKey:@"href"] stringByReplacingOccurrencesOfString:@"\n" withString:@""]; + cleanString = [cleanString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; + + NSURL *link = [NSURL URLWithString:cleanString]; + + // deal with relative URL + if (![link scheme]) + { + if ([cleanString length]) + { + link = [NSURL URLWithString:cleanString relativeToURL:_baseURL]; + + if (!link) + { + // NSURL did not like the link, so let's encode it + cleanString = [cleanString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + + link = [NSURL URLWithString:cleanString relativeToURL:_baseURL]; + } + } + else + { + link = _baseURL; + } + } + + _currentTag.link = link; + + // the name attribute of A becomes an anchor + _currentTag.anchorName = [_currentTag attributeForKey:@"name"]; + }; + + [_tagStartHandlers setObject:[aBlock copy] forKey:@"a"]; + + + void (^liBlock)(void) = ^ + { + _currentTag.paragraphStyle.paragraphSpacing = 0; + + DTCSSListStyle *listStyle = [_currentTag.paragraphStyle.textLists lastObject]; + + if (listStyle.type != DTCSSListStyleTypeNone) + { + // first tab is to right-align bullet, numbering against + CGFloat tabOffset = _currentTag.paragraphStyle.headIndent - 5.0f*_textScale; + [_currentTag.paragraphStyle addTabStopAtPosition:tabOffset alignment:kCTRightTextAlignment]; + } + + // second tab is for the beginning of first line after bullet + [_currentTag.paragraphStyle addTabStopAtPosition:_currentTag.paragraphStyle.headIndent alignment: kCTLeftTextAlignment]; + }; + + [_tagStartHandlers setObject:[liBlock copy] forKey:@"li"]; + + + void (^listBlock)(void) = ^ + { + _currentTag.paragraphStyle.firstLineHeadIndent = _currentTag.paragraphStyle.headIndent; + _currentTag.paragraphStyle.headIndent += _currentTag.paragraphStyle.listIndent; + + // create the appropriate list style from CSS + NSDictionary *styles = [_currentTag styles]; + DTCSSListStyle *newListStyle = [[DTCSSListStyle alloc] initWithStyles:styles]; + + // append this list style to the current paragraph style text lists + NSMutableArray *textLists = [_currentTag.paragraphStyle.textLists mutableCopy]; + if (!textLists) + { + textLists = [NSMutableArray array]; + } + + [textLists addObject:newListStyle]; + + // workaround for different styles on stacked lists + if ([textLists count]>1) // not necessary for first + { + // find out if each list is ordered or unordered + NSMutableArray *tmpArray = [NSMutableArray array]; + for (DTCSSListStyle *oneList in textLists) + { + if ([oneList isOrdered]) + { + [tmpArray addObject:@"ol"]; + } + else + { + [tmpArray addObject:@"ul"]; + } + } + + // build a CSS selector + NSString *selector = [tmpArray componentsJoinedByString:@" "]; + + // find style + NSDictionary *style = [[_globalStyleSheet styles] objectForKey:selector]; + + if (style) + { + [newListStyle updateFromStyleDictionary:style]; + } + } + + _currentTag.paragraphStyle.textLists = textLists; + }; + + [_tagStartHandlers setObject:[listBlock copy] forKey:@"ul"]; + [_tagStartHandlers setObject:[listBlock copy] forKey:@"ol"]; + + void (^h1Block)(void) = ^ + { + _currentTag.headerLevel = 1; + }; + [_tagStartHandlers setObject:[h1Block copy] forKey:@"h1"]; + + void (^h2Block)(void) = ^ + { + _currentTag.headerLevel = 2; + }; + [_tagStartHandlers setObject:[h2Block copy] forKey:@"h2"]; + + + void (^h3Block)(void) = ^ + { + _currentTag.headerLevel = 3; + }; + [_tagStartHandlers setObject:[h3Block copy] forKey:@"h3"]; + + + void (^h4Block)(void) = ^ + { + _currentTag.headerLevel = 4; + }; + [_tagStartHandlers setObject:[h4Block copy] forKey:@"h4"]; + + + void (^h5Block)(void) = ^ + { + _currentTag.headerLevel = 5; + }; + [_tagStartHandlers setObject:[h5Block copy] forKey:@"h5"]; + + + void (^h6Block)(void) = ^ + { + _currentTag.headerLevel = 6; + }; + [_tagStartHandlers setObject:[h6Block copy] forKey:@"h6"]; + + + void (^fontBlock)(void) = ^ + { + NSInteger size = [[_currentTag attributeForKey:@"size"] intValue]; + + switch (size) + { + case 1: + _currentTag.fontDescriptor.pointSize = _textScale * 9.0f; + break; + case 2: + _currentTag.fontDescriptor.pointSize = _textScale * 10.0f; + break; + case 4: + _currentTag.fontDescriptor.pointSize = _textScale * 14.0f; + break; + case 5: + _currentTag.fontDescriptor.pointSize = _textScale * 18.0f; + break; + case 6: + _currentTag.fontDescriptor.pointSize = _textScale * 24.0f; + break; + case 7: + _currentTag.fontDescriptor.pointSize = _textScale * 37.0f; + break; + case 3: + default: + _currentTag.fontDescriptor.pointSize = _defaultFontDescriptor.pointSize; + break; + } + + NSString *face = [_currentTag attributeForKey:@"face"]; + + if (face) + { + _currentTag.fontDescriptor.fontName = face; + + // face usually invalidates family + _currentTag.fontDescriptor.fontFamily = nil; + } + + NSString *color = [_currentTag attributeForKey:@"color"]; + + if (color) + { + _currentTag.textColor = [DTColor colorWithHTMLName:color]; + } + }; + + [_tagStartHandlers setObject:[fontBlock copy] forKey:@"font"]; + + + void (^pBlock)(void) = ^ + { + _currentTag.paragraphStyle.firstLineHeadIndent = _currentTag.paragraphStyle.headIndent + _defaultParagraphStyle.firstLineHeadIndent; + }; + + [_tagStartHandlers setObject:[pBlock copy] forKey:@"p"]; +} + +- (void)_registerTagEndHandlers +{ + if (_tagEndHandlers) + { + return; + } + + _tagEndHandlers = [[NSMutableDictionary alloc] init]; + + + void (^styleBlock)(void) = ^ + { + DTCSSStylesheet *localSheet = [(DTHTMLElementStylesheet *)_currentTag stylesheet]; + [_globalStyleSheet mergeStylesheet:localSheet]; + }; + + [_tagEndHandlers setObject:[styleBlock copy] forKey:@"style"]; +} + +#pragma mark DTHTMLParser Delegate + +- (void)parser:(DTHTMLParser *)parser didStartElement:(NSString *)elementName attributes:(NSDictionary *)attributeDict +{ + dispatch_group_async(_stringAssemblyGroup, _stringAssemblyQueue, ^{ + DTHTMLElement *newNode = [DTHTMLElement elementWithName:elementName attributes:attributeDict options:_options]; + + DTHTMLElement *previousLastChild = nil; + + if (_currentTag) + { + // inherit stuff + [newNode inheritAttributesFromElement:_currentTag]; + [newNode interpretAttributes]; + + previousLastChild = [_currentTag.childNodes lastObject]; + + // add as new child of current node + [_currentTag addChildNode:newNode]; + + // remember body node + if (!_bodyElement && [newNode.name isEqualToString:@"body"]) + { + _bodyElement = newNode; + } + } + else + { + // might be first node ever + if (!_rootNode) + { + _rootNode = newNode; + + [_rootNode inheritAttributesFromElement:_defaultTag]; + [_rootNode interpretAttributes]; + } + } + + // apply style from merged style sheet + NSDictionary *mergedStyles = [_globalStyleSheet mergedStyleDictionaryForElement:newNode]; + if (mergedStyles) + { + [newNode applyStyleDictionary:mergedStyles]; + } + + // adding a block element eliminates previous trailing white space text node + // because a new block starts on a new line + if (previousLastChild && newNode.displayStyle != DTHTMLElementDisplayStyleInline) + { + if ([previousLastChild isKindOfClass:[DTHTMLElementText class]]) + { + DTHTMLElementText *textElement = (DTHTMLElementText *)previousLastChild; + + if ([[textElement text] isIgnorableWhitespace]) + { + [_currentTag removeChildNode:textElement]; + } + } + } + + _currentTag = newNode; + + // find block to execute for this tag if any + void (^tagBlock)(void) = [_tagStartHandlers objectForKey:elementName]; + + if (tagBlock) + { + tagBlock(); + } + }); +} + +- (void)parser:(DTHTMLParser *)parser didEndElement:(NSString *)elementName +{ + dispatch_group_async(_stringAssemblyGroup, _stringAssemblyQueue, ^{ + // output the element if it is direct descendant of body tag, or close of body in case there are direct text nodes + + // find block to execute for this tag if any + void (^tagBlock)(void) = [_tagEndHandlers objectForKey:elementName]; + + if (tagBlock) + { + tagBlock(); + } + + if (_currentTag.displayStyle != DTHTMLElementDisplayStyleNone) + { + if (_currentTag == _bodyElement || _currentTag.parentElement == _bodyElement) + { + // has children that have not been output yet + if ([_currentTag needsOutput]) + { + // caller gets opportunity to modify tag before it is written + if (_willFlushCallback) + { + _willFlushCallback(_currentTag); + } + + NSAttributedString *nodeString = [_currentTag attributedString]; + + if (nodeString) + { + // if this is a block element then we need a paragraph break before it + if (_currentTag.displayStyle != DTHTMLElementDisplayStyleInline) + { + if ([_tmpString length] && ![[_tmpString string] hasSuffix:@"\n"]) + { + // trim off whitespace + while ([[_tmpString string] hasSuffixCharacterFromSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]) + { + [_tmpString deleteCharactersInRange:NSMakeRange([_tmpString length]-1, 1)]; + } + + [_tmpString appendString:@"\n"]; + } + } + + + [_tmpString appendAttributedString:nodeString]; + _currentTag.didOutput = YES; + + if (!_shouldKeepDocumentNodeTree) + { + // we don't need the children any more + [_currentTag removeAllChildNodes]; + } + } + } + } + } + + // go back up a level + _currentTag = [_currentTag parentElement]; + }); +} + +- (void)parser:(DTHTMLParser *)parser foundCharacters:(NSString *)string +{ + + dispatch_group_async(_stringAssemblyGroup, _stringAssemblyQueue, ^{ + NSAssert(_currentTag, @"Cannot add text node without a current node"); + + if ([string isIgnorableWhitespace]) + { + // ignore whitespace as first element of block element + if (_currentTag.displayStyle!=DTHTMLElementDisplayStyleInline && ![_currentTag.childNodes count]) + { + return; + } + + // ignore whitespace following a block element + DTHTMLElement *previousTag = [_currentTag.childNodes lastObject]; + + if (previousTag.displayStyle != DTHTMLElementDisplayStyleInline) + { + return; + } + + // ignore whitespace following a BR + if ([previousTag isKindOfClass:[DTHTMLElementBR class]]) + { + return; + } + } + + // adds a text node to the current node + DTHTMLElementText *textNode = [[DTHTMLElementText alloc] init]; + textNode.text = string; + + [textNode inheritAttributesFromElement:_currentTag]; + [textNode interpretAttributes]; + + // text directly contained in body needs to be output right away + if (_currentTag == _bodyElement) + { + [_tmpString appendAttributedString:[textNode attributedString]]; + _currentTag.didOutput = YES; + + // only add it to current tag if we need it + if (_shouldKeepDocumentNodeTree) + { + [_currentTag addChildNode:textNode]; + } + + return; + } + + // save it for later output + [_currentTag addChildNode:textNode]; + }); +} + +- (void)parser:(DTHTMLParser *)parser foundCDATA:(NSData *)CDATABlock +{ + dispatch_group_async(_stringAssemblyGroup, _stringAssemblyQueue, ^{ + NSAssert(_currentTag, @"Cannot add text node without a current node"); + + NSString *styleBlock = [[NSString alloc] initWithData:CDATABlock encoding:NSUTF8StringEncoding]; + + // adds a text node to the current node + DTHTMLParserTextNode *textNode = [[DTHTMLParserTextNode alloc] initWithCharacters:styleBlock]; + + [_currentTag addChildNode:textNode]; + }); +} + +- (void)parserDidEndDocument:(DTHTMLParser *)parser +{ + dispatch_group_async(_stringAssemblyGroup, _stringAssemblyQueue, ^{ + NSAssert(!_currentTag, @"Something went wrong, at end of document there is still an open node"); + + // trim off white space at end + while ([[_tmpString string] hasSuffixCharacterFromSet:[NSCharacterSet whitespaceCharacterSet]]) + { + [_tmpString deleteCharactersInRange:NSMakeRange([_tmpString length]-1, 1)]; + } + }); +} + +#pragma mark Properties + +@synthesize willFlushCallback = _willFlushCallback; +@synthesize shouldKeepDocumentNodeTree = _shouldKeepDocumentNodeTree; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTHTMLElement.h b/Pods/DTCoreText/Core/Source/DTHTMLElement.h new file mode 100644 index 00000000..00e25208 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTHTMLElement.h @@ -0,0 +1,155 @@ +// +// DTHTMLElement.h +// CoreTextExtensions +// +// Created by Oliver Drobnik on 4/14/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +@class DTCoreTextParagraphStyle; +@class DTCoreTextFontDescriptor; +@class DTTextAttachment; +@class DTCSSListStyle; +@class DTColor; + + +#import "DTCoreTextConstants.h" +#import "DTHTMLParserNode.h" +#import "DTTextAttachment.h" + +@class DTHTMLElementBR; + +/** + Class to represent a element (aka "tag") in a HTML document. Structure information - like parent or children - is inherited from its superclass . + */ +@interface DTHTMLElement : DTHTMLParserNode +{ + DTHTMLElement *_parent; + + DTCoreTextFontDescriptor *_fontDescriptor; + DTCoreTextParagraphStyle *_paragraphStyle; + DTTextAttachment *_textAttachment; + DTTextAttachmentVerticalAlignment _textAttachmentAlignment; + NSURL *_link; + NSString *_anchorName; + + DTColor *_textColor; + DTColor *_backgroundColor; + + CTUnderlineStyle _underlineStyle; + + NSString *_beforeContent; + + NSString *_linkGUID; + + BOOL _strikeOut; + NSInteger _superscriptStyle; + + NSInteger _headerLevel; + + NSArray *_shadows; + + NSMutableDictionary *_fontCache; + + NSMutableDictionary *_additionalAttributes; + + DTHTMLElementDisplayStyle _displayStyle; + DTHTMLElementFloatStyle _floatStyle; + + BOOL _isColorInherited; + + BOOL _preserveNewlines; + BOOL _containsAppleConvertedSpace; + + DTHTMLElementFontVariant _fontVariant; + + CGFloat _textScale; + CGSize _size; + + NSMutableArray *_children; + + NSDictionary *_styles; + + BOOL _didOutput; +} + +@property (nonatomic, copy) DTCoreTextFontDescriptor *fontDescriptor; +@property (nonatomic, copy) DTCoreTextParagraphStyle *paragraphStyle; +@property (nonatomic, strong) DTTextAttachment *textAttachment; +@property (nonatomic, copy) NSURL *link; +@property (nonatomic, copy) NSString *anchorName; +@property (nonatomic, strong) DTColor *textColor; +@property (nonatomic, strong) DTColor *backgroundColor; +@property (nonatomic, copy) NSString *beforeContent; +@property (nonatomic, copy) NSArray *shadows; +@property (nonatomic, assign) CTUnderlineStyle underlineStyle; +@property (nonatomic, assign) BOOL strikeOut; +@property (nonatomic, assign) NSInteger superscriptStyle; +@property (nonatomic, assign) NSInteger headerLevel; +@property (nonatomic, assign) DTHTMLElementDisplayStyle displayStyle; +@property (nonatomic, readonly) DTHTMLElementFloatStyle floatStyle; +@property (nonatomic, assign) BOOL isColorInherited; +@property (nonatomic, assign) BOOL preserveNewlines; +@property (nonatomic, assign) DTHTMLElementFontVariant fontVariant; +@property (nonatomic, assign) CGFloat textScale; +@property (nonatomic, assign) CGSize size; +@property (nonatomic, assign) BOOL containsAppleConvertedSpace; + +@property (nonatomic, assign) BOOL didOutput; + +/** + Ignores children for output that consist only of whitespace + */ +@property (nonatomic, assign) BOOL supressWhitespaceChildren; + + +/** + Designed initializer, creates the appropriate element sub type + @param attributes The attributes dictionary of the tag + @param options The parsing options dictionary + @returns the initialized element + */ ++ (DTHTMLElement *)elementWithName:(NSString *)name attributes:(NSDictionary *)attributes options:(NSDictionary *)options; +/** + Creates an `NSAttributedString` that represents the receiver including all its children + */ +- (NSAttributedString *)attributedString; + + +- (NSDictionary *)attributesDictionary; + +- (void)parseStyleString:(NSString *)styleString; +- (void)applyStyleDictionary:(NSDictionary *)styles; +- (NSDictionary *)styles; + +- (void)addAdditionalAttribute:(id)attribute forKey:(id)key; + +- (NSString *)attributeForKey:(NSString *)key; + +/** + Copies and inherits relevant attributes from the given parent element + */ +- (void)inheritAttributesFromElement:(DTHTMLElement *)element; + +/** + Interprets the tag attributes for e.g. writing direction. Usually you would call this after inheritAttributesFromElement:. + */ +- (void)interpretAttributes; + +/** + Returns the parent element. That's the same as the parent node but with adjusted type for convenience. + */ +- (DTHTMLElement *)parentElement; + + +- (BOOL)needsOutput; + +/** + Appends an attributed string representation of the receiver - including its children - to the given attributed string. + @param attributedString A mutable attributed string to append to + */ +//- (void)appendToAttributedString:(NSMutableAttributedString *)attributedString; + +- (BOOL)containedInBlock; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTHTMLElement.m b/Pods/DTCoreText/Core/Source/DTHTMLElement.m new file mode 100644 index 00000000..b3fc92cf --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTHTMLElement.m @@ -0,0 +1,1301 @@ +// +// DTHTMLElement.m +// CoreTextExtensions +// +// Created by Oliver Drobnik on 4/14/11. +// Copyright 2011 Drobnik.com. All rights reserved. +// + +#import "DTCoreText.h" +#import "DTHTMLElement.h" +#import "DTHTMLElementAttachment.h" +#import "DTHTMLElementBR.h" +#import "DTHTMLElementHR.h" +#import "DTHTMLElementLI.h" +#import "DTHTMLElementStylesheet.h" +#import "DTHTMLElementText.h" +#import "NSString+DTUtilities.h" + +@interface DTHTMLElement () + +@property (nonatomic, strong) NSMutableDictionary *fontCache; +@property (nonatomic, strong) NSString *linkGUID; + +- (DTCSSListStyle *)calculatedListStyle; + +// internal initializer +- (id)initWithName:(NSString *)name attributes:(NSDictionary *)attributes options:(NSDictionary *)options; + +@end + +BOOL ___shouldUseiOS6Attributes = NO; + +NSDictionary *_classesForNames = nil; + +@implementation DTHTMLElement + ++ (void)initialize +{ + // lookup table so that we quickly get the correct class to instantiate for special tags + NSMutableDictionary *tmpDict = [[NSMutableDictionary alloc] init]; + + [tmpDict setObject:[DTHTMLElementBR class] forKey:@"br"]; + [tmpDict setObject:[DTHTMLElementHR class] forKey:@"hr"]; + [tmpDict setObject:[DTHTMLElementLI class] forKey:@"li"]; + [tmpDict setObject:[DTHTMLElementStylesheet class] forKey:@"style"]; + [tmpDict setObject:[DTHTMLElementAttachment class] forKey:@"img"]; + [tmpDict setObject:[DTHTMLElementAttachment class] forKey:@"object"]; + [tmpDict setObject:[DTHTMLElementAttachment class] forKey:@"video"]; + [tmpDict setObject:[DTHTMLElementAttachment class] forKey:@"iframe"]; + + _classesForNames = [tmpDict copy]; +} + ++ (DTHTMLElement *)elementWithName:(NSString *)name attributes:(NSDictionary *)attributes options:(NSDictionary *)options +{ + // look for specialized class + Class class = [_classesForNames objectForKey:name]; + + // use generic of none found + if (!class) + { + class = [DTHTMLElement class]; + } + + DTHTMLElement *element = [[class alloc] initWithName:name attributes:attributes options:options]; + + return element; +} + +- (id)initWithName:(NSString *)name attributes:(NSDictionary *)attributes options:(NSDictionary *)options +{ + // node does not need the options, but it needs the name and attributes + self = [super initWithName:name attributes:attributes]; + if (self) + { + } + + return self; +} + +- (NSDictionary *)attributesDictionary +{ + NSMutableDictionary *tmpDict = [NSMutableDictionary dictionary]; + + BOOL shouldAddFont = YES; + + // copy additional attributes + if (_additionalAttributes) + { + [tmpDict setDictionary:_additionalAttributes]; + } + + // add text attachment + if (_textAttachment) + { +#if TARGET_OS_IPHONE + // need run delegate for sizing (only supported on iOS) + CTRunDelegateRef embeddedObjectRunDelegate = createEmbeddedObjectRunDelegate(_textAttachment); + [tmpDict setObject:CFBridgingRelease(embeddedObjectRunDelegate) forKey:(id)kCTRunDelegateAttributeName]; +#endif + + // add attachment + [tmpDict setObject:_textAttachment forKey:NSAttachmentAttributeName]; + + // remember original paragraphSpacing + [tmpDict setObject:[NSNumber numberWithFloat:self.paragraphStyle.paragraphSpacing] forKey:DTAttachmentParagraphSpacingAttribute]; + +#ifndef DT_ADD_FONT_ON_ATTACHMENTS + // omit adding a font unless we need it also on attachments, e.g. for editing + shouldAddFont = NO; +#endif + } + + // otherwise we have a font + if (shouldAddFont) + { + CTFontRef font = [_fontDescriptor newMatchingFont]; + + if (font) + { +#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_5_1 + if (___useiOS6Attributes) + { + UIFont *uiFont = [UIFont fontWithCTFont:font]; + [tmpDict setObject:uiFont forKey:NSFontAttributeName]; + } + else +#endif + { + // __bridge since its already retained elsewhere + [tmpDict setObject:(__bridge id)(font) forKey:(id)kCTFontAttributeName]; + } + + + // use this font to adjust the values needed for the run delegate during layout time + [_textAttachment adjustVerticalAlignmentForFont:font]; + + CFRelease(font); + } + } + + // add hyperlink + if (_link) + { + [tmpDict setObject:_link forKey:DTLinkAttribute]; + + // add a GUID to group multiple glyph runs belonging to same link + [tmpDict setObject:_linkGUID forKey:DTGUIDAttribute]; + } + + // add anchor + if (_anchorName) + { + [tmpDict setObject:_anchorName forKey:DTAnchorAttribute]; + } + + // add strikout if applicable + if (_strikeOut) + { +#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_5_1 + if (___useiOS6Attributes) + { + [tmpDict setObject:[NSNumber numberWithInteger:NSUnderlineStyleSingle] forKey:NSStrikethroughStyleAttributeName]; + } + else +#endif + { + [tmpDict setObject:[NSNumber numberWithBool:YES] forKey:DTStrikeOutAttribute]; + } + } + + // set underline style + if (_underlineStyle) + { +#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_5_1 + if (___useiOS6Attributes) + { + [tmpDict setObject:[NSNumber numberWithInteger:NSUnderlineStyleSingle] forKey:NSUnderlineStyleAttributeName]; + } + else +#endif + { + [tmpDict setObject:[NSNumber numberWithInteger:_underlineStyle] forKey:(id)kCTUnderlineStyleAttributeName]; + } + + // we could set an underline color as well if we wanted, but not supported by HTML + // [attributes setObject:(id)[DTImage redColor].CGColor forKey:(id)kCTUnderlineColorAttributeName]; + } + + if (_textColor) + { +#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_5_1 + if (___useiOS6Attributes) + { + [tmpDict setObject:_textColor forKey:NSForegroundColorAttributeName]; + } + else +#endif + { + [tmpDict setObject:(id)[_textColor CGColor] forKey:(id)kCTForegroundColorAttributeName]; + } + } + + if (_backgroundColor) + { +#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_5_1 + if (___useiOS6Attributes) + { + [tmpDict setObject:_backgroundColor forKey:NSBackgroundColorAttributeName]; + } + else +#endif + { + [tmpDict setObject:(id)[_backgroundColor CGColor] forKey:DTBackgroundColorAttribute]; + } + } + + if (_superscriptStyle) + { + [tmpDict setObject:(id)[NSNumber numberWithInteger:_superscriptStyle] forKey:(id)kCTSuperscriptAttributeName]; + } + + // add paragraph style + if (_paragraphStyle) + { +#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_5_1 + if (___useiOS6Attributes) + { + NSParagraphStyle *style = [self.paragraphStyle NSParagraphStyle]; + [tmpDict setObject:style forKey:NSParagraphStyleAttributeName]; + } + else +#endif + { + CTParagraphStyleRef newParagraphStyle = [self.paragraphStyle createCTParagraphStyle]; + [tmpDict setObject:CFBridgingRelease(newParagraphStyle) forKey:(id)kCTParagraphStyleAttributeName]; + } + } + + // add shadow array if applicable + if (_shadows) + { +#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_5_1 + if (___useiOS6Attributes) + { + // only a single shadow supported + NSDictionary *firstShadow = [_shadows objectAtIndex:0]; + + NSShadow *shadow = [[NSShadow alloc] init]; + shadow.shadowOffset = [[firstShadow objectForKey:@"Offset"] CGSizeValue]; + shadow.shadowColor = [firstShadow objectForKey:@"Color"]; + shadow.shadowBlurRadius = [[firstShadow objectForKey:@"Blur"] floatValue]; + [tmpDict setObject:shadow forKey:NSShadowAttributeName]; + } + else +#endif + { + [tmpDict setObject:_shadows forKey:DTShadowsAttribute]; + } + } + + // add tag for PRE so that we can omit changing this font if we override fonts + if (_preserveNewlines) + { + [tmpDict setObject:[NSNumber numberWithBool:YES] forKey:DTPreserveNewlinesAttribute]; + } + + if (_headerLevel) + { + [tmpDict setObject:[NSNumber numberWithInteger:_headerLevel] forKey:DTHeaderLevelAttribute]; + } + + if (_paragraphStyle.textLists) + { + [tmpDict setObject:_paragraphStyle.textLists forKey:DTTextListsAttribute]; + } + + if (_paragraphStyle.textBlocks) + { + [tmpDict setObject:_paragraphStyle.textBlocks forKey:DTTextBlocksAttribute]; + } + return tmpDict; +} + +/* +- (void)appendToAttributedString:(NSMutableAttributedString *)attributedString +{ + if (_displayStyle == DTHTMLElementDisplayStyleNone || _didOutput) + { + return; + } + + NSDictionary *attributes = [self attributesDictionary]; + + if (_textAttachment) + { + // ignore children, use unicode object placeholder + NSMutableAttributedString *tmpString = [[NSMutableAttributedString alloc] initWithString:UNICODE_OBJECT_PLACEHOLDER attributes:attributes]; + [attributedString appendAttributedString:tmpString]; + } + else + { + for (id oneChild in self.childNodes) + { + // the string for this single child + NSAttributedString *tmpString = nil; + + if ([oneChild isKindOfClass:[DTHTMLParserTextNode class]]) + { + [attributedString appendAttributedString:tmpString]; + } + else + { + NSAttributedString *tmpString = [oneChild attributedString]; + [attributedString appendAttributedString:tmpString]; +// +// if ([[oneChild name] isEqualToString:@"br"]) +// { +// [attributedString appendString:UNICODE_LINE_FEED]; +// } +// +// // should be a normal node +// [oneChild appendToAttributedString:attributedString]; + } + } + } + + if (_displayStyle != DTHTMLElementDisplayStyleInline) + { + if (![self.name isEqualToString:@"body"] && ![self.name isEqualToString:@"html"]) + { + [attributedString appendString:@"\n"]; + } + } + + _didOutput = YES; +} + */ + +- (BOOL)needsOutput +{ + if ([self.childNodes count]) + { + for (DTHTMLElement *oneChild in self.childNodes) + { + if (!oneChild.didOutput) + { + return YES; + } + } + + return NO; + } + + return YES; +} + +- (NSAttributedString *)attributedString +{ + if (_displayStyle == DTHTMLElementDisplayStyleNone || _didOutput) + { + return nil; + } + + NSDictionary *attributes = [self attributesDictionary]; + + NSMutableAttributedString *tmpString; + + if (_textAttachment) + { + // ignore text, use unicode object placeholder + tmpString = [[NSMutableAttributedString alloc] initWithString:UNICODE_OBJECT_PLACEHOLDER attributes:attributes]; + } + else + { + // walk through children + tmpString = [[NSMutableAttributedString alloc] init]; + + DTHTMLElement *previousChild = nil; + + for (DTHTMLElement *oneChild in self.childNodes) + { + // if previous node was inline and this child is block then we need a newline + if (previousChild && previousChild.displayStyle == DTHTMLElementDisplayStyleInline) + { + if (oneChild.displayStyle == DTHTMLElementDisplayStyleBlock) + { + // trim off whitespace suffix + while ([[tmpString string] hasSuffixCharacterFromSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]) + { + [tmpString deleteCharactersInRange:NSMakeRange([tmpString length]-1, 1)]; + } + + // paragraph break + [tmpString appendString:@"\n"]; + } + } + + NSAttributedString *nodeString = [oneChild attributedString]; + + if (nodeString) + { + if (!oneChild.containsAppleConvertedSpace) + { + // we already have a white space in the string so far + if ([[tmpString string] hasSuffixCharacterFromSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]) + { + while ([[nodeString string] hasPrefix:@" "]) + { + nodeString = [nodeString attributedSubstringFromRange:NSMakeRange(1, [nodeString length]-1)]; + } + } + } + + [tmpString appendAttributedString:nodeString]; + } + + previousChild = oneChild; + } + } + + // block-level elements get space trimmed and a newline + if (_displayStyle != DTHTMLElementDisplayStyleInline) + { + // trim off whitespace prefix + while ([[tmpString string] hasPrefix:@" "]) + { + [tmpString deleteCharactersInRange:NSMakeRange(0, 1)]; + } + + // trim off whitespace suffix + while ([[tmpString string] hasSuffix:@" "]) + { + [tmpString deleteCharactersInRange:NSMakeRange([tmpString length]-1, 1)]; + } + + if (![self.name isEqualToString:@"html"] && ![self.name isEqualToString:@"body"]) + { + if (![[tmpString string] hasSuffix:@"\n"]) + { + [tmpString appendString:@"\n"]; + } + } + } + + // make sure the last sub-paragraph of this has no less than the specified paragraph spacing of this element + // e.g. last LI needs to inherit the margin-after of the UL + if (self.displayStyle == DTHTMLElementDisplayStyleBlock) + { + NSRange paragraphRange = [[tmpString string] rangeOfParagraphAtIndex:[tmpString length]-1]; + + +#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_5_1 + if (___useiOS6Attributes) + { + NSParagraphStyle *paraStyle = [tmpString attribute:NSParagraphStyleAttributeName atIndex:paragraphRange.location effectiveRange:NULL]; + + DTCoreTextParagraphStyle *paragraphStyle = [DTCoreTextParagraphStyle paragraphStyleWithNSParagraphStyle:paraStyle]; + + if (paragraphStyle.paragraphSpacing < self.paragraphStyle.paragraphSpacing) + { + paragraphStyle.paragraphSpacing = self.paragraphStyle.paragraphSpacing; + + // make new paragraph style + NSParagraphStyle *newParaStyle = [paragraphStyle NSParagraphStyle]; + + // remove old (works around iOS 4.3 leak) + [tmpString removeAttribute:NSParagraphStyleAttributeName range:paragraphRange]; + + // set new + [tmpString addAttribute:NSParagraphStyleAttributeName value:newParaStyle range:paragraphRange]; + } + } + else +#endif + { + CTParagraphStyleRef paraStyle = (__bridge CTParagraphStyleRef)[tmpString attribute:(id)kCTParagraphStyleAttributeName atIndex:paragraphRange.location effectiveRange:NULL]; + + DTCoreTextParagraphStyle *paragraphStyle = [DTCoreTextParagraphStyle paragraphStyleWithCTParagraphStyle:paraStyle]; + + if (paragraphStyle.paragraphSpacing < self.paragraphStyle.paragraphSpacing) + { + paragraphStyle.paragraphSpacing = self.paragraphStyle.paragraphSpacing; + + // make new paragraph style + CTParagraphStyleRef newParaStyle = [paragraphStyle createCTParagraphStyle]; + + // remove old (works around iOS 4.3 leak) + [tmpString removeAttribute:(id)kCTParagraphStyleAttributeName range:paragraphRange]; + + // set new + [tmpString addAttribute:(id)kCTParagraphStyleAttributeName value:(__bridge_transfer id)newParaStyle range:paragraphRange]; + } + } + } + + return tmpString; +} + +- (DTHTMLElement *)parentElement +{ + return (DTHTMLElement *)self.parentNode; +} + +- (BOOL)containedInBlock +{ + id element = self; + + while (element && ![[element name] isEqualToString:@"body"]) + { + if ([element displayStyle] == DTHTMLElementDisplayStyleBlock) + { + return YES; + } + + element = [element parentNode]; + } + + return NO; +} + +- (void)applyStyleDictionary:(NSDictionary *)styles +{ + if (![styles count]) + { + return; + } + + // keep that for later lookup + _styles = styles; + + // register pseudo-selector contents + self.beforeContent = [[_styles objectForKey:@"before:content"] stringByDecodingCSSContentAttribute]; + + NSString *fontSize = [styles objectForKey:@"font-size"]; + if (fontSize) + { + // absolute sizes based on 12.0 CoreText default size, Safari has 16.0 + + if ([fontSize isEqualToString:@"smaller"]) + { + _fontDescriptor.pointSize /= 1.2f; + } + else if ([fontSize isEqualToString:@"larger"]) + { + _fontDescriptor.pointSize *= 1.2f; + } + else if ([fontSize isEqualToString:@"xx-small"]) + { + _fontDescriptor.pointSize = 9.0f/1.3333f * _textScale; + } + else if ([fontSize isEqualToString:@"x-small"]) + { + _fontDescriptor.pointSize = 10.0f/1.3333f * _textScale; + } + else if ([fontSize isEqualToString:@"small"]) + { + _fontDescriptor.pointSize = 13.0f/1.3333f * _textScale; + } + else if ([fontSize isEqualToString:@"medium"]) + { + _fontDescriptor.pointSize = 16.0f/1.3333f * _textScale; + } + else if ([fontSize isEqualToString:@"large"]) + { + _fontDescriptor.pointSize = 22.0f/1.3333f * _textScale; + } + else if ([fontSize isEqualToString:@"x-large"]) + { + _fontDescriptor.pointSize = 24.0f/1.3333f * _textScale; + } + else if ([fontSize isEqualToString:@"xx-large"]) + { + _fontDescriptor.pointSize = 37.0f/1.3333f * _textScale; + } + else if ([fontSize isEqualToString:@"inherit"]) + { + _fontDescriptor.pointSize = _parent.fontDescriptor.pointSize; + } + else + { + CGFloat fontSizeValue = [fontSize pixelSizeOfCSSMeasureRelativeToCurrentTextSize:_fontDescriptor.pointSize textScale:_textScale]; + _fontDescriptor.pointSize = fontSizeValue; + } + } + + NSString *color = [styles objectForKey:@"color"]; + if (color) + { + self.textColor = [DTColor colorWithHTMLName:color]; + } + + NSString *bgColor = [styles objectForKey:@"background-color"]; + if (bgColor) + { + self.backgroundColor = [DTColor colorWithHTMLName:bgColor]; + } + + NSString *floatString = [styles objectForKey:@"float"]; + + if (floatString) + { + if ([floatString isEqualToString:@"left"]) + { + _floatStyle = DTHTMLElementFloatStyleLeft; + } + else if ([floatString isEqualToString:@"right"]) + { + _floatStyle = DTHTMLElementFloatStyleRight; + } + else if ([floatString isEqualToString:@"none"]) + { + _floatStyle = DTHTMLElementFloatStyleNone; + } + } + + NSString *fontFamily = [[styles objectForKey:@"font-family"] stringByTrimmingCharactersInSet:[NSCharacterSet quoteCharacterSet]]; + + if (fontFamily) + { + NSString *lowercaseFontFamily = [fontFamily lowercaseString]; + + if ([lowercaseFontFamily rangeOfString:@"geneva"].length) + { + _fontDescriptor.fontFamily = @"Helvetica"; + } + else if ([lowercaseFontFamily rangeOfString:@"cursive"].length) + { + _fontDescriptor.stylisticClass = kCTFontScriptsClass; + _fontDescriptor.fontFamily = nil; + } + else if ([lowercaseFontFamily rangeOfString:@"sans-serif"].length) + { + // too many matches (24) + // fontDescriptor.stylisticClass = kCTFontSansSerifClass; + _fontDescriptor.fontFamily = @"Helvetica"; + } + else if ([lowercaseFontFamily rangeOfString:@"serif"].length) + { + // kCTFontTransitionalSerifsClass = Baskerville + // kCTFontClarendonSerifsClass = American Typewriter + // kCTFontSlabSerifsClass = Courier New + // + // strangely none of the classes yields Times + _fontDescriptor.fontFamily = @"Times New Roman"; + } + else if ([lowercaseFontFamily rangeOfString:@"fantasy"].length) + { + _fontDescriptor.fontFamily = @"Papyrus"; // only available on iPad + } + else if ([lowercaseFontFamily rangeOfString:@"monospace"].length) + { + _fontDescriptor.monospaceTrait = YES; + _fontDescriptor.fontFamily = @"Courier"; + } + else if ([lowercaseFontFamily rangeOfString:@"times"].length) + { + _fontDescriptor.fontFamily = @"Times New Roman"; + } + else + { + // probably custom font registered in info.plist + _fontDescriptor.fontFamily = fontFamily; + } + } + + NSString *fontStyle = [[styles objectForKey:@"font-style"] lowercaseString]; + if (fontStyle) + { + if ([fontStyle isEqualToString:@"normal"]) + { + _fontDescriptor.italicTrait = NO; + } + else if ([fontStyle isEqualToString:@"italic"] || [fontStyle isEqualToString:@"oblique"]) + { + _fontDescriptor.italicTrait = YES; + } + else if ([fontStyle isEqualToString:@"inherit"]) + { + // nothing to do + } + } + + NSString *fontWeight = [[styles objectForKey:@"font-weight"] lowercaseString]; + if (fontWeight) + { + if ([fontWeight isEqualToString:@"normal"]) + { + _fontDescriptor.boldTrait = NO; + } + else if ([fontWeight isEqualToString:@"bold"]) + { + _fontDescriptor.boldTrait = YES; + } + else if ([fontWeight isEqualToString:@"bolder"]) + { + _fontDescriptor.boldTrait = YES; + } + else if ([fontWeight isEqualToString:@"lighter"]) + { + _fontDescriptor.boldTrait = NO; + } + else + { + // can be 100 - 900 + + NSInteger value = [fontWeight intValue]; + + if (value<=600) + { + _fontDescriptor.boldTrait = NO; + } + else + { + _fontDescriptor.boldTrait = YES; + } + } + } + + + NSString *decoration = [[styles objectForKey:@"text-decoration"] lowercaseString]; + if (decoration) + { + if ([decoration isEqualToString:@"underline"]) + { + self.underlineStyle = kCTUnderlineStyleSingle; + } + else if ([decoration isEqualToString:@"line-through"]) + { + self.strikeOut = YES; + } + else if ([decoration isEqualToString:@"none"]) + { + // remove all + self.underlineStyle = kCTUnderlineStyleNone; + self.strikeOut = NO; + } + else if ([decoration isEqualToString:@"overline"]) + { + //TODO: add support for overline decoration + } + else if ([decoration isEqualToString:@"blink"]) + { + //TODO: add support for blink decoration + } + else if ([decoration isEqualToString:@"inherit"]) + { + // nothing to do + } + } + + NSString *alignment = [[styles objectForKey:@"text-align"] lowercaseString]; + if (alignment) + { + if ([alignment isEqualToString:@"left"]) + { + self.paragraphStyle.alignment = kCTLeftTextAlignment; + } + else if ([alignment isEqualToString:@"right"]) + { + self.paragraphStyle.alignment = kCTRightTextAlignment; + } + else if ([alignment isEqualToString:@"center"]) + { + self.paragraphStyle.alignment = kCTCenterTextAlignment; + } + else if ([alignment isEqualToString:@"justify"]) + { + self.paragraphStyle.alignment = kCTJustifiedTextAlignment; + } + else if ([alignment isEqualToString:@"inherit"]) + { + // nothing to do + } + } + + NSString *verticalAlignment = [[styles objectForKey:@"vertical-align"] lowercaseString]; + if (verticalAlignment) + { + if ([verticalAlignment isEqualToString:@"sub"]) + { + self.superscriptStyle = -1; + } + else if ([verticalAlignment isEqualToString:@"super"]) + { + self.superscriptStyle = +1; + } + else if ([verticalAlignment isEqualToString:@"baseline"]) + { + self.superscriptStyle = 0; + } + else if ([verticalAlignment isEqualToString:@"inherit"]) + { + // nothing to do + } + else if ([verticalAlignment isEqualToString:@"text-top"]) + { + _textAttachmentAlignment = DTTextAttachmentVerticalAlignmentTop; + } + else if ([verticalAlignment isEqualToString:@"middle"]) + { + _textAttachmentAlignment = DTTextAttachmentVerticalAlignmentCenter; + } + else if ([verticalAlignment isEqualToString:@"text-bottom"]) + { + _textAttachmentAlignment = DTTextAttachmentVerticalAlignmentBottom; + } + else if ([verticalAlignment isEqualToString:@"baseline"]) + { + _textAttachmentAlignment = DTTextAttachmentVerticalAlignmentBaseline; + } + } + + // if there is a text attachment we transfer the aligment we got + _textAttachment.verticalAlignment = _textAttachmentAlignment; + + NSString *shadow = [styles objectForKey:@"text-shadow"]; + if (shadow) + { + self.shadows = [shadow arrayOfCSSShadowsWithCurrentTextSize:_fontDescriptor.pointSize currentColor:_textColor]; + } + + NSString *lineHeight = [[styles objectForKey:@"line-height"] lowercaseString]; + if (lineHeight) + { + if ([lineHeight isEqualToString:@"normal"]) + { + self.paragraphStyle.lineHeightMultiple = 0.0; // default + self.paragraphStyle.minimumLineHeight = 0.0; // default + self.paragraphStyle.maximumLineHeight = 0.0; // default + } + else if ([lineHeight isEqualToString:@"inherit"]) + { + // no op, we already inherited it + } + else if ([lineHeight isNumeric]) + { + self.paragraphStyle.lineHeightMultiple = [lineHeight floatValue]; + } + else // interpret as length + { + CGFloat lineHeightValue = [lineHeight pixelSizeOfCSSMeasureRelativeToCurrentTextSize:_fontDescriptor.pointSize textScale:_textScale]; + self.paragraphStyle.minimumLineHeight = lineHeightValue; + self.paragraphStyle.maximumLineHeight = lineHeightValue; + } + } + + NSString *marginBottom = [styles objectForKey:@"margin-bottom"]; + if (marginBottom) + { + CGFloat marginBottomValue = [marginBottom pixelSizeOfCSSMeasureRelativeToCurrentTextSize:_fontDescriptor.pointSize textScale:_textScale]; + self.paragraphStyle.paragraphSpacing = marginBottomValue; + + } + else + { + NSString *webkitMarginAfter = [styles objectForKey:@"-webkit-margin-after"]; + if (webkitMarginAfter) + { + self.paragraphStyle.paragraphSpacing = [webkitMarginAfter pixelSizeOfCSSMeasureRelativeToCurrentTextSize:_fontDescriptor.pointSize textScale:_textScale]; + } + } + + NSString *marginLeft = [styles objectForKey:@"margin-left"]; + if (marginLeft) + { + self.paragraphStyle.headIndent = [marginLeft pixelSizeOfCSSMeasureRelativeToCurrentTextSize:_fontDescriptor.pointSize textScale:_textScale]; + self.paragraphStyle.firstLineHeadIndent = self.paragraphStyle.headIndent; + } + + NSString *marginRight = [styles objectForKey:@"margin-right"]; + if (marginRight) + { + self.paragraphStyle.tailIndent = -[marginRight pixelSizeOfCSSMeasureRelativeToCurrentTextSize:_fontDescriptor.pointSize textScale:_textScale]; + } + + NSString *fontVariantStr = [[styles objectForKey:@"font-variant"] lowercaseString]; + if (fontVariantStr) + { + if ([fontVariantStr isEqualToString:@"small-caps"]) + { + _fontVariant = DTHTMLElementFontVariantSmallCaps; + } + else if ([fontVariantStr isEqualToString:@"inherit"]) + { + _fontVariant = DTHTMLElementFontVariantInherit; + } + else + { + _fontVariant = DTHTMLElementFontVariantNormal; + } + } + + NSString *widthString = [styles objectForKey:@"width"]; + if (widthString && ![widthString isEqualToString:@"auto"]) + { + _size.width = [widthString pixelSizeOfCSSMeasureRelativeToCurrentTextSize:self.fontDescriptor.pointSize textScale:_textScale]; + } + + NSString *heightString = [styles objectForKey:@"height"]; + if (heightString && ![heightString isEqualToString:@"auto"]) + { + _size.height = [heightString pixelSizeOfCSSMeasureRelativeToCurrentTextSize:self.fontDescriptor.pointSize textScale:_textScale]; + } + + // if this has an attachment set its size too + _textAttachment.displaySize = _size; + + NSString *whitespaceString = [styles objectForKey:@"white-space"]; + if ([whitespaceString hasPrefix:@"pre"]) + { + _preserveNewlines = YES; + } + else + { + _preserveNewlines = NO; + } + + NSString *displayString = [styles objectForKey:@"display"]; + if (displayString) + { + if ([displayString isEqualToString:@"none"]) + { + _displayStyle = DTHTMLElementDisplayStyleNone; + } + else if ([displayString isEqualToString:@"block"]) + { + _displayStyle = DTHTMLElementDisplayStyleBlock; + } + else if ([displayString isEqualToString:@"inline"]) + { + _displayStyle = DTHTMLElementDisplayStyleInline; + } + else if ([displayString isEqualToString:@"list-item"]) + { + _displayStyle = DTHTMLElementDisplayStyleListItem; + } + else if ([displayString isEqualToString:@"table"]) + { + _displayStyle = DTHTMLElementDisplayStyleTable; + } + else if ([verticalAlignment isEqualToString:@"inherit"]) + { + // nothing to do + } + } + + DTEdgeInsets padding = {0,0,0,0}; + + // webkit default value + NSString *webkitPaddingStart = [styles objectForKey:@"-webkit-padding-start"]; + + if (webkitPaddingStart) + { + self.paragraphStyle.listIndent = [webkitPaddingStart pixelSizeOfCSSMeasureRelativeToCurrentTextSize:self.fontDescriptor.pointSize textScale:_textScale]; + } + + BOOL needsTextBlock = (_backgroundColor!=nil); + + NSString *paddingString = [styles objectForKey:@"padding"]; + + if (paddingString) + { + // maybe it's using the short style + NSArray *parts = [paddingString componentsSeparatedByString:@" "]; + + if ([parts count] == 4) + { + padding.top = [[parts objectAtIndex:0] pixelSizeOfCSSMeasureRelativeToCurrentTextSize:self.fontDescriptor.pointSize textScale:_textScale]; + padding.right = [[parts objectAtIndex:1] pixelSizeOfCSSMeasureRelativeToCurrentTextSize:self.fontDescriptor.pointSize textScale:_textScale]; + padding.bottom = [[parts objectAtIndex:2] pixelSizeOfCSSMeasureRelativeToCurrentTextSize:self.fontDescriptor.pointSize textScale:_textScale]; + padding.left = [[parts objectAtIndex:3] pixelSizeOfCSSMeasureRelativeToCurrentTextSize:self.fontDescriptor.pointSize textScale:_textScale]; + } + else if ([parts count] == 3) + { + padding.top = [[parts objectAtIndex:0] pixelSizeOfCSSMeasureRelativeToCurrentTextSize:self.fontDescriptor.pointSize textScale:_textScale]; + padding.right = [[parts objectAtIndex:1] pixelSizeOfCSSMeasureRelativeToCurrentTextSize:self.fontDescriptor.pointSize textScale:_textScale]; + padding.bottom = [[parts objectAtIndex:2] pixelSizeOfCSSMeasureRelativeToCurrentTextSize:self.fontDescriptor.pointSize textScale:_textScale]; + padding.left = padding.right; + } + else if ([parts count] == 2) + { + padding.top = [[parts objectAtIndex:0] pixelSizeOfCSSMeasureRelativeToCurrentTextSize:self.fontDescriptor.pointSize textScale:_textScale]; + padding.right = [[parts objectAtIndex:1] pixelSizeOfCSSMeasureRelativeToCurrentTextSize:self.fontDescriptor.pointSize textScale:_textScale]; + padding.bottom = padding.top; + padding.left = padding.right; + } + else + { + CGFloat paddingAmount = [paddingString pixelSizeOfCSSMeasureRelativeToCurrentTextSize:self.fontDescriptor.pointSize textScale:_textScale]; + padding = DTEdgeInsetsMake(paddingAmount, paddingAmount, paddingAmount, paddingAmount); + } + + // left padding overrides webkit list indent + self.paragraphStyle.listIndent = padding.left; + + needsTextBlock = YES; + } + else + { + paddingString = [styles objectForKey:@"padding-left"]; + + if (paddingString) + { + padding.left = [paddingString pixelSizeOfCSSMeasureRelativeToCurrentTextSize:self.fontDescriptor.pointSize textScale:_textScale]; + needsTextBlock = YES; + + // left padding overrides webkit list indent + self.paragraphStyle.listIndent = padding.left; + } + + paddingString = [styles objectForKey:@"padding-top"]; + + if (paddingString) + { + padding.top = [paddingString pixelSizeOfCSSMeasureRelativeToCurrentTextSize:self.fontDescriptor.pointSize textScale:_textScale]; + needsTextBlock = YES; + } + + paddingString = [styles objectForKey:@"padding-right"]; + + if (paddingString) + { + padding.right = [paddingString pixelSizeOfCSSMeasureRelativeToCurrentTextSize:self.fontDescriptor.pointSize textScale:_textScale]; + needsTextBlock = YES; + } + + paddingString = [styles objectForKey:@"padding-bottom"]; + + if (paddingString) + { + padding.bottom = [paddingString pixelSizeOfCSSMeasureRelativeToCurrentTextSize:self.fontDescriptor.pointSize textScale:_textScale]; + needsTextBlock = YES; + } + } + + if (_displayStyle == DTHTMLElementDisplayStyleBlock) + { + if (needsTextBlock) + { + // need a block + DTTextBlock *newBlock = [[DTTextBlock alloc] init]; + + newBlock.padding = padding; + + // transfer background color to block + newBlock.backgroundColor = _backgroundColor; + _backgroundColor = nil; + + NSArray *newBlocks = [self.paragraphStyle.textBlocks mutableCopy]; + + if (!newBlocks) + { + // need an array, this is the first block + newBlocks = [NSArray arrayWithObject:newBlock]; + } + + self.paragraphStyle.textBlocks = newBlocks; + } + } +} + +- (NSDictionary *)styles +{ + return _styles; +} + +- (void)parseStyleString:(NSString *)styleString +{ + NSDictionary *styles = [styleString dictionaryOfCSSStyles]; + [self applyStyleDictionary:styles]; +} + +- (void)addAdditionalAttribute:(id)attribute forKey:(id)key +{ + if (!_additionalAttributes) + { + _additionalAttributes = [[NSMutableDictionary alloc] init]; + } + + [_additionalAttributes setObject:attribute forKey:key]; +} + +- (NSString *)attributeForKey:(NSString *)key +{ + return [_attributes objectForKey:key]; +} + +#pragma mark Calulcating Properties + +- (id)valueForKeyPathWithInheritance:(NSString *)keyPath +{ + + + id value = [self valueForKeyPath:keyPath]; + + // if property is not set we also go to parent + if (!value && _parent) + { + return [_parent valueForKeyPathWithInheritance:keyPath]; + } + + // enum properties have 0 for inherit + if ([value isKindOfClass:[NSNumber class]]) + { + NSNumber *number = value; + + if (([number integerValue]==0) && _parent) + { + return [_parent valueForKeyPathWithInheritance:keyPath]; + } + } + + // string properties have 'inherit' for inheriting + if ([value isKindOfClass:[NSString class]]) + { + NSString *string = value; + + if ([string isEqualToString:@"inherit"] && _parent) + { + return [_parent valueForKeyPathWithInheritance:keyPath]; + } + } + + // obviously not inherited + return value; +} + + +- (DTCSSListStyle *)calculatedListStyle +{ + DTCSSListStyle *style = [[DTCSSListStyle alloc] init]; + + id calcType = [self valueForKeyPathWithInheritance:@"listStyle.type"]; + id calcPos = [self valueForKeyPathWithInheritance:@"listStyle.position"]; + id calcImage = [self valueForKeyPathWithInheritance:@"listStyle.imageName"]; + + style.type = (DTCSSListStyleType)[calcType integerValue]; + style.position = (DTCSSListStylePosition)[calcPos integerValue]; + style.imageName = calcImage; + + return style; +} + +#pragma mark - Inheriting Attributes + +- (void)inheritAttributesFromElement:(DTHTMLElement *)element +{ + _fontDescriptor = [element.fontDescriptor copy]; + _paragraphStyle = [element.paragraphStyle copy]; + + _fontVariant = element.fontVariant; + _underlineStyle = element.underlineStyle; + _strikeOut = element.strikeOut; + _superscriptStyle = element.superscriptStyle; + + _shadows = [element.shadows copy]; + + _link = [element.link copy]; + _anchorName = [element.anchorName copy]; + _linkGUID = element.linkGUID; + + _textColor = element.textColor; + _isColorInherited = YES; + + _preserveNewlines = element.preserveNewlines; + _textScale = element.textScale; + + // only inherit background-color from inline elements + if (element.displayStyle == DTHTMLElementDisplayStyleInline) + { + self.backgroundColor = element.backgroundColor; + } + + _containsAppleConvertedSpace = element.containsAppleConvertedSpace; +} + +- (void)interpretAttributes +{ + if (!_attributes) + { + // nothing to interpret + return; + } + + // transfer Apple Converted Space tag + if ([[self attributeForKey:@"class"] isEqualToString:@"Apple-converted-space"]) + { + _containsAppleConvertedSpace = YES; + } + + // detect writing direction if set + NSString *directionStr = [self attributeForKey:@"dir"]; + + if (directionStr) + { + NSAssert(_paragraphStyle, @"Found dir attribute, but missing paragraph style on element"); + + if ([directionStr isEqualToString:@"rtl"]) + { + _paragraphStyle.baseWritingDirection = NSWritingDirectionRightToLeft; + } + else if ([directionStr isEqualToString:@"ltr"]) + { + _paragraphStyle.baseWritingDirection = NSWritingDirectionLeftToRight; + } + else if ([directionStr isEqualToString:@"auto"]) + { + _paragraphStyle.baseWritingDirection = NSWritingDirectionNatural; // that's also default + } + else + { + // other values are invalid and will be ignored + } + } +} + +#pragma mark Properties + +- (void)setTextColor:(DTColor *)textColor +{ + if (_textColor != textColor) + { + + _textColor = textColor; + _isColorInherited = NO; + } +} + +- (DTHTMLElementFontVariant)fontVariant +{ + if (_fontVariant == DTHTMLElementFontVariantInherit) + { + if (_parent) + { + return _parent.fontVariant; + } + + return DTHTMLElementFontVariantNormal; + } + + return _fontVariant; +} + +- (void)setAttributes:(NSDictionary *)attributes +{ + [super setAttributes:[attributes copy]]; + + // decode size contained in attributes, might be overridden later by CSS size + _size = CGSizeMake([[self attributeForKey:@"width"] floatValue], [[self attributeForKey:@"height"] floatValue]); +} + +- (void)setTextAttachment:(DTTextAttachment *)textAttachment +{ + textAttachment.verticalAlignment = _textAttachmentAlignment; + _textAttachment = textAttachment; + + // transfer link GUID + _textAttachment.hyperLinkGUID = _linkGUID; + + // transfer size + _textAttachment.displaySize = _size; +} + +- (void)setLink:(NSURL *)link +{ + _linkGUID = [NSString stringWithUUID]; + _link = [link copy]; + + if (_textAttachment) + { + _textAttachment.hyperLinkGUID = _linkGUID; + } +} + +@synthesize fontDescriptor = _fontDescriptor; +@synthesize paragraphStyle = _paragraphStyle; +@synthesize textColor = _textColor; +@synthesize backgroundColor = _backgroundColor; +@synthesize beforeContent = _beforeContent; +@synthesize link = _link; +@synthesize anchorName = _anchorName; +@synthesize underlineStyle = _underlineStyle; +@synthesize textAttachment = _textAttachment; +@synthesize strikeOut = _strikeOut; +@synthesize superscriptStyle = _superscriptStyle; +@synthesize headerLevel = _headerLevel; +@synthesize shadows = _shadows; +@synthesize floatStyle = _floatStyle; +@synthesize isColorInherited = _isColorInherited; +@synthesize preserveNewlines = _preserveNewlines; +@synthesize displayStyle = _displayStyle; +@synthesize fontVariant = _fontVariant; +@synthesize textScale = _textScale; +@synthesize size = _size; +@synthesize linkGUID = _linkGUID; +@synthesize containsAppleConvertedSpace = _containsAppleConvertedSpace; + +@end + + diff --git a/Pods/DTCoreText/Core/Source/DTHTMLElementAttachment.h b/Pods/DTCoreText/Core/Source/DTHTMLElementAttachment.h new file mode 100644 index 00000000..3ec3f54e --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTHTMLElementAttachment.h @@ -0,0 +1,13 @@ +// +// DTHTMLElementAttachment.h +// DTCoreText +// +// Created by Oliver Drobnik on 26.12.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import "DTHTMLElement.h" + +@interface DTHTMLElementAttachment : DTHTMLElement + +@end diff --git a/Pods/DTCoreText/Core/Source/DTHTMLElementAttachment.m b/Pods/DTCoreText/Core/Source/DTHTMLElementAttachment.m new file mode 100644 index 00000000..0f4eb8b3 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTHTMLElementAttachment.m @@ -0,0 +1,68 @@ +// +// DTHTMLElementAttachment.m +// DTCoreText +// +// Created by Oliver Drobnik on 26.12.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import "DTHTMLElementAttachment.h" + +#import "DTHTMLElement.h" +#import "DTTextAttachment.h" +#import "DTCoreTextParagraphStyle.h" +#import "NSMutableAttributedString+HTML.h" + +@implementation DTHTMLElementAttachment + +- (id)initWithName:(NSString *)name attributes:(NSDictionary *)attributes options:(NSDictionary *)options +{ + self = [super initWithName:name attributes:attributes]; + + if (self) + { + // make appropriate attachment + DTTextAttachment *attachment = [DTTextAttachment textAttachmentWithElement:self options:options]; + + // add it to tag + _textAttachment = attachment; + + // to avoid much too much space before the image + _paragraphStyle.lineHeightMultiple = 1; + + // specifiying line height interfers with correct positioning + _paragraphStyle.minimumLineHeight = 0; + _paragraphStyle.maximumLineHeight = 0; + } + + return self; +} + +- (NSAttributedString *)attributedString +{ + NSDictionary *attributes = [self attributesDictionary]; + + // ignore text, use unicode object placeholder + NSMutableAttributedString *tmpString = [[NSMutableAttributedString alloc] initWithString:UNICODE_OBJECT_PLACEHOLDER attributes:attributes]; + + // block-level elements get space trimmed and a newline + if (self.displayStyle != DTHTMLElementDisplayStyleInline) + { + [tmpString appendString:@"\n"]; + } + + return tmpString; +} + +// workaround, because we don't support float yet. float causes the image to be its own block +- (DTHTMLElementDisplayStyle)displayStyle +{ + if ([super floatStyle]==DTHTMLElementFloatStyleNone) + { + return [super displayStyle]; + } + + return DTHTMLElementDisplayStyleBlock; +} + +@end diff --git a/Pods/DTCoreText/Core/Source/DTHTMLElementBR.h b/Pods/DTCoreText/Core/Source/DTHTMLElementBR.h new file mode 100644 index 00000000..9f157a02 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTHTMLElementBR.h @@ -0,0 +1,13 @@ +// +// DTHTMLElementBR.h +// DTCoreText +// +// Created by Oliver Drobnik on 26.12.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import "DTHTMLElement.h" + +@interface DTHTMLElementBR : DTHTMLElement + +@end diff --git a/Pods/DTCoreText/Core/Source/DTHTMLElementBR.m b/Pods/DTCoreText/Core/Source/DTHTMLElementBR.m new file mode 100644 index 00000000..fa627904 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTHTMLElementBR.m @@ -0,0 +1,19 @@ +// +// DTHTMLElementBR.m +// DTCoreText +// +// Created by Oliver Drobnik on 26.12.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import "DTHTMLElementBR.h" + +@implementation DTHTMLElementBR + +- (NSAttributedString *)attributedString +{ + NSDictionary *attributes = [self attributesDictionary]; + return [[NSAttributedString alloc] initWithString:UNICODE_LINE_FEED attributes:attributes]; +} + +@end diff --git a/Pods/DTCoreText/Core/Source/DTHTMLElementHR.h b/Pods/DTCoreText/Core/Source/DTHTMLElementHR.h new file mode 100644 index 00000000..658f7f25 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTHTMLElementHR.h @@ -0,0 +1,13 @@ +// +// DTHTMLElementHR.h +// DTCoreText +// +// Created by Oliver Drobnik on 26.12.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import + +@interface DTHTMLElementHR : DTHTMLElement + +@end diff --git a/Pods/DTCoreText/Core/Source/DTHTMLElementHR.m b/Pods/DTCoreText/Core/Source/DTHTMLElementHR.m new file mode 100644 index 00000000..751f03b6 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTHTMLElementHR.m @@ -0,0 +1,27 @@ +// +// DTHTMLElementHR.m +// DTCoreText +// +// Created by Oliver Drobnik on 26.12.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import "DTHTMLElementHR.h" + +@implementation DTHTMLElementHR + +- (NSDictionary *)attributesDictionary +{ + NSMutableDictionary *dict = [[super attributesDictionary] mutableCopy]; + [dict setObject:[NSNumber numberWithBool:YES] forKey:DTHorizontalRuleStyleAttribute]; + + return dict; +} + +- (NSAttributedString *)attributedString +{ + NSDictionary *attributes = [self attributesDictionary]; + return [[NSAttributedString alloc] initWithString:@"\n" attributes:attributes]; +} + +@end diff --git a/Pods/DTCoreText/Core/Source/DTHTMLElementLI.h b/Pods/DTCoreText/Core/Source/DTHTMLElementLI.h new file mode 100644 index 00000000..1c03e629 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTHTMLElementLI.h @@ -0,0 +1,13 @@ +// +// DTHTMLElementLI.h +// DTCoreText +// +// Created by Oliver Drobnik on 27.12.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import + +@interface DTHTMLElementLI : DTHTMLElement + +@end diff --git a/Pods/DTCoreText/Core/Source/DTHTMLElementLI.m b/Pods/DTCoreText/Core/Source/DTHTMLElementLI.m new file mode 100644 index 00000000..a043c716 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTHTMLElementLI.m @@ -0,0 +1,71 @@ +// +// DTHTMLElementLI.m +// DTCoreText +// +// Created by Oliver Drobnik on 27.12.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import "DTHTMLElementLI.h" + +@implementation DTHTMLElementLI + + +- (NSUInteger)_indexOfListItemInListRoot:(DTHTMLElement *)listRoot +{ + NSInteger index = -1; + + for (DTHTMLElement *oneElement in listRoot.childNodes) + { + if ([oneElement isKindOfClass:[DTHTMLElementLI class]]) + { + index++; + } + + if (oneElement == self) + { + break; + } + } + + return index; +} + +- (NSAttributedString *)attributedString +{ + NSMutableAttributedString *tmpString = [[NSMutableAttributedString alloc] init]; + + DTCSSListStyle *effectiveList = [self.paragraphStyle.textLists lastObject]; + + DTHTMLElement *listRoot = self.parentElement; + + NSUInteger counter = [self _indexOfListItemInListRoot:listRoot]+effectiveList.startingItemNumber; + + // need to get prefix text color from list parent + NSAttributedString *prefixString = [NSAttributedString prefixForListItemWithCounter:counter listStyle:effectiveList listIndent:self.paragraphStyle.listIndent attributes:[listRoot attributesDictionary]]; + + if (prefixString) + { + [tmpString appendAttributedString:prefixString]; + } + + if ([self.childNodes count]) + { + DTHTMLElement *firstchild = [self.childNodes objectAtIndex:0]; + if (firstchild.displayStyle != DTHTMLElementDisplayStyleInline) + { + [tmpString appendString:@"\n"]; + } + } + + NSAttributedString *childrenString = [super attributedString]; + + if (childrenString) + { + [tmpString appendAttributedString:childrenString]; + } + + return tmpString; +} + +@end diff --git a/Pods/DTCoreText/Core/Source/DTHTMLElementStylesheet.h b/Pods/DTCoreText/Core/Source/DTHTMLElementStylesheet.h new file mode 100644 index 00000000..305c5039 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTHTMLElementStylesheet.h @@ -0,0 +1,21 @@ +// +// DTHTMLElementStylesheet.h +// DTCoreText +// +// Created by Oliver Drobnik on 29.12.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import + +/** + This class represents STYLE tags. + */ +@interface DTHTMLElementStylesheet : DTHTMLElement + +/** + Parses the text children and assembles the resulting stylesheet. + */ +- (DTCSSStylesheet *)stylesheet; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTHTMLElementStylesheet.m b/Pods/DTCoreText/Core/Source/DTHTMLElementStylesheet.m new file mode 100644 index 00000000..e1aca0c1 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTHTMLElementStylesheet.m @@ -0,0 +1,26 @@ +// +// DTHTMLElementStylesheet.m +// DTCoreText +// +// Created by Oliver Drobnik on 29.12.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import "DTHTMLElementStylesheet.h" +#import "DTCSSStylesheet.h" + +@implementation DTHTMLElementStylesheet + +- (NSAttributedString *)attributedString +{ + return nil; +} + +- (DTCSSStylesheet *)stylesheet +{ + NSString *text = [self text]; + + return [[DTCSSStylesheet alloc] initWithStyleBlock:text]; +} + +@end diff --git a/Pods/DTCoreText/Core/Source/DTHTMLElementText.h b/Pods/DTCoreText/Core/Source/DTHTMLElementText.h new file mode 100644 index 00000000..8797be67 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTHTMLElementText.h @@ -0,0 +1,15 @@ +// +// DTHTMLElementText.h +// DTCoreText +// +// Created by Oliver Drobnik on 26.12.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import "DTHTMLElement.h" + +@interface DTHTMLElementText : DTHTMLElement + +@property (nonatomic, strong) NSString *text; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTHTMLElementText.m b/Pods/DTCoreText/Core/Source/DTHTMLElementText.m new file mode 100644 index 00000000..914be943 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTHTMLElementText.m @@ -0,0 +1,112 @@ +// +// DTHTMLElementText.m +// DTCoreText +// +// Created by Oliver Drobnik on 26.12.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import "DTHTMLElementText.h" +#import "NSString+HTML.h" +#import "DTCoreTextFontDescriptor.h" +#import "NSAttributedString+SmallCaps.h" + +#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_5_1 +#import "UIFont+DTCoreText.h" +#endif + +@implementation DTHTMLElementText +{ + NSString *_text; +} + +- (void)_appendHTMLToString:(NSMutableString *)string indentLevel:(NSUInteger)indentLevel +{ + // indent to the level + for (int i=0; i __IPHONE_5_1 + if (___useiOS6Attributes) + { + UIFont *font = [UIFont fontWithCTFont:smallerFont]; + + [smallAttributes setObject:font forKey:NSFontAttributeName]; + CFRelease(smallerFont); + } + else +#endif + { + [smallAttributes setObject:CFBridgingRelease(smallerFont) forKey:(id)kCTFontAttributeName]; + } + + return [[NSAttributedString alloc] initWithString:_text attributes:smallAttributes]; + } + else + { + return [NSAttributedString synthesizedSmallCapsAttributedStringWithText:_text attributes:attributes]; + } + } +} + +#pragma mark - Properties + +@synthesize text = _text; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTHTMLParserNode.h b/Pods/DTCoreText/Core/Source/DTHTMLParserNode.h new file mode 100644 index 00000000..e23400a6 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTHTMLParserNode.h @@ -0,0 +1,70 @@ +// +// DTHTMLParserNode.h +// DTCoreText +// +// Created by Oliver Drobnik on 26.12.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import + +@class DTHTMLParserTextNode; + +@interface DTHTMLParserNode : NSObject +{ + NSDictionary *_attributes; +} + +/** + Designated initializer + */ +- (id)initWithName:(NSString *)name attributes:(NSDictionary *)attributes; + +/** + The name of the receiver + */ +@property (nonatomic, copy) NSString *name; + +/** + The attributes of the receiver. + */ +@property (nonatomic, copy) NSDictionary *attributes; + +/** + A weak link to the parent node of the receiver + */ +@property (nonatomic, assign) DTHTMLParserNode *parentNode; + +/** + The child nodes of the receiver + */ +@property (nonatomic, readonly) NSArray *childNodes; + +/** + Adds a child node to the receiver. + @param childNode The child node to be appended to the list of children + */ +- (void)addChildNode:(DTHTMLParserNode *)childNode; + +/** + Removes a child node from the receiver + @param childNode The child node to remove + */ +- (void)removeChildNode:(DTHTMLParserNode *)childNode; + +/** + Removes all child nodes from the receiver +*/ +- (void)removeAllChildNodes; + +/** + Hierarchy representation of the receiver including all attributes and children + */ +- (NSString *)debugDescription; + +/** + Concatenated contents of all text nodes + */ +- (NSString *)text; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTHTMLParserNode.m b/Pods/DTCoreText/Core/Source/DTHTMLParserNode.m new file mode 100644 index 00000000..6c350542 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTHTMLParserNode.m @@ -0,0 +1,136 @@ +// +// DTHTMLParserNode.m +// DTCoreText +// +// Created by Oliver Drobnik on 26.12.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import "DTHTMLParserNode.h" +#import "DTHTMLParserTextNode.h" + +@implementation DTHTMLParserNode +{ + NSString *_name; + __unsafe_unretained DTHTMLParserNode *_parentNode; + NSMutableArray *_childNodes; +} + + +- (id)initWithName:(NSString *)name attributes:(NSDictionary *)attributes +{ + self = [super init]; + + if (self) + { + _name = [name copy]; + [self setAttributes:attributes]; // property to allow overriding + } + + return self; +} + +- (void)addChildNode:(DTHTMLParserNode *)childNode +{ + // first child creates array + if (!_childNodes) + { + _childNodes = [[NSMutableArray alloc] init]; + } + + childNode.parentNode = self; + [_childNodes addObject:childNode]; +} + +- (void)removeChildNode:(DTHTMLParserNode *)childNode +{ + [_childNodes removeObject:childNode]; +} + +- (void)removeAllChildNodes +{ + [_childNodes removeAllObjects]; +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@ name='%@'>", NSStringFromClass([self class]), _name]; +} + + +- (void)_appendHTMLToString:(NSMutableString *)string indentLevel:(NSUInteger)indentLevel +{ + // indent to the level + for (int i=0; i\n"]; + return; + } + + [string appendFormat:@">\n"]; + + // output attributes + for (DTHTMLParserNode *childNode in _childNodes) + { + [childNode _appendHTMLToString:string indentLevel:indentLevel+1]; + } + + // indent to the level + for (int i=0; i\n", _name]; +} + +- (NSString *)debugDescription +{ + NSMutableString *tmpString = [NSMutableString string]; + + [self _appendHTMLToString:tmpString indentLevel:0]; + + return tmpString; +} + +- (NSString *)text +{ + NSMutableString *text = [NSMutableString string]; + + for (DTHTMLParserTextNode *oneChild in self.childNodes) + { + if ([oneChild isKindOfClass:[DTHTMLParserTextNode class]]) + { + [text appendString:[oneChild characters]]; + } + } + + return text; +} + +#pragma mark - Properties + +@synthesize name = _name; +@synthesize attributes = _attributes; +@synthesize parentNode = _parentNode; +@synthesize childNodes = _childNodes; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTHTMLParserTextNode.h b/Pods/DTCoreText/Core/Source/DTHTMLParserTextNode.h new file mode 100644 index 00000000..4f24ed2b --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTHTMLParserTextNode.h @@ -0,0 +1,20 @@ +// +// DTHTMLParserTextNode.h +// DTCoreText +// +// Created by Oliver Drobnik on 26.12.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import "DTHTMLParserNode.h" + +@interface DTHTMLParserTextNode : DTHTMLParserNode + +- (id)initWithCharacters:(NSString *)characters; + +/** + Returns the receivers character contents + */ +@property (nonatomic, readonly) NSString *characters; + +@end diff --git a/Pods/DTCoreText/Core/Source/DTHTMLParserTextNode.m b/Pods/DTCoreText/Core/Source/DTHTMLParserTextNode.m new file mode 100644 index 00000000..a39c9d51 --- /dev/null +++ b/Pods/DTCoreText/Core/Source/DTHTMLParserTextNode.m @@ -0,0 +1,51 @@ +// +// DTHTMLParserTextNode.m +// DTCoreText +// +// Created by Oliver Drobnik on 26.12.12. +// Copyright (c) 2012 Drobnik.com. All rights reserved. +// + +#import "DTHTMLParserTextNode.h" +#import "NSString+HTML.h" + +@implementation DTHTMLParserTextNode +{ + NSString *_characters; +} + +- (id)initWithCharacters:(NSString *)characters +{ + self = [super init]; + + if (self) + { + self.name = @"#TEXT#"; + + _characters = characters; + + } + return self; +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@ content='%@'>", NSStringFromClass([self class]), _characters]; +} + +- (void)_appendHTMLToString:(NSMutableString *)string indentLevel:(NSUInteger)indentLevel +{ + // indent to the level + for (int i=0; i __IPHONE_5_1 + // if running on iOS6 or higher + if ([DTVersion osVersionIsLessThen:@"6.0"]) + { + _iOS6TagsPossible = NO; + } + else + { + _iOS6TagsPossible = YES; + } +#endif + } + + return self; +} + +#pragma mark - Generating HTML + +// checks the style against previous styles and returns the style class for this +- (NSString *)_styleClassForElement:(NSString *)elementName style:(NSString *)style +{ + // get array of styles for element + NSMutableArray *_styleArray = [_styleLookup objectForKey:elementName]; + + if (!_styleArray) + { + // first time we see this element + _styleArray = [[NSMutableArray alloc] init]; + [_styleLookup setObject:_styleArray forKey:elementName]; + } + + NSInteger index = [_styleArray indexOfObject:style]; + + if (index==NSNotFound) + { + // need to add this style + [_styleArray addObject:style]; + index = [_styleArray count]; + } + else + { + index++; + } + + return [NSString stringWithFormat:@"%@%d", [elementName substringToIndex:1],(int)index]; +} + +- (NSString *)_tagRepresentationForListStyle:(DTCSSListStyle *)listStyle closingTag:(BOOL)closingTag +{ + BOOL isOrdered = NO; + + NSString *typeString = nil; + + switch (listStyle.type) + { + case DTCSSListStyleTypeInherit: + case DTCSSListStyleTypeDisc: + { + typeString = @"disc"; + isOrdered = NO; + break; + } + + case DTCSSListStyleTypeCircle: + { + typeString = @"circle"; + isOrdered = NO; + break; + } + + case DTCSSListStyleTypePlus: + { + typeString = @"plus"; + isOrdered = NO; + break; + } + + case DTCSSListStyleTypeUnderscore: + { + typeString = @"underscore"; + isOrdered = NO; + break; + } + + case DTCSSListStyleTypeImage: + { + typeString = @"image"; + isOrdered = NO; + break; + } + + case DTCSSListStyleTypeDecimal: + { + typeString = @"decimal"; + isOrdered = YES; + break; + } + + case DTCSSListStyleTypeDecimalLeadingZero: + { + typeString = @"decimal-leading-zero"; + isOrdered = YES; + break; + } + + case DTCSSListStyleTypeUpperAlpha: + { + typeString = @"upper-alpha"; + isOrdered = YES; + break; + } + + case DTCSSListStyleTypeUpperLatin: + { + typeString = @"upper-latin"; + isOrdered = YES; + break; + } + + case DTCSSListStyleTypeLowerAlpha: + { + typeString = @"lower-alpha"; + isOrdered = YES; + break; + } + + case DTCSSListStyleTypeLowerLatin: + { + typeString = @"lower-latin"; + isOrdered = YES; + break; + } + + default: + break; + } + + if (closingTag) + { + if (isOrdered) + { + return @""; + } + else + { + return @""; + } + } + else + { + if (listStyle.position == DTCSSListStylePositionInside) + { + typeString = [typeString stringByAppendingString:@" inside"]; + } + else if (listStyle.position == DTCSSListStylePositionOutside) + { + typeString = [typeString stringByAppendingString:@" outside"]; + } + + NSString *blockElement; + if (isOrdered) + { + blockElement = @"ol"; + } + else + { + blockElement = @"ul"; + } + + NSString *listStyleString = [NSString stringWithFormat:@"list-style='%@';\">", typeString]; + NSString *className = [self _styleClassForElement:blockElement style:listStyleString]; + + return [NSString stringWithFormat:@"<%@ class=\"%@\">", blockElement, className]; + } +} + + +- (void)_buildOutput +{ + // reusable styles + _styleLookup = [[NSMutableDictionary alloc] init]; + + NSString *plainString = [_attributedString string]; + + // divide the string into it's blocks (we assume that these are the P) + NSArray *paragraphs = [plainString componentsSeparatedByString:@"\n"]; + + NSMutableString *retString = [NSMutableString string]; + + NSInteger location = 0; + + NSArray *previousListStyles = nil; + + for (NSUInteger i=0; i<[paragraphs count]; i++) + { + NSString *oneParagraph = [paragraphs objectAtIndex:i]; + NSRange paragraphRange = NSMakeRange(location, [oneParagraph length]); + + // skip empty paragraph at the end + if (i==[paragraphs count]-1) + { + if (!paragraphRange.length) + { + continue; + } + } + + BOOL needsToRemovePrefix = NO; + + BOOL fontIsBlockLevel = NO; + + // check if font is same in the entire paragraph + NSRange fontEffectiveRange; + CTFontRef paragraphFont = (__bridge CTFontRef)[_attributedString attribute:(id)kCTFontAttributeName atIndex:paragraphRange.location longestEffectiveRange:&fontEffectiveRange inRange:paragraphRange]; + + if (NSEqualRanges(paragraphRange, fontEffectiveRange)) + { + fontIsBlockLevel = YES; + } + + // next paragraph start + location = location + paragraphRange.length + 1; + + NSDictionary *paraAttributes = [_attributedString attributesAtIndex:paragraphRange.location effectiveRange:NULL]; + + // lets see if we have a list style + NSArray *currentListStyles = [paraAttributes objectForKey:DTTextListsAttribute]; + + DTCSSListStyle *effectiveListStyle = [currentListStyles lastObject]; + + CTParagraphStyleRef paraStyle = (__bridge CTParagraphStyleRef)[paraAttributes objectForKey:(id)kCTParagraphStyleAttributeName]; + NSString *paraStyleString = nil; + + if (paraStyle) + { + DTCoreTextParagraphStyle *para = [DTCoreTextParagraphStyle paragraphStyleWithCTParagraphStyle:paraStyle]; + + if (_textScale!=1.0f) + { + para.minimumLineHeight = roundf(para.minimumLineHeight / _textScale); + para.maximumLineHeight = roundf(para.maximumLineHeight / _textScale); + + para.paragraphSpacing = roundf(para.paragraphSpacing/ _textScale); + para.paragraphSpacingBefore = roundf(para.paragraphSpacingBefore / _textScale); + + para.firstLineHeadIndent = roundf(para.firstLineHeadIndent / _textScale); + para.headIndent = roundf(para.headIndent / _textScale); + para.tailIndent = roundf(para.tailIndent / _textScale); + } + + paraStyleString = [para cssStyleRepresentation]; + } + + if (!paraStyleString) + { + paraStyleString = @""; + } + + if (fontIsBlockLevel) + { + if (paragraphFont) + { + DTCoreTextFontDescriptor *desc = [DTCoreTextFontDescriptor fontDescriptorForCTFont:paragraphFont]; + + if (_textScale!=1.0f) + { + desc.pointSize /= _textScale; + } + + NSString *paraFontStyle = [desc cssStyleRepresentation]; + + if (paraFontStyle) + { + paraStyleString = [paraStyleString stringByAppendingString:paraFontStyle]; + } + } + } + + NSString *blockElement; + + // close until we are at current or nil + if ([previousListStyles count]>[currentListStyles count]) + { + NSMutableArray *closingStyles = [previousListStyles mutableCopy]; + + do + { + DTCSSListStyle *closingStyle = [closingStyles lastObject]; + + if (closingStyle == effectiveListStyle) + { + break; + } + + // end of a list block + [retString appendString:[self _tagRepresentationForListStyle:closingStyle closingTag:YES]]; + [retString appendString:@"\n"]; + + [closingStyles removeLastObject]; + + previousListStyles = closingStyles; + } + while ([closingStyles count]); + } + + if (effectiveListStyle) + { + // next text needs to have list prefix removed + needsToRemovePrefix = YES; + + if (![previousListStyles containsObject:effectiveListStyle]) + { + // beginning of a list block + [retString appendString:[self _tagRepresentationForListStyle:effectiveListStyle closingTag:NO]]; + [retString appendString:@"\n"]; + } + + blockElement = @"li"; + } + else + { + blockElement = @"p"; + } + + NSNumber *headerLevel = [paraAttributes objectForKey:DTHeaderLevelAttribute]; + + if (headerLevel) + { + blockElement = [NSString stringWithFormat:@"h%d", (int)[headerLevel integerValue]]; + } + + if ([paragraphs lastObject] == oneParagraph) + { + // last paragraph in string + + if (![plainString hasSuffix:@"\n"]) + { + // not a whole paragraph, so we don't put it in P + blockElement = @"span"; + } + } + + if ([paraStyleString length]) + { + NSString *className = [self _styleClassForElement:blockElement style:paraStyleString]; + [retString appendFormat:@"<%@ class=\"%@\">", blockElement, className]; + //[retString appendFormat:@"<%@ style=\"%@\">", blockElement, paraStyleString]; + } + else + { + [retString appendFormat:@"<%@>", blockElement]; + } + + // add the attributed string ranges in this paragraph to the paragraph container + NSRange effectiveRange; + NSUInteger index = paragraphRange.location; + + NSUInteger paragraphRangeEnd = NSMaxRange(paragraphRange); + + while (index < paragraphRangeEnd) + { + NSDictionary *attributes = [_attributedString attributesAtIndex:index longestEffectiveRange:&effectiveRange inRange:paragraphRange]; + + NSString *plainSubString =[plainString substringWithRange:effectiveRange]; + + if (effectiveListStyle && needsToRemovePrefix) + { + NSInteger counter = [_attributedString itemNumberInTextList:effectiveListStyle atIndex:index]; + NSString *prefix = [effectiveListStyle prefixWithCounter:counter]; + + if ([plainSubString hasPrefix:prefix]) + { + plainSubString = [plainSubString substringFromIndex:[prefix length]]; + } + + needsToRemovePrefix = NO; + } + + index += effectiveRange.length; + + NSString *subString = [plainSubString stringByAddingHTMLEntities]; + + if (!subString) + { + continue; + } + + DTTextAttachment *attachment = [attributes objectForKey:NSAttachmentAttributeName]; + + + if (attachment) + { + NSString *urlString; + + if (attachment.contentURL) + { + + if ([attachment.contentURL isFileURL]) + { + NSString *path = [attachment.contentURL path]; + + NSRange range = [path rangeOfString:@".app/"]; + + if (range.length) + { + urlString = [path substringFromIndex:NSMaxRange(range)]; + } + else + { + urlString = [attachment.contentURL absoluteString]; + } + } + else + { + urlString = [attachment.contentURL relativeString]; + } + } + else + { + if (attachment.contentType == DTTextAttachmentTypeImage && attachment.contents) + { + urlString = [attachment dataURLRepresentation]; + } + else + { + // no valid image remote or local + continue; + } + } + + // write appropriate tag + if (attachment.contentType == DTTextAttachmentTypeVideoURL) + { + [retString appendFormat:@"