Skip to content

Commit

Permalink
#391 add direction for text attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
Daniil Manin committed Jun 13, 2019
1 parent 60b145c commit 6314726
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 26 deletions.
4 changes: 4 additions & 0 deletions Macaw.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,7 @@
A74C8326229EB77B0085A832 /* masking-path-13-f-manual.reference in Resources */ = {isa = PBXBuildFile; fileRef = A74C8325229EB77B0085A832 /* masking-path-13-f-manual.reference */; };
A74C832C229FB7690085A832 /* color-prop-04-t-manual-osx.svg in Resources */ = {isa = PBXBuildFile; fileRef = A74C832B229FB7690085A832 /* color-prop-04-t-manual-osx.svg */; };
A74C832E229FBA4C0085A832 /* color-prop-04-t-manual-osx.reference in Resources */ = {isa = PBXBuildFile; fileRef = A74C832D229FBA4C0085A832 /* color-prop-04-t-manual-osx.reference */; };
A7632F6022B2500500803489 /* Direction.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7632F5F22B2500500803489 /* Direction.swift */; };
A7E675561EC4213500BD9ECB /* NodeBoundsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7E675551EC4213500BD9ECB /* NodeBoundsTests.swift */; };
C410148E1F834D290022EE44 /* style.svg in Resources */ = {isa = PBXBuildFile; fileRef = C410148D1F834D280022EE44 /* style.svg */; };
C4153A8F1F8793DE001BA5EE /* small-logo.png in Resources */ = {isa = PBXBuildFile; fileRef = C4153A8E1F8793DD001BA5EE /* small-logo.png */; };
Expand Down Expand Up @@ -1248,6 +1249,7 @@
A74C8325229EB77B0085A832 /* masking-path-13-f-manual.reference */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "masking-path-13-f-manual.reference"; sourceTree = "<group>"; };
A74C832B229FB7690085A832 /* color-prop-04-t-manual-osx.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "color-prop-04-t-manual-osx.svg"; sourceTree = "<group>"; };
A74C832D229FBA4C0085A832 /* color-prop-04-t-manual-osx.reference */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "color-prop-04-t-manual-osx.reference"; sourceTree = "<group>"; };
A7632F5F22B2500500803489 /* Direction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Direction.swift; sourceTree = "<group>"; };
A7E675551EC4213500BD9ECB /* NodeBoundsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NodeBoundsTests.swift; path = Bounds/NodeBoundsTests.swift; sourceTree = "<group>"; };
C410148D1F834D280022EE44 /* style.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = style.svg; sourceTree = "<group>"; };
C4153A8E1F8793DD001BA5EE /* small-logo.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "small-logo.png"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1642,6 +1644,7 @@
5B6E191F20AC58F900454E7E /* Color.swift */,
5874CCB620DA8A860090DBD5 /* ColorMatrix.swift */,
5BFEF5CD20B80A83008DAC11 /* ColorMatrixEffect.swift */,
A7632F5F22B2500500803489 /* Direction.swift */,
5B6E191B20AC58F800454E7E /* Drawable.swift */,
5B6E191C20AC58F800454E7E /* Effect.swift */,
5B6E191D20AC58F900454E7E /* Fill.swift */,
Expand Down Expand Up @@ -2870,6 +2873,7 @@
57E5E16C1E3B393900D1CB28 /* CombineAnimation.swift in Sources */,
57E5E1661E3B393900D1CB28 /* TransformHashable.swift in Sources */,
57E5E1921E3B393900D1CB28 /* MoveTo.swift in Sources */,
A7632F6022B2500500803489 /* Direction.swift in Sources */,
5B7E79C020CA7E9300C50BCF /* Pattern.swift in Sources */,
57E5E1A51E3B393900D1CB28 /* NodeRenderer.swift in Sources */,
5B6E193920AC58F900454E7E /* Drawable.swift in Sources */,
Expand Down
13 changes: 12 additions & 1 deletion Source/model/draw/Align.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,30 @@ open class Align {
open func align(size: Double) -> Double {
return align(outer: size, inner: 0)
}


open func reverse() -> Align {
return .max
}
}

private class MidAlign: Align {

override func align(outer: Double, inner: Double) -> Double {
return (outer - inner) / 2
}

override func reverse() -> Align {
return .mid
}
}

private class MaxAlign: Align {

override func align(outer: Double, inner: Double) -> Double {
return outer - inner
}

override func reverse() -> Align {
return .min
}
}
40 changes: 40 additions & 0 deletions Source/model/draw/Direction.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import UIKit

public enum Direction {
case lre
case rle
case lro
case rlo
}

