Skip to content

Commit

Permalink
Merge pull request #291 from f3dm76/task/svgViewBox
Browse files Browse the repository at this point in the history
Fix #87: Support <svg> viewBox attribute
  • Loading branch information
ystrot authored Apr 11, 2018
2 parents 2cec07b + 2ea6621 commit dd7b790
Show file tree
Hide file tree
Showing 13 changed files with 315 additions and 224 deletions.
17 changes: 16 additions & 1 deletion Macaw.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,10 @@
57F1087C1F53CA7E00DC365B /* MDisplayLink_iOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57F1087B1F53CA7E00DC365B /* MDisplayLink_iOS.swift */; };
57FCD2771D76EA4600CC0FB6 /* Macaw.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 57FCD26C1D76EA4600CC0FB6 /* Macaw.framework */; };
57FCD27C1D76EA4600CC0FB6 /* MacawTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57FCD27B1D76EA4600CC0FB6 /* MacawTests.swift */; };
5B1FFD7A207E083600716A46 /* SvgContentLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BAA56A7207C73FF0055BC5B /* SvgContentLayout.swift */; };
5BAA56A8207C73FF0055BC5B /* SvgContentLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BAA56A7207C73FF0055BC5B /* SvgContentLayout.swift */; };
5BAEA9C9206CEAA20049AAAE /* viewBox.svg in Resources */ = {isa = PBXBuildFile; fileRef = 5BAEA9C8206CEAA20049AAAE /* viewBox.svg */; };
5BAEA9CB206CEB7D0049AAAE /* viewBox.reference in Resources */ = {isa = PBXBuildFile; fileRef = 5BAEA9CA206CEB7D0049AAAE /* viewBox.reference */; };
A718CD441F45C28200966E06 /* Common_iOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = A718CD431F45C28200966E06 /* Common_iOS.swift */; };
A718CD471F45C28700966E06 /* Graphics_iOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = A718CD451F45C28700966E06 /* Graphics_iOS.swift */; };
A718CD481F45C28700966E06 /* MView_iOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = A718CD461F45C28700966E06 /* MView_iOS.swift */; };
Expand Down Expand Up @@ -449,6 +453,9 @@
57FCD2761D76EA4600CC0FB6 /* MacawTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MacawTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
57FCD27B1D76EA4600CC0FB6 /* MacawTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacawTests.swift; sourceTree = "<group>"; };
57FCD27D1D76EA4600CC0FB6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
5BAA56A7207C73FF0055BC5B /* SvgContentLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SvgContentLayout.swift; sourceTree = "<group>"; };
5BAEA9C8206CEAA20049AAAE /* viewBox.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = viewBox.svg; sourceTree = "<group>"; };
5BAEA9CA206CEB7D0049AAAE /* viewBox.reference */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = viewBox.reference; sourceTree = "<group>"; };
A718CD431F45C28200966E06 /* Common_iOS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Common_iOS.swift; path = Source/platform/iOS/Common_iOS.swift; sourceTree = SOURCE_ROOT; };
A718CD451F45C28700966E06 /* Graphics_iOS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Graphics_iOS.swift; path = Source/platform/iOS/Graphics_iOS.swift; sourceTree = SOURCE_ROOT; };
A718CD461F45C28700966E06 /* MView_iOS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MView_iOS.swift; path = Source/platform/iOS/MView_iOS.swift; sourceTree = SOURCE_ROOT; };
Expand Down Expand Up @@ -547,6 +554,7 @@
57900FF71EA0DEBF00809FFB /* utils */ = {
isa = PBXGroup;
children = (
5BAA56A7207C73FF0055BC5B /* SvgContentLayout.swift */,
57900FF81EA0DEBF00809FFB /* UIImage2Image.swift */,
);
path = utils;
Expand All @@ -555,6 +563,7 @@
57CAB1241D7832E000FD8E47 /* svg */ = {
isa = PBXGroup;
children = (
5BAEA9C8206CEAA20049AAAE /* viewBox.svg */,
C46E83541F94B20E00208037 /* transform.svg */,
C43B064C1F9738EF00787A35 /* clip.svg */,
C4153A8E1F8793DD001BA5EE /* small-logo.png */,
Expand All @@ -568,6 +577,7 @@
57CAB12B1D7832E000FD8E47 /* rect.svg */,
57CAB12C1D7832E000FD8E47 /* roundRect.svg */,
57CAB12D1D7832E000FD8E47 /* triangle.svg */,
5BAEA9CA206CEB7D0049AAAE /* viewBox.reference */,
C43B06521F989D9300787A35 /* transform.reference */,
C43B06541F98A53600787A35 /* group.reference */,
C43B06561F98A7B700787A35 /* arcsgroup.reference */,
Expand Down Expand Up @@ -848,7 +858,8 @@
572CEFC31E2CED4B008C7C83 /* Dependencies */,
57E5E0E01E3B393900D1CB28 /* Source */,
);
path = Macaw;
name = Macaw;
path = Source;
sourceTree = "<group>";
};
57FCD27A1D76EA4600CC0FB6 /* MacawTests */ = {
Expand Down Expand Up @@ -1057,6 +1068,7 @@
57CAB1361D7832E000FD8E47 /* triangle.svg in Resources */,
C43B06691F99FC2300787A35 /* pathbounds4.svg in Resources */,
C43B06631F99A33400787A35 /* pathbounds3.svg in Resources */,
5BAEA9C9206CEAA20049AAAE /* viewBox.svg in Resources */,
C43B064D1F9738EF00787A35 /* clip.svg in Resources */,
57B7A4E11EE70DA5009D78D7 /* logo_base64.txt in Resources */,
C43B06671F99EE7300787A35 /* cubicRelative.svg in Resources */,
Expand All @@ -1077,6 +1089,7 @@
57CAB1351D7832E000FD8E47 /* roundRect.svg in Resources */,
57CAB12E1D7832E000FD8E47 /* circle.svg in Resources */,
57CAB1331D7832E000FD8E47 /* polyline.svg in Resources */,
5BAEA9CB206CEB7D0049AAAE /* viewBox.reference in Resources */,
57CAB1311D7832E000FD8E47 /* line.svg in Resources */,
57B7A4DF1EE70D17009D78D7 /* logo.png in Resources */,
57CAB1321D7832E000FD8E47 /* polygon.svg in Resources */,
Expand Down Expand Up @@ -1122,6 +1135,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5B1FFD7A207E083600716A46 /* SvgContentLayout.swift in Sources */,
57614AFD1F83D15600875933 /* Group.swift in Sources */,
57D9DAE11FC9AA4C0002555D /* Locus+ToPath.swift in Sources */,
57614AFE1F83D15600875933 /* TextRenderer.swift in Sources */,
Expand Down Expand Up @@ -1350,6 +1364,7 @@
57E5E1A01E3B393900D1CB28 /* Node.swift in Sources */,
57E5E1751E3B393900D1CB28 /* PanEvent.swift in Sources */,
57E5E1771E3B393900D1CB28 /* RotateEvent.swift in Sources */,
5BAA56A8207C73FF0055BC5B /* SvgContentLayout.swift in Sources */,
57E5E18F1E3B393900D1CB28 /* Insets.swift in Sources */,
57E5E19A1E3B393900D1CB28 /* Rect.swift in Sources */,
57E5E1941E3B393900D1CB28 /* PathBuilder.swift in Sources */,
Expand Down
4 changes: 4 additions & 0 deletions MacawTests/MacawSVGTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ class MacawSVGTests: XCTestCase {
}
}
}

