1
1
mirror of https://github.com/exyte/Macaw.git synced 2024-09-21 01:47:44 +03:00

Merge branch 'master' into task/visibilityAttribute

# Conflicts:
#	Source/svg/SVGParser.swift
This commit is contained in:
Alisa Mylnikova 2018-04-23 21:09:30 +07:00
commit 26a3e7bee1
9 changed files with 136 additions and 57 deletions

View File

@ -253,6 +253,7 @@
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 */; };
602C561D2081C984003AD452 /* rounded.svg in Resources */ = {isa = PBXBuildFile; fileRef = 602C561C2081C984003AD452 /* rounded.svg */; };
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 */; };
@ -453,6 +454,7 @@
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>"; };
602C561C2081C984003AD452 /* rounded.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = rounded.svg; 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>"; };
@ -563,6 +565,7 @@
57CAB1241D7832E000FD8E47 /* svg */ = {
isa = PBXGroup;
children = (
602C561C2081C984003AD452 /* rounded.svg */,
5BAEA9C8206CEAA20049AAAE /* viewBox.svg */,
C46E83541F94B20E00208037 /* transform.svg */,
C43B064C1F9738EF00787A35 /* clip.svg */,
@ -1092,6 +1095,7 @@
5BAEA9CB206CEB7D0049AAAE /* viewBox.reference in Resources */,
57CAB1311D7832E000FD8E47 /* line.svg in Resources */,
57B7A4DF1EE70D17009D78D7 /* logo.png in Resources */,
602C561D2081C984003AD452 /* rounded.svg in Resources */,
57CAB1321D7832E000FD8E47 /* polygon.svg in Resources */,
57CAB12F1D7832E000FD8E47 /* ellipse.svg in Resources */,
57CAB1341D7832E000FD8E47 /* rect.svg in Resources */,

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="30px" height="30px" viewBox="0 0 30 30" enable-background="new 0 0 30 30" xml:space="preserve">
<polyline fill="none" stroke="#000000" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" points="
24.27,6.35 12.54,23.65 5.73,16.59 "/>
</svg>

After

Width:  |  Height:  |  Size: 551 B

View File

@ -6,7 +6,14 @@ open class Stop {
open let color: Color
public init(offset: Double = 0, color: Color) {
self.offset = offset
self.color = color
if offset < 0 {
self.offset = 0
} else if offset > 1 {
self.offset = 1
} else {
self.offset = offset
}
}
}

View File

@ -7,12 +7,14 @@ open class Stroke {
open let cap: LineCap
open let join: LineJoin
open let dashes: [Double]
open let offset: Double
public init(fill: Fill = Color.black, width: Double = 1, cap: LineCap = .butt, join: LineJoin = .miter, dashes: [Double] = []) {
public init(fill: Fill = Color.black, width: Double = 1, cap: LineCap = .butt, join: LineJoin = .miter, dashes: [Double] = [], offset: Double = 0.0) {
self.fill = fill
self.width = width
self.cap = cap
self.join = join
self.dashes = dashes
self.offset = offset
}
}

View File

@ -167,14 +167,9 @@ class ShapeRenderer: NodeRenderer {
ctx!.setLineWidth(CGFloat(stroke.width))
ctx!.setLineJoin(RenderUtils.mapLineJoin(stroke.join))
ctx!.setLineCap(RenderUtils.mapLineCap(stroke.cap))
let dashes = stroke.dashes
if !dashes.isEmpty {
var floatDashes = [CGFloat]()
dashes.forEach { dash in
floatDashes.append(CGFloat(dash))
}
ctx?.setLineDash(phase: 0.0, lengths: floatDashes)
if !stroke.dashes.isEmpty {
ctx?.setLineDash(phase: CGFloat(stroke.offset),
lengths: stroke.dashes.map{ CGFloat($0) })
}
}

View File

@ -15,8 +15,8 @@ open class SVGParser {
var svgSize: Size?
var viewBox: Rect?
var scalingMode: AspectRatio?
var xAligningMode: Align?
var yAligningMode: Align?
var xAligningMode: Align = .mid
var yAligningMode: Align = .mid
}
/// Parse an SVG file identified by the specified bundle, name and file extension.
@ -41,11 +41,11 @@ open class SVGParser {
return SVGParser(text).parse()
}
let availableStyleAttributes = ["stroke", "stroke-width", "stroke-opacity", "stroke-dasharray", "stroke-linecap", "stroke-linejoin",
let availableStyleAttributes = ["stroke", "stroke-width", "stroke-opacity", "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin",
"fill", "text-anchor", "clip-path", "fill-opacity",
"stop-color", "stop-opacity",
"font-family", "font-size", "font-weight",
"opacity", "visibility"]
"font-family", "font-size",
"font-weight", "opacity", "color", "visibility"]
fileprivate let xmlString: String
fileprivate let initialPosition: Transform
@ -139,8 +139,8 @@ open class SVGParser {
if scalingMode === AspectRatio.slice {
// setup new clipping to slice extra bits
let newSize = AspectRatio.meet.fit(size: svgSize, into: viewBox)
let newX = viewBox.x + viewBox.w / 2 - newSize.w / 2
let newY = viewBox.y + viewBox.h / 2 - newSize.h / 2
let newX = viewBox.x + params.xAligningMode.align(outer: viewBox.w, inner: newSize.w)
let newY = viewBox.y + params.yAligningMode.align(outer: viewBox.h, inner: newSize.h)
node.clip = Rect(x: newX, y: newY, w: newSize.w, h: newSize.h)
}
@ -228,7 +228,7 @@ open class SVGParser {
}
}
fileprivate func parseDefinitions(_ defs: XMLIndexer) {
fileprivate func parseDefinitions(_ defs: XMLIndexer, groupStyle: [String: String] = [:]) {
for child in defs.children {
guard let id = child.element?.allAttributes["id"]?.text else {
continue
@ -266,41 +266,42 @@ open class SVGParser {
return .none
}
let position = getPosition(element)
switch element.name {
case "path":
if let path = parsePath(node) {
return Shape(form: path, fill: getFillColor(styleAttributes), stroke: getStroke(styleAttributes), place: position, opacity: getOpacity(styleAttributes), clip: getClipPath(styleAttributes), tag: getTag(element))
return Shape(form: path, fill: getFillColor(styleAttributes, groupStyle: styleAttributes), stroke: getStroke(styleAttributes, groupStyle: styleAttributes), place: position, opacity: getOpacity(styleAttributes), clip: getClipPath(styleAttributes), tag: getTag(element))
}
case "line":
if let line = parseLine(node) {
return Shape(form: line, fill: getFillColor(styleAttributes), stroke: getStroke(styleAttributes), place: position, opacity: getOpacity(styleAttributes), clip: getClipPath(styleAttributes), tag: getTag(element))
return Shape(form: line, fill: getFillColor(styleAttributes, groupStyle: styleAttributes), stroke: getStroke(styleAttributes, groupStyle: styleAttributes), place: position, opacity: getOpacity(styleAttributes), clip: getClipPath(styleAttributes), tag: getTag(element))
}
case "rect":
if let rect = parseRect(node) {
return Shape(form: rect, fill: getFillColor(styleAttributes), stroke: getStroke(styleAttributes), place: position, opacity: getOpacity(styleAttributes), clip: getClipPath(styleAttributes), tag: getTag(element))
return Shape(form: rect, fill: getFillColor(styleAttributes, groupStyle: styleAttributes), stroke: getStroke(styleAttributes, groupStyle: styleAttributes), place: position, opacity: getOpacity(styleAttributes), clip: getClipPath(styleAttributes), tag: getTag(element))
}
case "circle":
if let circle = parseCircle(node) {
return Shape(form: circle, fill: getFillColor(styleAttributes), stroke: getStroke(styleAttributes), place: position, opacity: getOpacity(styleAttributes), clip: getClipPath(styleAttributes), tag: getTag(element))
return Shape(form: circle, fill: getFillColor(styleAttributes, groupStyle: styleAttributes), stroke: getStroke(styleAttributes, groupStyle: styleAttributes), place: position, opacity: getOpacity(styleAttributes), clip: getClipPath(styleAttributes), tag: getTag(element))
}
case "ellipse":
if let ellipse = parseEllipse(node) {
return Shape(form: ellipse, fill: getFillColor(styleAttributes), stroke: getStroke(styleAttributes), place: position, opacity: getOpacity(styleAttributes), clip: getClipPath(styleAttributes), tag: getTag(element))
return Shape(form: ellipse, fill: getFillColor(styleAttributes, groupStyle: styleAttributes), stroke: getStroke(styleAttributes, groupStyle: styleAttributes), place: position, opacity: getOpacity(styleAttributes), clip: getClipPath(styleAttributes), tag: getTag(element))
}
case "polygon":
if let polygon = parsePolygon(node) {
return Shape(form: polygon, fill: getFillColor(styleAttributes), stroke: getStroke(styleAttributes), place: position, opacity: getOpacity(styleAttributes), clip: getClipPath(styleAttributes), tag: getTag(element))
return Shape(form: polygon, fill: getFillColor(styleAttributes, groupStyle: styleAttributes), stroke: getStroke(styleAttributes, groupStyle: styleAttributes), place: position, opacity: getOpacity(styleAttributes), clip: getClipPath(styleAttributes), tag: getTag(element))
}
case "polyline":
if let polyline = parsePolyline(node) {
return Shape(form: polyline, fill: getFillColor(styleAttributes), stroke: getStroke(styleAttributes), place: position, opacity: getOpacity(styleAttributes), clip: getClipPath(styleAttributes), tag: getTag(element))
return Shape(form: polyline, fill: getFillColor(styleAttributes, groupStyle: styleAttributes), stroke: getStroke(styleAttributes, groupStyle: styleAttributes), place: position, opacity: getOpacity(styleAttributes), clip: getClipPath(styleAttributes), tag: getTag(element))
}
case "image":
return parseImage(node, opacity: getOpacity(styleAttributes), pos: position, clip: getClipPath(styleAttributes))
case "text":
return parseText(node, textAnchor: getTextAnchor(styleAttributes), fill: getFillColor(styleAttributes),
stroke: getStroke(styleAttributes), opacity: getOpacity(styleAttributes), fontName: getFontName(styleAttributes), fontSize: getFontSize(styleAttributes), fontWeight: getFontWeight(styleAttributes), pos: position)
return parseText(node, textAnchor: getTextAnchor(styleAttributes), fill: getFillColor(styleAttributes, groupStyle: styleAttributes),
stroke: getStroke(styleAttributes, groupStyle: styleAttributes), opacity: getOpacity(styleAttributes), fontName: getFontName(styleAttributes), fontSize: getFontSize(styleAttributes), fontWeight: getFontWeight(styleAttributes), pos: position)
case "use":
return parseUse(node, groupStyle: styleAttributes, place: position)
case "mask":
@ -317,11 +318,12 @@ open class SVGParser {
guard let element = fill.element else {
return .none
}
let style = getStyleAttributes([:], element: element)
switch element.name {
case "linearGradient":
return parseLinearGradient(fill)
return parseLinearGradient(fill, groupStyle: style)
case "radialGradient":
return parseRadialGradient(fill)
return parseRadialGradient(fill, groupStyle: style)
default:
return .none
}
@ -385,6 +387,8 @@ open class SVGParser {
guard let matcher = SVGParserRegexHelper.getTransformAttributeMatcher() else {
return transform
}
let attributes = attributes.replacingOccurrences(of: "\n", with: "")
var finalTransform = transform
let fullRange = NSRange(location: 0, length: attributes.count)
@ -522,7 +526,7 @@ open class SVGParser {
}
self.availableStyleAttributes.forEach { availableAttribute in
if let styleAttribute = element.allAttributes[availableAttribute]?.text {
if let styleAttribute = element.allAttributes[availableAttribute]?.text, styleAttribute != "inherit" {
styleAttributes.updateValue(styleAttribute, forKey: availableAttribute)
}
}
@ -531,6 +535,7 @@ open class SVGParser {
}
fileprivate func createColor(_ hexString: String, opacity: Double = 1) -> Color {
let opacity = min(max(opacity, 0), 1)
var cleanedHexString = hexString
if hexString.hasPrefix("#") {
cleanedHexString = hexString.replacingOccurrences(of: "#", with: "")
@ -549,13 +554,16 @@ open class SVGParser {
return Color.rgba(r: Int(red), g: Int(green), b: Int(blue), a: opacity)
}
fileprivate func getFillColor(_ styleParts: [String: String]) -> Fill? {
guard let fillColor = styleParts["fill"] else {
fileprivate func getFillColor(_ styleParts: [String: String], groupStyle: [String: String] = [:]) -> Fill? {
guard var fillColor = styleParts["fill"] else {
return Color.black
}
if fillColor == "none" || fillColor == "transparent" {
return .none
}
if fillColor == "currentColor", let currentColor = groupStyle["color"] {
fillColor = currentColor
}
var opacity: Double = 1
var hasFillOpacity = false
if let fillOpacity = styleParts["fill-opacity"] {
@ -581,13 +589,16 @@ open class SVGParser {
}
}
fileprivate func getStroke(_ styleParts: [String: String]) -> Stroke? {
guard let strokeColor = styleParts["stroke"] else {
fileprivate func getStroke(_ styleParts: [String: String], groupStyle: [String: String] = [:]) -> Stroke? {
guard var strokeColor = styleParts["stroke"] else {
return .none
}
if strokeColor == "none" {
return .none
}
if strokeColor == "currentColor", let currentColor = groupStyle["color"] {
strokeColor = currentColor
}
var opacity: Double = 1
if let strokeOpacity = styleParts["stroke-opacity"] {
opacity = Double(strokeOpacity.replacingOccurrences(of: " ", with: "")) ?? 1
@ -614,7 +625,8 @@ open class SVGParser {
width: getStrokeWidth(styleParts),
cap: getStrokeCap(styleParts),
join: getStrokeJoin(styleParts),
dashes: getStrokeDashes(styleParts))
dashes: getStrokeDashes(styleParts),
offset: getStrokeOffset(styleParts))
}
return .none
@ -625,8 +637,8 @@ open class SVGParser {
let characterSet = NSCharacterSet.decimalDigits.union(NSCharacterSet.punctuationCharacters).inverted
let digitsArray = strokeWidth.components(separatedBy: characterSet)
let digits = digitsArray.joined()
if let value = NumberFormatter().number(from: digits) {
return value.doubleValue
if let value = Double(digits) {
return value
}
}
return 1
@ -636,6 +648,8 @@ open class SVGParser {
var cap = LineCap.butt
if let strokeCap = styleParts["stroke-linecap"] {
switch strokeCap {
case "round":
cap = .round
case "butt":
cap = .butt
case "square":
@ -651,6 +665,8 @@ open class SVGParser {
var join = LineJoin.miter
if let strokeJoin = styleParts["stroke-linejoin"] {
switch strokeJoin {
case "round":
join = .round
case "miter":
join = .miter
case "bevel":
@ -670,13 +686,20 @@ open class SVGParser {
characterSet.insert(",")
let separatedValues = strokeDashes.components(separatedBy: characterSet)
separatedValues.forEach { value in
if let doubleValue = Double(value) {
if let doubleValue = doubleFromString(value) {
dashes.append(doubleValue)
}
}
}
return dashes
}
fileprivate func getStrokeOffset(_ styleParts: [String: String]) -> Double {
if let strokeOffset = styleParts["stroke-dashoffset"], let offset = Double(strokeOffset) { // TODO use doubleFromString once it's merged
return offset
}
return 0
}
fileprivate func getTag(_ element: SWXMLHash.XMLElement) -> [String] {
let id = element.allAttributes["id"]?.text
@ -1020,7 +1043,7 @@ open class SVGParser {
return maskShape
}
fileprivate func parseLinearGradient(_ gradient: XMLIndexer) -> Fill? {
fileprivate func parseLinearGradient(_ gradient: XMLIndexer, groupStyle: [String: String] = [:]) -> Fill? {
guard let element = gradient.element else {
return .none
}
@ -1036,7 +1059,7 @@ open class SVGParser {
if gradient.children.isEmpty {
stopsArray = parentGradient?.stops
} else {
stopsArray = parseStops(gradient.children)
stopsArray = parseStops(gradient.children, groupStyle: groupStyle)
}
guard let stops = stopsArray else {
@ -1081,7 +1104,7 @@ open class SVGParser {
return LinearGradient(x1: x1, y1: y1, x2: x2, y2: y2, userSpace: userSpace, stops: stops)
}
fileprivate func parseRadialGradient(_ gradient: XMLIndexer) -> Fill? {
fileprivate func parseRadialGradient(_ gradient: XMLIndexer, groupStyle: [String: String] = [:]) -> Fill? {
guard let element = gradient.element else {
return .none
}
@ -1097,7 +1120,7 @@ open class SVGParser {
if gradient.children.isEmpty {
stopsArray = parentGradient?.stops
} else {
stopsArray = parseStops(gradient.children)
stopsArray = parseStops(gradient.children, groupStyle: groupStyle)
}
guard let stops = stopsArray else {
@ -1143,36 +1166,34 @@ open class SVGParser {
return RadialGradient(cx: cx, cy: cy, fx: fx, fy: fy, r: r, userSpace: userSpace, stops: stops)
}
fileprivate func parseStops(_ stops: [XMLIndexer]) -> [Stop] {
fileprivate func parseStops(_ stops: [XMLIndexer], groupStyle: [String: String] = [:]) -> [Stop] {
var result = [Stop]()
stops.forEach { stopXML in
if let stop = parseStop(stopXML) {
if let stop = parseStop(stopXML, groupStyle: groupStyle) {
result.append(stop)
}
}
return result
}
fileprivate func parseStop(_ stop: XMLIndexer) -> Stop? {
fileprivate func parseStop(_ stop: XMLIndexer, groupStyle: [String: String] = [:]) -> Stop? {
guard let element = stop.element else {
return .none
}
guard var offset = getDoubleValueFromPercentage(element, attribute: "offset") else {
guard let offset = getDoubleValueFromPercentage(element, attribute: "offset") else {
return .none
}
if offset < 0 {
offset = 0
} else if offset > 1 {
offset = 1
}
var opacity: Double = 1
if let stopOpacity = getStyleAttributes([:], element: element)["stop-opacity"], let doubleValue = Double(stopOpacity) {
opacity = doubleValue
}
var color = Color.black
if let stopColor = getStyleAttributes([:], element: element)["stop-color"] {
if var stopColor = getStyleAttributes([:], element: element)["stop-color"] {
if stopColor == "currentColor", let currentColor = groupStyle["color"] {
stopColor = currentColor
}
if let defaultColor = SVGConstants.colorList[stopColor] {
color = Color(val: defaultColor).with(a: opacity)
} else {
@ -1192,10 +1213,31 @@ open class SVGParser {
}
fileprivate func getDoubleValue(_ element: SWXMLHash.XMLElement, attribute: String) -> Double? {
guard let attributeValue = element.allAttributes[attribute]?.text, let doubleValue = Double(attributeValue) else {
guard let attributeValue = element.allAttributes[attribute]?.text else {
return .none
}
return doubleValue
return doubleFromString(attributeValue)
}
fileprivate func doubleFromString(_ string: String) -> Double? {
if let doubleValue = Double(string) {
return doubleValue
}
guard let matcher = SVGParserRegexHelper.getUnitsIdenitifierMatcher() else { return .none }
let fullRange = NSRange(location: 0, length: string.count)
if let match = matcher.firstMatch(in: string, options: .reportCompletion, range: fullRange) {
let unitString = (string as NSString).substring(with: match.range(at: 1))
let numberString = String(string.dropLast(unitString.count))
switch unitString {
case "px" :
return Double(numberString)
default:
print("SVG parsing error. Unit \(unitString) not supported")
return Double(numberString)
}
}
return .none
}
fileprivate func getDoubleValueFromPercentage(_ element: SWXMLHash.XMLElement, attribute: String) -> Double? {

View File

@ -6,11 +6,13 @@ class SVGParserRegexHelper {
fileprivate static let transformPattern = "\\-?\\d+\\.?\\d*e?\\-?\\d*"
fileprivate static let textElementPattern = "<text.*?>((?s:.*))<\\/text>"
fileprivate static let maskIdenitifierPattern = "url\\(#((?s:.*))\\)"
fileprivate static let unitsIdenitifierPattern = "([a-zA-Z]+)$"
fileprivate static var transformMatcher: NSRegularExpression?
fileprivate static var transformAttributeMatcher: NSRegularExpression?
fileprivate static var textElementMatcher: NSRegularExpression?
fileprivate static var maskIdenitifierMatcher: NSRegularExpression?
fileprivate static var unitsMatcher: NSRegularExpression?
class func getTransformAttributeMatcher() -> NSRegularExpression? {
if self.transformAttributeMatcher == nil {
@ -55,5 +57,16 @@ class SVGParserRegexHelper {
}
return self.maskIdenitifierMatcher
}
class func getUnitsIdenitifierMatcher() -> NSRegularExpression? {
if unitsMatcher == nil {
do {
unitsMatcher = try NSRegularExpression(pattern: unitsIdenitifierPattern, options: .caseInsensitive)
} catch {
}
}
return unitsMatcher
}
}

View File

@ -247,6 +247,15 @@ open class SVGSerializer {
result += " stroke-linejoin=\"\(strokeJoin)\""
}
}
if let strokeDashes = stroke?.dashes, strokeDashes.count > 0 {
let dashes = strokeDashes.map{ String($0) }.joined(separator: ",")
result += " stroke-dasharray=\"\(dashes)\""
}
if let strokeOffset = stroke?.offset {
if strokeOffset != 0 {
result += " stroke-dashoffset=\"\(strokeOffset)\""
}
}
return result
}

View File

@ -11,10 +11,10 @@ open class SvgContentLayout: ContentLayout {
public let xAligningMode: Align
public let yAligningMode: Align
public init(scalingMode: AspectRatio, xAligningMode: Align? = Align.min, yAligningMode: Align? = Align.min) {
public init(scalingMode: AspectRatio, xAligningMode: Align = Align.min, yAligningMode: Align = Align.min) {
self.scalingMode = scalingMode
self.xAligningMode = xAligningMode ?? Align.min
self.yAligningMode = yAligningMode ?? Align.min
self.xAligningMode = xAligningMode
self.yAligningMode = yAligningMode
}
public static var standard: ContentLayout {