Skip to content
This repository has been archived by the owner on Apr 18, 2023. It is now read-only.

feature: make RichTextView usable from Objective-C code #198

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Source/RichTextView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public class RichTextView: UIView {

// MARK: - Init

public init(input: String = "",
@objc public init(input: String = "",
latexParser: LatexParserProtocol = LatexParser(),
font: UIFont = UIFont.systemFont(ofSize: UIFont.systemFontSize),
textColor: UIColor = UIColor.black,
Expand All @@ -41,7 +41,7 @@ public class RichTextView: UIView {
textViewDelegate: RichTextViewDelegate? = nil,
customAdditionalAttributes: [String: [NSAttributedString.Key: Any]]? = nil,
frame: CGRect,
completion: (([ParsingError]?) -> Void)? = nil) {
completion: (([Error]?) -> Void)? = nil) {
self.input = input
self.isSelectable = isSelectable
self.isEditable = isEditable
Expand Down
134 changes: 66 additions & 68 deletions Source/Text Parsing/LatexParserProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,82 +9,80 @@
import Down
import iosMath

public protocol LatexParserProtocol: class {
func extractLatex(from input: String, textColor: UIColor, baselineOffset: CGFloat, fontSize: CGFloat, height: CGFloat?) -> NSAttributedString?
@objc public protocol LatexParserProtocol: AnyObject {
@objc func extractLatex(from input: String, textColor: UIColor, baselineOffset: CGFloat, fontSize: CGFloat, height: CGFloat) -> NSAttributedString?
}

extension LatexParserProtocol {
public func extractLatex(from input: String, textColor: UIColor, baselineOffset: CGFloat, fontSize: CGFloat, height: CGFloat?) -> NSAttributedString? {
let latexInput = self.extractLatexStringInsideTags(from: input)
var mathImage: UIImage?
@objc public class LatexParser: NSObject, LatexParserProtocol {
public override init() {}

public func extractLatex(from input: String, textColor: UIColor, baselineOffset: CGFloat, fontSize: CGFloat, height: CGFloat) -> NSAttributedString? {
let latexInput = self.extractLatexStringInsideTags(from: input)
var mathImage: UIImage?

if Thread.isMainThread {
mathImage = self.setupMathLabelAndGetImage(from: latexInput, textColor: textColor, fontSize: height ?? fontSize)
} else {
DispatchQueue.main.sync {
mathImage = self.setupMathLabelAndGetImage(from: latexInput, textColor: textColor, fontSize: height ?? fontSize)
}
}
if Thread.isMainThread {
mathImage = self.setupMathLabelAndGetImage(from: latexInput, textColor: textColor, fontSize: height ?? fontSize)
} else {
DispatchQueue.main.sync {
mathImage = self.setupMathLabelAndGetImage(from: latexInput, textColor: textColor, fontSize: height ?? fontSize)
}
}

guard let image = mathImage else {
return nil
}
let imageOffset = self.getLatexOffset(fromInput: input, image: image, fontSize: fontSize)
let textAttachment = NSTextAttachment()
textAttachment.image = image
textAttachment.bounds = CGRect(
x: 0,
y: baselineOffset - imageOffset,
width: textAttachment.image?.size.width ?? 0,
height: textAttachment.image?.size.height ?? 0
)
let latexString = NSMutableAttributedString(attachment: textAttachment)
latexString.addAttribute(.baselineOffset, value: baselineOffset - imageOffset, range: NSRange(location: 0, length: latexString.length))
return latexString
}
guard let image = mathImage else {
return nil
}
let imageOffset = self.getLatexOffset(fromInput: input, image: image, fontSize: fontSize)
let textAttachment = NSTextAttachment()
textAttachment.image = image
textAttachment.bounds = CGRect(
x: 0,
y: baselineOffset - imageOffset,
width: textAttachment.image?.size.width ?? 0,
height: textAttachment.image?.size.height ?? 0
)
let latexString = NSMutableAttributedString(attachment: textAttachment)
latexString.addAttribute(.baselineOffset, value: baselineOffset - imageOffset, range: NSRange(location: 0, length: latexString.length))
return latexString
}

// MARK: - Helpers
// MARK: - Helpers

public func extractLatexStringInsideTags(from input: String) -> String {
let mathTagName = RichTextParser.ParserConstants.mathTagName
return input.getSubstring(inBetween: "[\(mathTagName)]", and: "[/\(mathTagName)]") ?? input
}
public func extractLatexStringInsideTags(from input: String) -> String {
let mathTagName = RichTextParser.ParserConstants.mathTagName
return input.getSubstring(inBetween: "[\(mathTagName)]", and: "[/\(mathTagName)]") ?? input
}

private func setupMathLabelAndGetImage(from input: String, textColor: UIColor, fontSize: CGFloat) -> UIImage? {
let label = MTMathUILabel()
label.textColor = textColor
label.latex = input
label.fontSize = fontSize
private func setupMathLabelAndGetImage(from input: String, textColor: UIColor, fontSize: CGFloat) -> UIImage? {
let label = MTMathUILabel()
label.textColor = textColor
label.latex = input
label.fontSize = fontSize

var newFrame = label.frame
newFrame.size = label.sizeThatFits(CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude))
label.frame = newFrame
return self.getImage(from: label)
}
var newFrame = label.frame
newFrame.size = label.sizeThatFits(CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude))
label.frame = newFrame
return self.getImage(from: label)
}

private func getImage(from label: MTMathUILabel) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(label.bounds.size, false, 0.0)
guard let context = UIGraphicsGetCurrentContext() else {
return nil
}
let verticalFlip = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: label.frame.size.height)
context.concatenate(verticalFlip)
label.layer.render(in: context)
guard let image = UIGraphicsGetImageFromCurrentImageContext(), let cgImage = image.cgImage else {
return nil
}
UIGraphicsEndImageContext()
return UIImage(cgImage: cgImage, scale: UIScreen.main.scale, orientation: image.imageOrientation)
}
private func getImage(from label: MTMathUILabel) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(label.bounds.size, false, 0.0)
guard let context = UIGraphicsGetCurrentContext() else {
return nil
}
let verticalFlip = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: label.frame.size.height)
context.concatenate(verticalFlip)
label.layer.render(in: context)
guard let image = UIGraphicsGetImageFromCurrentImageContext(), let cgImage = image.cgImage else {
return nil
}
UIGraphicsEndImageContext()
return UIImage(cgImage: cgImage, scale: UIScreen.main.scale, orientation: image.imageOrientation)
}

private func getLatexOffset(fromInput input: String, image: UIImage, fontSize: CGFloat) -> CGFloat {
let defaultSubScriptOffset = RichTextParser.ParserConstants.defaultSubScriptOffset
let imageOffset = max((image.size.height - fontSize)/2, 0)
let subscriptOffset: CGFloat = input.contains(RichTextParser.ParserConstants.latexSubscriptCharacter) ? defaultSubScriptOffset : 0
return max(subscriptOffset, imageOffset)
}
}

public class LatexParser: LatexParserProtocol {
public init() {}
private func getLatexOffset(fromInput input: String, image: UIImage, fontSize: CGFloat) -> CGFloat {
let defaultSubScriptOffset = RichTextParser.ParserConstants.defaultSubScriptOffset
let imageOffset = max((image.size.height - fontSize)/2, 0)
let subscriptOffset: CGFloat = input.contains(RichTextParser.ParserConstants.latexSubscriptCharacter) ? defaultSubScriptOffset : 0
return max(subscriptOffset, imageOffset)
}
}