func testViewBox() {
validate("viewBox", withReference: true)
}

func testClipWithParser() {
validate("clip", withReference: true)
Expand Down
1 change: 1 addition & 0 deletions MacawTests/svg/viewBox.reference
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" ><defs><clipPath id="clipPath1"><rect height="400" x="100" y="50" width="400" /></clipPath></defs><g clip-path="url(#clipPath1)" transform="matrix(0.444444444444444,0.0,0.0,0.444444444444444,-5.55555555555554,-22.2222222222222)" ><g><g><ellipse cy="80" ry="50" rx="100" cx="200" fill="yellow" stroke="purple" stroke-width="2.0"/></g></g></svg>
1 change: 1 addition & 0 deletions MacawTests/svg/viewBox.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
32 changes: 28 additions & 4 deletions Source/model/draw/Align.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,29 @@
public enum Align {
case min
case mid
case max
open class Align {

public static let min: Align = Align()
public static let mid: Align = MidAlign()
public static let max: Align = MaxAlign()

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

open func align(size: Double) -> Double {
return align(outer: size, inner: 0)
}

}

private class MidAlign : Align {

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

private class MaxAlign : Align {

override func align(outer: Double, inner: Double) -> Double {
return outer - inner
}
}
67 changes: 63 additions & 4 deletions Source/model/draw/AspectRatio.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,64 @@
public enum AspectRatio {
case none
case meet
case slice
open class AspectRatio {

public static let none: AspectRatio = NoneAspectRatio()
public static let meet: AspectRatio = MeetAspectRatio()
public static let slice: AspectRatio = SliceAspectRatio()

open func fit(rect: Rect, into rectToFitIn: Rect) -> Size {
return Size(w: 0, h: 0)
}

open func fit(size: Size, into rectToFitIn: Rect) -> Size {
return fit(rect: Rect(x: 0, y: 0, w: size.w, h: size.h), into: rectToFitIn)
}

open func fit(size: Size, into sizeToFitIn: Size) -> Size {
return fit(size: size, into: Rect(x: 0, y: 0, w: sizeToFitIn.w, h: sizeToFitIn.h))
}

}

private class NoneAspectRatio : AspectRatio {

override func fit(rect: Rect, into rectToFitIn: Rect) -> Size {
return Size(w: rectToFitIn.w, h: rectToFitIn.h)
}
}

private class MeetAspectRatio : AspectRatio {

override func fit(rect: Rect, into rectToFitIn: Rect) -> Size {
let widthRatio = rectToFitIn.w / rect.w
let heightRatio = rectToFitIn.h / rect.h

var newWidth = rectToFitIn.w
var newHeight = rectToFitIn.h

if heightRatio < widthRatio {
newWidth = rect.w * heightRatio
}
else {
newHeight = rect.h * widthRatio
}
return Size(w: newWidth, h: newHeight)
}
}

private class SliceAspectRatio : AspectRatio {

override func fit(rect: Rect, into rectToFitIn: Rect) -> Size {
let widthRatio = rectToFitIn.w / rect.w
let heightRatio = rectToFitIn.h / rect.h

var newWidth = rectToFitIn.w
var newHeight = rectToFitIn.h

if heightRatio > widthRatio {
newWidth = rect.w * heightRatio
}
else {
newHeight = rect.h * widthRatio
}
return Size(w: newWidth, h: newHeight)
}
}
11 changes: 1 addition & 10 deletions Source/model/scene/Text.swift
Original file line number Diff line number Diff line change
Expand Up @@ -121,16 +121,7 @@ open class Text: Node {
NSAttributedStringKey.font: font
]
let textSize = NSString(string: text).size(withAttributes: textAttributes)
var alignmentOffset = 0.0
switch align {
case .mid:
alignmentOffset = (textSize.width / 2).doubleValue
case .max:
alignmentOffset = textSize.width.doubleValue
default:
break
}
return -alignmentOffset
return -align.align(size: textSize.width.doubleValue)
}

}
93 changes: 8 additions & 85 deletions Source/render/ImageRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -95,91 +95,14 @@ class ImageRenderer: NodeRenderer {
} else if h == 0 {
h = imageSize.height * w / imageSize.width
}
switch image.aspectRatio {
case AspectRatio.meet:
return calculateMeetAspectRatio(image, size: imageSize)
case AspectRatio.slice:
return calculateSliceAspectRatio(image, size: imageSize)
//ctx.cgContext!.clip(to: CGRect(x: 0, y: 0, width: w, height: h))
default:
return CGRect(x: 0, y: 0, width: w, height: h)
}
}
}

fileprivate func calculateMeetAspectRatio(_ image: Image, size: CGSize) -> CGRect {
let w = CGFloat(image.w)
let h = CGFloat(image.h)
// destination and source aspect ratios
let destAR = w / h
let srcAR = size.width / size.height
var resultW = w
var resultH = h
var destX = CGFloat(0)
var destY = CGFloat(0)
if destAR < srcAR {
// fill all available width and scale height
resultH = size.height * w / size.width
} else {
// fill all available height and scale width
resultW = size.width * h / size.height
}
let xalign = image.xAlign
switch xalign {
case Align.min:
destX = 0
case Align.mid:
destX = w / 2 - resultW / 2
case Align.max:
destX = w - resultW
}
let yalign = image.yAlign
switch yalign {
case Align.min:
destY = 0
case Align.mid:
destY = h / 2 - resultH / 2
case Align.max:
destY = h - resultH
}
return CGRect(x: destX, y: destY, width: resultW, height: resultH)
}

fileprivate func calculateSliceAspectRatio(_ image: Image, size: CGSize) -> CGRect {
let w = CGFloat(image.w)
let h = CGFloat(image.h)
var srcX = CGFloat(0)
var srcY = CGFloat(0)
var totalH: CGFloat = 0
var totalW: CGFloat = 0
// destination and source aspect ratios
let destAR = w / h
let srcAR = size.width / size.height
if destAR > srcAR {
// fill all available width and scale height
totalH = size.height * w / size.width
totalW = w
switch image.yAlign {
case Align.min:
srcY = 0
case Align.mid:
srcY = -(totalH / 2 - h / 2)
case Align.max:
srcY = -(totalH - h)
}
} else {
// fill all available height and scale width
totalW = size.width * h / size.height
totalH = h
switch image.xAlign {
case Align.min:
srcX = 0
case Align.mid:
srcX = -(totalW / 2 - w / 2)
case Align.max:
srcX = -(totalW - w)
}

let newSize = image.aspectRatio.fit(
size: Size(w: Double(image.w), h: Double(image.h)),
into: Size(w: Double(imageSize.width), h: Double(imageSize.height))
)
let destX = image.xAlign.align(outer: w.doubleValue, inner: newSize.w)
let destY = image.yAlign.align(outer: h.doubleValue, inner: newSize.h)
return CGRect(x: destX, y: destY, width: newSize.w, height: newSize.h)
}
return CGRect(x: srcX, y: srcY, width: totalW, height: totalH)
}
}
11 changes: 1 addition & 10 deletions Source/render/TextRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -145,16 +145,7 @@ class TextRenderer: NodeRenderer {
NSAttributedStringKey.font: font
]
let textSize = NSString(string: text.text).size(withAttributes: textAttributes)
var alignmentOffset = CGFloat(0)
switch text.align {
case Align.mid:
alignmentOffset = textSize.width / 2
case Align.max:
alignmentOffset = textSize.width
default:
break
}
return -alignmentOffset
return -CGFloat(text.align.align(size: textSize.width.doubleValue))
}

fileprivate func getTextColor(_ fill: Fill) -> MColor {
Expand Down
Loading

0 comments on commit dd7b790

Please sign in to comment.