extension Direction {

var attributedStringValue: NSArray {
switch self {
case .lre:
return [NSNumber(integerLiteral: 0)]
case .rle:
return [NSNumber(integerLiteral: 1)]
case .lro:
return [NSNumber(integerLiteral: 2)]
case .rlo:
return [NSNumber(integerLiteral: 3)]
}
}

static func from(direction: String?, unicodebidi: String?) -> Direction {
let direction = direction ?? "ltr"
let unicodebidi = unicodebidi ?? "normal"

switch (direction, unicodebidi) {
case ("ltr", "bidi-override"):
return .lro
case ("rtl", "bidi-override"):
return .rlo
case ("rtl", "normal"):
return .rle
default:
return .lre
}
}
}
18 changes: 15 additions & 3 deletions Source/model/scene/Text.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,28 @@ open class Text: Node {
get { return kerningVar.value }
set(val) { kerningVar.value = val }
}

public let directionVar: Variable<Direction>
open var direction: Direction {
get { return directionVar.value }
set(val) { directionVar.value = val }
}

public init(text: String, font: Font? = nil, fill: Fill? = Color.black, stroke: Stroke? = nil, align: Align = .min, baseline: Baseline = .top, kerning: Float = 0.0, place: Transform = Transform.identity, opaque: Bool = true, opacity: Double = 1, clip: Locus? = nil, mask: Node? = nil, effect: Effect? = nil, visible: Bool = true, tag: [String] = []) {
public init(text: String, font: Font? = nil, fill: Fill? = Color.black, stroke: Stroke? = nil, align: Align = .min, baseline: Baseline = .top, kerning: Float = 0.0, direction: Direction = .lre, place: Transform = Transform.identity, opaque: Bool = true, opacity: Double = 1, clip: Locus? = nil, mask: Node? = nil, effect: Effect? = nil, visible: Bool = true, tag: [String] = []) {
self.textVar = Variable<String>(text)
self.fontVar = Variable<Font?>(font)
self.fillVar = Variable<Fill?>(fill)
self.strokeVar = Variable<Stroke?>(stroke)
self.alignVar = Variable<Align>(align)
self.baselineVar = Variable<Baseline>(baseline)
self.kerningVar = Variable<Float>(kerning)
self.directionVar = Variable<Direction>(direction)

if direction == .rle || direction == .rlo {
self.alignVar = Variable<Align>(align.reverse())
} else {
self.alignVar = Variable<Align>(align)
}

super.init(
place: place,
opaque: opaque,
Expand All @@ -71,7 +84,6 @@ open class Text: Node {
override open var bounds: Rect {
let font: MFont
if let f = self.font {

if let customFont = RenderUtils.loadFont(name: f.name, size: f.size, weight: f.weight) {
font = customFont
} else {
Expand Down
10 changes: 6 additions & 4 deletions Source/render/TextRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,9 @@ class TextRenderer: NodeRenderer {
if text.kerning != 0.0 {
attributes[NSAttributedString.Key.kern] = NSNumber(value: text.kerning)
}


attributes[NSAttributedString.Key.writingDirection] = text.direction.attributedStringValue

if attributes.count > 1 {
MGraphicsPushContext(context)
message.draw(in: getBounds(font), withAttributes: attributes)
Expand Down Expand Up @@ -141,6 +143,7 @@ class TextRenderer: NodeRenderer {
if let stroke = text.stroke {
textAttributes[NSAttributedString.Key.strokeWidth] = NSNumber(value: stroke.width)
}

let textSize = NSString(string: text.text).size(withAttributes: textAttributes)
return CGRect(x: calculateAlignmentOffset(text, font: font),
y: calculateBaselineOffset(text, font: font),
Expand All @@ -164,21 +167,20 @@ class TextRenderer: NodeRenderer {

fileprivate func calculateAlignmentOffset(_ text: Text, font: MFont) -> CGFloat {
let textAttributes = [
NSAttributedString.Key.font: font
NSAttributedString.Key.font: font,
NSAttributedString.Key.writingDirection: text.direction.attributedStringValue
]
let textSize = NSString(string: text.text).size(withAttributes: textAttributes)
return -CGFloat(text.align.align(size: textSize.width.doubleValue))
}

fileprivate func getTextColor(_ fill: Fill) -> MColor {
if let color = fill as? Color {

#if os(iOS)
return MColor(cgColor: color.toCG())
#elseif os(OSX)
return MColor(cgColor: color.toCG()) ?? .black
#endif

}
return MColor.black
}
Expand Down
37 changes: 19 additions & 18 deletions Source/svg/SVGParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -930,21 +930,21 @@ open class SVGParser {
}

fileprivate func anchorToAlign(_ textAnchor: String?) -> Align {
if let anchor = textAnchor {
if anchor == "middle" {
return .mid
} else if anchor == "end" {
return .max
}
switch textAnchor {
case "middle":
return .mid
case "end":
return .max
default:
return .min
}
return Align.min
}

fileprivate func parseSimpleText(_ text: SWXMLHash.XMLElement, textAnchor: String?, fill: Fill?, stroke: Stroke?, opacity: Double, fontName: String?, fontSize: Int?, fontWeight: String?, pos: Transform = Transform()) -> Text? {
let string = text.text
let position = pos.move(dx: getDoubleValue(text, attribute: "x") ?? 0, dy: getDoubleValue(text, attribute: "y") ?? 0)

return Text(text: string, font: getFont(fontName: fontName, fontWeight: fontWeight, fontSize: fontSize), fill: fill, stroke: stroke, align: anchorToAlign(textAnchor), baseline: .bottom, place: position, opacity: opacity, tag: getTag(text))
let direction = getDirection(text)
return Text(text: string, font: getFont(fontName: fontName, fontWeight: fontWeight, fontSize: fontSize), fill: fill, stroke: stroke, align: anchorToAlign(textAnchor), baseline: .bottom, direction: direction, place: position, opacity: opacity, tag: getTag(text))
}

// REFACTOR
Expand Down Expand Up @@ -990,9 +990,7 @@ open class SVGParser {
nextStringWhitespace = true
}
trimmedString = withWhitespace ? " \(trimmedString)" : trimmedString
let text = Text(text: trimmedString, font: getFont(fontName: fontName, fontWeight: fontWeight, fontSize: fontSize),
fill: fill, stroke: stroke, align: anchorToAlign(textAnchor), baseline: .alphabetic,
place: Transform().move(dx: bounds.x + bounds.w, dy: bounds.y), opacity: opacity)
let text = Text(text: trimmedString, font: getFont(fontName: fontName, fontWeight: fontWeight, fontSize: fontSize), fill: fill, stroke: stroke, align: anchorToAlign(textAnchor), baseline: .alphabetic, place: Transform().move(dx: bounds.x + bounds.w, dy: bounds.y), opacity: opacity)
collection.append(text)
if tagRange.location >= fullString.length { // leave recursion
return collection
Expand All @@ -1013,11 +1011,8 @@ open class SVGParser {
let pos = getTspanPosition(element, bounds: bounds, previousCollectedTspan: previousCollectedTspan, withWhitespace: &shouldAddWhitespace)
let text = shouldAddWhitespace ? " \(string)" : string
let attributes = getStyleAttributes([:], element: element)

return Text(text: text, font: getFont(attributes, fontName: fontName, fontWeight: fontWeight, fontSize: fontSize),
fill: (attributes[SVGKeys.fill] != nil) ? getFillColor(attributes)! : fill, stroke: stroke ?? getStroke(attributes),
align: anchorToAlign(textAnchor ?? getTextAnchor(attributes)), baseline: .alphabetic,
place: pos, opacity: getOpacity(attributes), tag: getTag(element))
let direction = getDirection(element)
return Text(text: text, font: getFont(attributes, fontName: fontName, fontWeight: fontWeight, fontSize: fontSize), fill: (attributes[SVGKeys.fill] != nil) ? getFillColor(attributes)! : fill, stroke: stroke ?? getStroke(attributes), align: anchorToAlign(textAnchor ?? getTextAnchor(attributes)), baseline: .alphabetic, direction: direction, place: pos, opacity: getOpacity(attributes), tag: getTag(element))
}

fileprivate func getFont(_ attributes: [String: String] = [:], fontName: String?, fontWeight: String?, fontSize: Int?) -> Font {
Expand All @@ -1026,6 +1021,12 @@ open class SVGParser {
size: getFontSize(attributes) ?? fontSize ?? 12,
weight: getFontWeight(attributes) ?? fontWeight ?? "normal")
}

private func getDirection(_ element: SWXMLHash.XMLElement) -> Direction {
let direction = element.allAttributes["direction"]?.text
let unicodebidi = element.allAttributes["unicode-bidi"]?.text
return Direction.from(direction: direction, unicodebidi: unicodebidi)
}

fileprivate func getTspanPosition(_ element: SWXMLHash.XMLElement, bounds: Rect, previousCollectedTspan: Node?, withWhitespace: inout Bool) -> Transform {
var xPos: Double = bounds.x + bounds.w
Expand Down Expand Up @@ -1594,7 +1595,7 @@ open class SVGParser {
return Shape(form: shape.form, fill: shape.fill, stroke: shape.stroke, place: pos, opaque: opaque, clip: clip, visible: visible, tag: tag)
}
if let text = referenceNode as? Text {
return Text(text: text.text, font: text.font, fill: text.fill, stroke: text.stroke, align: text.align, baseline: text.baseline, place: pos, opaque: opaque, clip: clip, visible: visible, tag: tag)
return Text(text: text.text, font: text.font, fill: text.fill, stroke: text.stroke, align: text.align, baseline: text.baseline, direction: text.direction, place: pos, opaque: opaque, clip: clip, visible: visible, tag: tag)
}
if let image = referenceNode as? Image {
return Image(src: image.src, xAlign: image.xAlign, yAlign: image.yAlign, aspectRatio: image.aspectRatio, w: image.w, h: image.h, place: pos, opaque: opaque, clip: clip, visible: visible, tag: tag)
Expand Down

0 comments on commit 6314726

Please sign in to comment.