Skip to content

Commit

Permalink
Merge branch 'master1' into task/fontWeight
Browse files Browse the repository at this point in the history
# Conflicts:
#	Source/render/RenderUtils.swift
#	Source/render/TextRenderer.swift
  • Loading branch information
f3dm76 committed May 3, 2018
2 parents f4adb55 + 495f42e commit 41b31ad
Show file tree
Hide file tree
Showing 10 changed files with 124 additions and 95 deletions.
2 changes: 0 additions & 2 deletions MacawTests/MacawSVGTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,6 @@ class MacawSVGTests: XCTestCase {
validate("triangle")
}



func validateJSON(node: Node, referenceFile: String) {
let bundle = Bundle(for: type(of: TestUtils()))

Expand Down
69 changes: 23 additions & 46 deletions MacawTests/SceneSerialization.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,22 @@ func parse<T: Initializable>(_ from: Any?) -> T {
return from as? T ?? T()
}


internal protocol Serializable {
protocol Serializable {
func toDictionary() -> [String:Any]
}



class NodeSerializer {

var factories = [String:([String:Any]) -> Node]()

static var shared = NodeSerializer()
let locusSerializer = LocusSerializer()

init() {
factories["Shape"] = { dictionary in
let locusDict = dictionary["form"] as! [String:Any]
let locus = LocusSerializer.shared.instance(dictionary: locusDict)
let locus = self.locusSerializer.instance(dictionary: locusDict)

let shape = Shape(form: locus)
shape.fromDictionary(dictionary: dictionary) // fill in the fields inherited from Node

if let fillDict = dictionary["fill"] as? [String:Any], let fillType = fillDict["type"] as? String, fillType == "Color" {
shape.fill = Color(dictionary: fillDict)
Expand All @@ -54,7 +50,6 @@ class NodeSerializer {
factories["Text"] = { dictionary in
let textString = dictionary["text"] as! String
let text = Text(text: textString)
text.fromDictionary(dictionary: dictionary) // fill in the fields inherited from Node

if let fontDict = dictionary["font"] as? [String:Any] {
text.font = Font(dictionary: fontDict)
Expand All @@ -81,18 +76,22 @@ class NodeSerializer {
let contents = dictionary["contents"] as! [[String:Any]]
var nodes = [Node]()
for dict in contents {
nodes.append(NodeSerializer.shared.instance(dictionary: dict))
nodes.append(self.instance(dictionary: dict))
}
let group = Group()
group.fromDictionary(dictionary: dictionary) // fill in the fields inherited from Node
group.contents = nodes
return group
return Group(contents: nodes)
}
}

func instance(dictionary: [String:Any]) -> Node {
let type = dictionary["node"] as! String
return factories[type]!(dictionary)
let node = factories[type]!(dictionary)
node.place = Transform(string: dictionary["place"] as? String)
node.opaque = Bool(dictionary["opaque"] as? String ?? "") ?? true
node.opacity = Double(dictionary["opacity"] as? String ?? "") ?? 0
if let locusDict = dictionary["clip"] as? [String:Any] {
node.clip = locusSerializer.instance(dictionary: locusDict)
}
return node
}
}

Expand All @@ -105,15 +104,6 @@ extension Node {
}
return result
}

func fromDictionary(dictionary: [String:Any]) {
place = Transform(string: dictionary["place"] as? String)
opaque = Bool(dictionary["opaque"] as? String ?? "") ?? true
opacity = Double(dictionary["opacity"] as? String ?? "") ?? 0
if let locusDict = dictionary["clip"] as? [String:Any] {
clip = LocusSerializer.shared.instance(dictionary: locusDict)
}
}
}

extension Shape: Serializable {
Expand Down Expand Up @@ -171,14 +161,10 @@ extension Group: Serializable {
}
}



class LocusSerializer {

var factories = [String:([String:Any]) -> Locus]()

static var shared = LocusSerializer()

init() {
factories["Arc"] = { dictionary in
Arc(ellipse: self.instance(dictionary: dictionary["Ellipse"] as! [String:Any]) as! Ellipse,
Expand Down Expand Up @@ -206,7 +192,9 @@ class LocusSerializer {
let array = dictionary["segments"] as! [[String:Any]]
var pathSegments = [PathSegment]()
for dict in array {
pathSegments.append(PathSegment(dictionary: dict))
pathSegments.append(PathSegment(
type: typeForString(dict["type"] as! String),
data: dict["data"] as! [Double]))
}
return Path(segments: pathSegments)
}
Expand All @@ -228,7 +216,7 @@ class LocusSerializer {
ry: parse(dictionary["ry"]))
}
}

func instance(dictionary: [String:Any]) -> Locus {
let type = dictionary["type"] as! String
return factories[type]!(dictionary)
Expand Down Expand Up @@ -302,20 +290,11 @@ extension RoundRect: Serializable {
}
}



extension PathSegment: Serializable {

internal func toDictionary() -> [String:Any] {
func toDictionary() -> [String:Any] {
return ["type": "\(type)", "data": data]
}

convenience init(dictionary: [String:Any]) {
guard let typeString = dictionary["type"] as? String, let array = dictionary["data"] as? [Double] else { self.init(); return }

self.init(type: typeForString(typeString),
data: array)
}
}

extension Color: Serializable {
Expand All @@ -331,15 +310,15 @@ extension Color: Serializable {

extension Stroke: Serializable {

internal func toDictionary() -> [String:Any] {
func toDictionary() -> [String:Any] {
var result = ["width": width, "cap": "\(cap)", "join": "\(join)", "dashes": dashes] as [String : Any]
if let fillColor = fill as? Color {
result["fill"] = fillColor.toDictionary()
}
return result
}

internal convenience init?(dictionary: [String:Any]) {
convenience init?(dictionary: [String:Any]) {

guard let fillDict = dictionary["fill"] as? [String:Any], let fillType = fillDict["type"] as? String, fillType == "Color", let fill = Color(dictionary: fillDict) else {
return nil
Expand All @@ -363,7 +342,7 @@ extension Stroke: Serializable {

extension Font: Serializable {

internal func toDictionary() -> [String:Any] {
func toDictionary() -> [String:Any] {
return ["name": name, "size": size, "weight": weight]
}

Expand Down Expand Up @@ -407,7 +386,7 @@ extension Transform {

extension Align {

internal func toString() -> String {
func toString() -> String {
if self === Align.mid {
return "mid"
}
Expand All @@ -417,7 +396,7 @@ extension Align {
return "min"
}

internal static func instantiate(string: String) -> Align {
static func instantiate(string: String) -> Align {
switch string {
case "mid":
return .mid
Expand All @@ -429,8 +408,6 @@ extension Align {
}
}



fileprivate func typeForString(_ string: String) -> PathSegmentType {
switch(string) {
case "M": return .M
Expand Down
14 changes: 14 additions & 0 deletions MacawTests/w3c-test-suite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
## W3C SVG Test Suite Coverage

Total: 6

Passed: 33%

|Name |Status |
|------|-------|
|[color-prop-01-b-manual](w3cSVGTests/color-prop-01-b-manual.svg) ||
|[color-prop-02-f-manual](w3cSVGTests/color-prop-02-f-manual.svg) ||
|[color-prop-03-t-manual](w3cSVGTests/color-prop-03-t-manual.svg) | [#123](https://github.com/exyte/Macaw/issues/123) |
|[color-prop-04-t-manual](w3cSVGTests/color-prop-04-t-manual.svg) | [#42](https://github.com/exyte/Macaw/issues/42) |
|[color-prop-05-t-manual](w3cSVGTests/color-prop-05-t-manual.svg) ||
|[shapes-circle-01-t-manual](w3cSVGTests/shapes-circle-01-t-manual.svg) ||
8 changes: 7 additions & 1 deletion Source/model/geom2d/Path.swift
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import Foundation

public enum FillRule {
case nonzero, evenodd
}

open class Path: Locus {

open let segments: [PathSegment]
open let fillRule: FillRule

public init(segments: [PathSegment] = []) {
public init(segments: [PathSegment] = [], fillRule: FillRule = .nonzero) {
self.segments = segments
self.fillRule = fillRule
}

override open func bounds() -> Rect {
Expand Down
4 changes: 4 additions & 0 deletions Source/platform/iOS/Common_iOS.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ extension MFont {
class var mSystemFontSize: CGFloat {
return UIFont.systemFontSize
}

class var mFamilyNames: [String] {
return UIFont.familyNames
}
}

extension UIScreen {
Expand Down
4 changes: 4 additions & 0 deletions Source/platform/macOS/Common_macOS.swift
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ extension NSFont {
class var mSystemFontSize: CGFloat {
return NSFont.systemFontSize
}

class var mFamilyNames: [String] {
return NSFontManager.shared.availableFontFamilies
}
}

extension NSScreen {
Expand Down
48 changes: 30 additions & 18 deletions Source/render/RenderUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -90,29 +90,41 @@ class RenderUtils {
fatalError("Unsupported node: \(node)")
}

static let availableFonts = MFont.mFamilyNames.map{ $0.lowercased() }

class func loadFont(name: String, size: Int, weight: String?) -> MFont? {
let separationSet = CharacterSet(charactersIn: ",")
let names = name.components(separatedBy: separationSet)
var customFont: MFont? = .none
names.forEach { fontName in
if customFont != .none {
return

var fontName = ""
let fontPriorities = name.split(separator: ",").map{ String($0).trimmingCharacters(in: CharacterSet(charactersIn: " '")).lowercased() }
for font in fontPriorities {
if availableFonts.contains(font) {
fontName = font
}

let fontName = fontName.trimmingCharacters(in: .whitespaces)
var fontDec = MFontDescriptor(name: fontName, size: CGFloat(size))
if weight == "bold" || weight == "bolder" {
#if os(iOS)
fontDec = fontDec.withSymbolicTraits(.traitBold)!
#elseif os(OSX)
fontDec = fontDec.withSymbolicTraits(.bold)
#endif

if font == "serif" {
fontName = "Georgia"
}
if font == "sans-serif" {
fontName = "Arial"
}
if font == "monospace" {
fontName = "Courier"
}
customFont = MFont(descriptor: fontDec, size: CGFloat(size))
}

return customFont
if fontName.isEmpty {
return .none
}

var fontDesc = MFontDescriptor(name: fontName, size: CGFloat(size))
if weight == "bold" || weight == "bolder" {
#if os(iOS)
fontDesc = fontDesc.withSymbolicTraits(.traitBold)!
#elseif os(OSX)
fontDesc = fontDesc.withSymbolicTraits(.bold)
#endif

}
return MFont(descriptor: fontDesc, size: CGFloat(size))
}

class func applyOpacity(_ color: Color, opacity: Double) -> Color {
Expand Down
15 changes: 10 additions & 5 deletions Source/render/ShapeRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,12 @@ class ShapeRenderer: NodeRenderer {

if shape.fill != nil || shape.stroke != nil {
setGeometry(shape.form, ctx: ctx.cgContext!)
drawPath(shape.fill, stroke: shape.stroke, ctx: ctx.cgContext!, opacity: opacity)

var fillRule = FillRule.nonzero
if let path = shape.form as? Path {
fillRule = path.fillRule
}
drawPath(shape.fill, stroke: shape.stroke, ctx: ctx.cgContext!, opacity: opacity, fillRule: fillRule)
}
}

Expand Down Expand Up @@ -102,7 +107,7 @@ class ShapeRenderer: NodeRenderer {
return CGRect(x: CGFloat(rect.x), y: CGFloat(rect.y), width: CGFloat(rect.w), height: CGFloat(rect.h))
}

fileprivate func drawPath(_ fill: Fill?, stroke: Stroke?, ctx: CGContext?, opacity: Double) {
fileprivate func drawPath(_ fill: Fill?, stroke: Stroke?, ctx: CGContext?, opacity: Double, fillRule: FillRule) {
var shouldStrokePath = false
if fill is Gradient || stroke?.fill is Gradient {
shouldStrokePath = true
Expand All @@ -112,15 +117,15 @@ class ShapeRenderer: NodeRenderer {
let path = ctx!.path
setFill(fill, ctx: ctx, opacity: opacity)
if stroke.fill is Gradient && !(fill is Gradient) {
ctx!.drawPath(using: .fill)
ctx!.drawPath(using: fillRule == .nonzero ? .fill : .eoFill)
}
drawWithStroke(stroke, ctx: ctx, opacity: opacity, shouldStrokePath: shouldStrokePath, path: path, mode: .fillStroke)
drawWithStroke(stroke, ctx: ctx, opacity: opacity, shouldStrokePath: shouldStrokePath, path: path, mode: fillRule == .nonzero ? .fillStroke : .eoFillStroke)
return
}

if let fill = fill {
setFill(fill, ctx: ctx, opacity: opacity)
ctx!.drawPath(using: .fill)
ctx!.drawPath(using: fillRule == .nonzero ? .fill : .eoFill)
return
}

Expand Down
21 changes: 9 additions & 12 deletions Source/render/TextRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,21 +76,18 @@ class TextRenderer: NodeRenderer {
// However it is needed for the Swift Package Manager to work accordingly.
return MFont()
}
guard let text = text else {
return MFont.systemFont(ofSize: 18.0)
guard let text = text, let textFont = text.font else {
return MFont.systemFont(ofSize: MFont.mSystemFontSize)
}

if let textFont = text.font {
if let customFont = RenderUtils.loadFont(name: textFont.name, size: textFont.size, weight: textFont.weight) {
return customFont
} else {
if let weight = getWeight(textFont.weight) {
return MFont.systemFont(ofSize: CGFloat(textFont.size), weight: weight)
}
return MFont.systemFont(ofSize: CGFloat(textFont.size))

if let customFont = RenderUtils.loadFont(name: textFont.name, size: textFont.size, weight: textFont.weight) {
return customFont
} else {
if let weight = getWeight(textFont.weight) {
return MFont.systemFont(ofSize: CGFloat(textFont.size), weight: weight)
}
return MFont.systemFont(ofSize: CGFloat(textFont.size))
}
return MFont.systemFont(ofSize: MFont.mSystemFontSize)
}

fileprivate func getWeight(_ weight: String) -> MFont.Weight? {
Expand Down
Loading

0 comments on commit 41b31ad

Please sign in to comment.