diff --git a/Example-macOS/Example-macOS.xcodeproj/project.pbxproj b/Example-macOS/Example-macOS.xcodeproj/project.pbxproj index 2de1a47a..fa69904f 100644 --- a/Example-macOS/Example-macOS.xcodeproj/project.pbxproj +++ b/Example-macOS/Example-macOS.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 49265C832227BB7A00923A66 /* TextsExampleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49265C822227BB7A00923A66 /* TextsExampleView.swift */; }; 970AB811D179560D74930D39 /* Pods_Example_macOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5184A00051CAC5E360198B48 /* Pods_Example_macOS.framework */; }; A715CA7D215E472800EE7651 /* ExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A715CA7C215E472800EE7651 /* ExampleViewController.swift */; }; A72862CB1F4308A50033893D /* AnimationsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A72862CA1F4308A50033893D /* AnimationsViewController.swift */; }; @@ -25,6 +26,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 49265C822227BB7A00923A66 /* TextsExampleView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextsExampleView.swift; sourceTree = ""; }; 5184A00051CAC5E360198B48 /* Pods_Example_macOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Example_macOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; A715CA7C215E472800EE7651 /* ExampleViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleViewController.swift; sourceTree = ""; }; A72862CA1F4308A50033893D /* AnimationsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AnimationsViewController.swift; path = Examples/Animations/AnimationsViewController.swift; sourceTree = ""; }; @@ -66,6 +68,15 @@ name = Frameworks; sourceTree = ""; }; + 49265C812227BB7A00923A66 /* Text */ = { + isa = PBXGroup; + children = ( + 49265C822227BB7A00923A66 /* TextsExampleView.swift */, + ); + name = Text; + path = ../../Example/Example/Examples/Text; + sourceTree = ""; + }; 4F8946156C3F28DC7B262776 /* Pods */ = { isa = PBXGroup; children = ( @@ -110,6 +121,7 @@ A759399F1F41C51A000CE329 /* Examples */ = { isa = PBXGroup; children = ( + 49265C812227BB7A00923A66 /* Text */, A715CA7C215E472800EE7651 /* ExampleViewController.swift */, A75939A01F41C544000CE329 /* Shapes */, A75939A61F41C58D000CE329 /* Transform */, @@ -281,7 +293,7 @@ files = ( ); inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Example-macOS/Pods-Example-macOS-frameworks.sh", + "${SRCROOT}/Pods/Target Support Files/Pods-Example-macOS/Pods-Example-macOS-frameworks.sh", "${BUILT_PRODUCTS_DIR}/Macaw/Macaw.framework", "${BUILT_PRODUCTS_DIR}/SWXMLHash/SWXMLHash.framework", ); @@ -292,7 +304,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Example-macOS/Pods-Example-macOS-frameworks.sh\"\n"; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Example-macOS/Pods-Example-macOS-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -310,6 +322,7 @@ A768EAD81F42C98E00F22A17 /* EventsExampleController.swift in Sources */, A72862CE1F430DF00033893D /* EasingViewController.swift in Sources */, A72862CB1F4308A50033893D /* AnimationsViewController.swift in Sources */, + 49265C832227BB7A00923A66 /* TextsExampleView.swift in Sources */, A768EADE1F42CCBC00F22A17 /* AnimationsView.swift in Sources */, A75939A81F41C5A8000CE329 /* TransformExampleView.swift in Sources */, A768EADC1F42CC7C00F22A17 /* EasingView.swift in Sources */, diff --git a/Example-macOS/Example-macOS/Base.lproj/Main.storyboard b/Example-macOS/Example-macOS/Base.lproj/Main.storyboard index 3809006d..07ae1950 100644 --- a/Example-macOS/Example-macOS/Base.lproj/Main.storyboard +++ b/Example-macOS/Example-macOS/Base.lproj/Main.storyboard @@ -1,8 +1,8 @@ - + - + @@ -111,7 +111,7 @@ - + @@ -120,6 +120,7 @@ + @@ -138,6 +139,7 @@ + @@ -345,5 +347,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Example/Example.xcodeproj/project.pbxproj b/Example/Example.xcodeproj/project.pbxproj index 04fc2ac4..dcb8c18f 100644 --- a/Example/Example.xcodeproj/project.pbxproj +++ b/Example/Example.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 48BC2FE8BD3BF26100D04E07 /* Pods_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0A92F90D3D20D2A289BED45F /* Pods_Example.framework */; }; + 49265C802227B1EB00923A66 /* TextsExampleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49265C7F2227B1EB00923A66 /* TextsExampleView.swift */; }; 5747F9BD1E38B683004E338F /* MorphingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5747F9BC1E38B683004E338F /* MorphingView.swift */; }; 574EC43E1CB7DE7F0063F317 /* ShapesExampleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 574EC4341CB7DE7F0063F317 /* ShapesExampleView.swift */; }; 574EC4411CB7E2440063F317 /* MenuViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 574EC4401CB7E2440063F317 /* MenuViewController.swift */; }; @@ -41,6 +42,7 @@ /* Begin PBXFileReference section */ 0A92F90D3D20D2A289BED45F /* Pods_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 131DDFA4A9D164CC4B64C3E8 /* Pods-Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-Example/Pods-Example.release.xcconfig"; sourceTree = ""; }; + 49265C7F2227B1EB00923A66 /* TextsExampleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextsExampleView.swift; sourceTree = ""; }; 5747F9BC1E38B683004E338F /* MorphingView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MorphingView.swift; sourceTree = ""; }; 574EC4341CB7DE7F0063F317 /* ShapesExampleView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShapesExampleView.swift; sourceTree = ""; }; 574EC4401CB7E2440063F317 /* MenuViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MenuViewController.swift; path = Example/MenuViewController.swift; sourceTree = SOURCE_ROOT; }; @@ -82,6 +84,14 @@ name = Pods; sourceTree = ""; }; + 49265C7E2227B1B200923A66 /* Text */ = { + isa = PBXGroup; + children = ( + 49265C7F2227B1EB00923A66 /* TextsExampleView.swift */, + ); + path = Text; + sourceTree = ""; + }; 5747F9BB1E38B660004E338F /* Morphing */ = { isa = PBXGroup; children = ( @@ -93,6 +103,7 @@ 574EC4271CB7DE7F0063F317 /* Examples */ = { isa = PBXGroup; children = ( + 49265C7E2227B1B200923A66 /* Text */, 574EC4331CB7DE7F0063F317 /* Shapes */, 6699B7CE1DFFE8B90072585E /* Transform */, 574EC4421CBB607E0063F317 /* Animations */, @@ -353,6 +364,7 @@ 5BAE3CB120C54E3D006BEF51 /* FiltersViewController.swift in Sources */, 57AF398C1E67E9DB00F0BFE2 /* EventsExampleController.swift in Sources */, B04416FA1E041A420016BC50 /* EasingView.swift in Sources */, + 49265C802227B1EB00923A66 /* TextsExampleView.swift in Sources */, 574EC4411CB7E2440063F317 /* MenuViewController.swift in Sources */, 574EC43E1CB7DE7F0063F317 /* ShapesExampleView.swift in Sources */, B04416FC1E04282A0016BC50 /* EasingExampleController.swift in Sources */, diff --git a/Example/Example/Base.lproj/Main.storyboard b/Example/Example/Base.lproj/Main.storyboard index 8059167b..ee523c93 100644 --- a/Example/Example/Base.lproj/Main.storyboard +++ b/Example/Example/Base.lproj/Main.storyboard @@ -1,11 +1,11 @@ - + - + @@ -353,5 +353,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Example/Example/Examples/Text/TextsExampleView.swift b/Example/Example/Examples/Text/TextsExampleView.swift new file mode 100644 index 00000000..bea97afc --- /dev/null +++ b/Example/Example/Examples/Text/TextsExampleView.swift @@ -0,0 +1,46 @@ +// +// TextsExampleView.swift +// Example +// +// Created by Andrew Romanov on 28/02/2019. +// Copyright © 2019 Exyte. All rights reserved. +// + +import Macaw + +class TextsExampleView: MacawView { + + required init?(coder aDecoder: NSCoder) { + let text1 = TextsExampleView.newText("Font", .move(dx: 100, dy: 40)) + text1.font = Font(name: "Helvetica", size: 20, weight: "normal") + + let text2 = TextsExampleView.newText("Stroke", .move(dx: 100, dy: 200)) + text2.font = Font(name: "Helvetica", size: 40, weight: "normal") + text2.fill = Color(val: 0xFF0000); + text2.stroke = Stroke(fill: Color(val: 0x00FF00), width: 20.0); + + let text4 = TextsExampleView.newText("Stroke", .move(dx: 100, dy: 200)) + text4.font = Font(name: "Helvetica", size: 40, weight: "normal") + text4.fill = Color(val: 0xFF0000); + + let text3 = TextsExampleView.newText("Kern inc", .move(dx: 100, dy: 250)) + text3.kern = 3.0 + + let text5 = TextsExampleView.newText("Kern dec", .move(dx: 100, dy: 300)) + text5.kern = -1.0 + + let group = Group( + contents: [ + text1, text2, text3, text4, text5 + ] + ) + + super.init(node: group, coder: aDecoder) + } + + fileprivate static func newText(_ text: String, _ place: Transform, baseline: Baseline = .bottom) -> Text { + return Text(text: text, fill: Color.black, align: .mid, baseline: baseline, place: place) + } + +} + diff --git a/Example/Example/MenuViewController.swift b/Example/Example/MenuViewController.swift index 26ec5f96..e0c55692 100644 --- a/Example/Example/MenuViewController.swift +++ b/Example/Example/MenuViewController.swift @@ -12,7 +12,8 @@ open class MenuViewController: UIViewController, UITableViewDataSource, UITableV "EasingExampleController", "MorphingExampleController", "EventsExampleController", - "FiltersViewController" + "FiltersViewController", + "TextsViewController" ].map { UIStoryboard(name: "Main", bundle: .none).instantiateViewController(withIdentifier: $0) } diff --git a/Source/model/scene/Text.swift b/Source/model/scene/Text.swift index d9899eb9..2d389217 100644 --- a/Source/model/scene/Text.swift +++ b/Source/model/scene/Text.swift @@ -41,14 +41,21 @@ open class Text: Node { get { return baselineVar.value } set(val) { baselineVar.value = val } } + + public let kernVar: Variable + open var kern: Float { + get { return kernVar.value } + set(val) { kernVar.value = val} + } - public init(text: String, font: Font? = nil, fill: Fill? = Color.black, stroke: Stroke? = nil, align: Align = .min, baseline: Baseline = .top, 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, kern: 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] = []) { self.textVar = Variable(text) self.fontVar = Variable(font) self.fillVar = Variable(fill) self.strokeVar = Variable(stroke) self.alignVar = Variable(align) self.baselineVar = Variable(baseline) + self.kernVar = Variable(kern) super.init( place: place, opaque: opaque, @@ -75,6 +82,7 @@ open class Text: Node { } var stringAttributes: [NSAttributedString.Key: AnyObject] = [:] stringAttributes[NSAttributedString.Key.font] = font + stringAttributes[NSAttributedString.Key.kern] = NSNumber(value: self.kern) let size = (text as NSString).size(withAttributes: stringAttributes) return Rect( x: calculateAlignmentOffset(font: font), diff --git a/Source/render/TextRenderer.swift b/Source/render/TextRenderer.swift index 6f19a29f..9a79ba77 100644 --- a/Source/render/TextRenderer.swift +++ b/Source/render/TextRenderer.swift @@ -35,6 +35,7 @@ class TextRenderer: NodeRenderer { observe(text.strokeVar) observe(text.alignVar) observe(text.baselineVar) + observe(text.kernVar) } override func doRender(in context: CGContext, force: Bool, opacity: Double, coloringMode: ColoringMode = .rgb) { @@ -65,6 +66,10 @@ class TextRenderer: NodeRenderer { } attributes[NSAttributedString.Key.strokeWidth] = width as NSObject? } + if text.kern != 0.0 { + attributes[NSAttributedString.Key.kern] = NSNumber(value: text.kern) + } + if attributes.count > 1 { MGraphicsPushContext(context) message.draw(in: getBounds(font), withAttributes: attributes) @@ -129,7 +134,13 @@ class TextRenderer: NodeRenderer { return .zero } - let textAttributes = [NSAttributedString.Key.font: font] + var textAttributes : [NSAttributedString.Key: Any] = [NSAttributedString.Key.font: font] + if (text.kern != 0.0){ + textAttributes[NSAttributedString.Key.kern] = NSNumber(value: text.kern) + } + 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),