From f2ea825c70fdd43cd454dbcb25d5da873b49b982 Mon Sep 17 00:00:00 2001 From: GoodNotesCI Date: Tue, 10 Jun 2025 16:59:03 +0800 Subject: [PATCH 1/5] Parse width and height --- Source/Parser/SVG/SVGParserPrimitives.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Source/Parser/SVG/SVGParserPrimitives.swift b/Source/Parser/SVG/SVGParserPrimitives.swift index 7570527..01f37a1 100644 --- a/Source/Parser/SVG/SVGParserPrimitives.swift +++ b/Source/Parser/SVG/SVGParserPrimitives.swift @@ -200,6 +200,11 @@ public class SVGHelper: NSObject { let height = SVGLengthParser.yAxis.double(string: nums[3], context: context) { return CGRect(x: x, y: y, width: width, height: height) } + } else if let widthAttr = attributes[ignoreCase: "width"], + let heightAttr = attributes[ignoreCase: "height"], + let width = SVGLengthParser.xAxis.double(string: widthAttr, context: context), + let height = SVGLengthParser.yAxis.double(string: heightAttr, context: context) { + return CGRect(x: 0.0, y: 0.0, width: width, height: height) } return nil } From d6dac9322288055c009b115afea90604027f369c Mon Sep 17 00:00:00 2001 From: GoodNotesCI Date: Wed, 11 Jun 2025 15:10:44 +0800 Subject: [PATCH 2/5] ViewPort and ViewBox are two concepts --- Source/Parser/SVG/SVGContext.swift | 4 +++- Source/Parser/SVG/SVGParserPrimitives.swift | 15 ++++++++++----- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/Source/Parser/SVG/SVGContext.swift b/Source/Parser/SVG/SVGContext.swift index 5506fcc..6438e7f 100644 --- a/Source/Parser/SVG/SVGContext.swift +++ b/Source/Parser/SVG/SVGContext.swift @@ -115,7 +115,9 @@ class SVGNodeContext: SVGContext { private static func replaceRoot(element: XMLElement, context: SVGContext) -> SVGRootContext { if element.name == "svg" { - if let viewBox = SVGHelper.parseViewBox(element.attributes, context: context) { + let viewPort = SVGHelper.parseViewPort(element.attributes, context: context) + // Each SVG viewport generates a viewport coordinate system and a user coordinate system, initially identical. Providing a ‘viewBox’ on a viewport's element transforms the user coordinate system relative to the viewport coordinate system + if let viewBox = SVGHelper.parseViewBox(element.attributes, context: context) ?? viewPort { let screen = SVGScreen(ppi: context.screen.ppi, width: viewBox.width, height: viewBox.height) return SVGRootContext(logger: context.logger, linker: context.linker, screen: screen, index: context.index, defaultFontSize: context.defaultFontSize) } diff --git a/Source/Parser/SVG/SVGParserPrimitives.swift b/Source/Parser/SVG/SVGParserPrimitives.swift index 01f37a1..e1376b5 100644 --- a/Source/Parser/SVG/SVGParserPrimitives.swift +++ b/Source/Parser/SVG/SVGParserPrimitives.swift @@ -189,6 +189,16 @@ public class SVGHelper: NSObject { return scale.concatenating(move) } + static func parseViewPort(_ attributes: [String: String], context: SVGContext) -> CGRect? { + if let widthAttr = attributes[ignoreCase: "width"], + let heightAttr = attributes[ignoreCase: "height"], + let width = SVGLengthParser.xAxis.double(string: widthAttr, context: context), + let height = SVGLengthParser.yAxis.double(string: heightAttr, context: context) { + return CGRect(x: 0.0, y: 0.0, width: width, height: height) + } + return nil + } + static func parseViewBox(_ attributes: [String: String], context: SVGContext) -> CGRect? { // TODO: temporary solution, all attributes should be case insensitive if let string = attributes[ignoreCase: "viewBox"] { @@ -200,11 +210,6 @@ public class SVGHelper: NSObject { let height = SVGLengthParser.yAxis.double(string: nums[3], context: context) { return CGRect(x: x, y: y, width: width, height: height) } - } else if let widthAttr = attributes[ignoreCase: "width"], - let heightAttr = attributes[ignoreCase: "height"], - let width = SVGLengthParser.xAxis.double(string: widthAttr, context: context), - let height = SVGLengthParser.yAxis.double(string: heightAttr, context: context) { - return CGRect(x: 0.0, y: 0.0, width: width, height: height) } return nil } From 17a4ce1180c0f488818462053878109f10d5ca28 Mon Sep 17 00:00:00 2001 From: GoodNotesCI Date: Wed, 11 Jun 2025 23:24:23 +0800 Subject: [PATCH 3/5] Add test case --- GenerateReferencesCLI/cli.swift | 22 ++++++++++++++----- Makefile | 3 ++- .../Parser/SVG/Elements/SVGShapeParser.swift | 1 + Tests/SVGViewTests/SVGCustomTests.swift | 16 ++++++++++++++ .../w3c/Custom/refs/viewport-01.ref | 15 +++++++++++++ .../w3c/Custom/svg/viewport-01.svg | 7 ++++++ 6 files changed, 58 insertions(+), 6 deletions(-) create mode 100644 Tests/SVGViewTests/SVGCustomTests.swift create mode 100644 Tests/SVGViewTests/w3c/Custom/refs/viewport-01.ref create mode 100644 Tests/SVGViewTests/w3c/Custom/svg/viewport-01.svg diff --git a/GenerateReferencesCLI/cli.swift b/GenerateReferencesCLI/cli.swift index c89f5c4..0eee756 100644 --- a/GenerateReferencesCLI/cli.swift +++ b/GenerateReferencesCLI/cli.swift @@ -6,7 +6,7 @@ import ArgumentParser #if os(macOS) @main struct cli: ParsableCommand { - @Argument(help: "Path to a folder that contains 1.1F2/ and 1.2T/") + @Argument(help: "Path to a folder that contains 1.1F2/, 1.2T/ and Custom/") var input: String static let v11Refs: [String] = [ @@ -144,7 +144,11 @@ struct cli: ParsableCommand { "struct-frag-01-t", "struct-use-03-t", ] - + + static let customRefs: [String] = [ + "viewport-01", + ] + mutating func run() throws { let inputURL = URL(fileURLWithPath: input) @@ -154,9 +158,10 @@ struct cli: ParsableCommand { let v11FolderURL = inputURL.appendingPathComponent("1.1F2") let v12FolderURL = inputURL.appendingPathComponent("1.2T") - - guard FileManager.default.fileExists(atPath: v11FolderURL.path) || FileManager.default.fileExists(atPath: v12FolderURL.path) else { - throw ValidationError("1.1F2/ or 1.2T/ folder does not exist in '\(input)'") + let customFolderURL = inputURL.appendingPathComponent("Custom") + + guard FileManager.default.fileExists(atPath: v11FolderURL.path) || FileManager.default.fileExists(atPath: v12FolderURL.path) || FileManager.default.fileExists(atPath: customFolderURL.path) else { + throw ValidationError("1.1F2/, 1.2T/ or Custom/ folder does not exist in '\(input)'") } for ref in Self.v11Refs { @@ -172,6 +177,13 @@ struct cli: ParsableCommand { let refURL = v12FolderURL.appending(path: "refs/\(ref).ref") try svgContent.write(to: refURL, atomically: true, encoding: .utf8) } + + for ref in Self.customRefs { + let svgURL = customFolderURL.appending(path: "svg/\(ref).svg") + let svgContent = try serialize(inputURL: svgURL) + let refURL = customFolderURL.appending(path: "refs/\(ref).ref") + try svgContent.write(to: refURL, atomically: true, encoding: .utf8) + } } private func serialize(inputURL: URL) throws -> String { diff --git a/Makefile b/Makefile index 4ba3a72..8c6b3ff 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,8 @@ generate-test-cases: # Generate test cases from w3c reference files printf "}" >> ../SVGViewTests/$$class.swift; \ }; \ generateTest "1.1F2" "SVG11Tests"; \ - generateTest "1.2T" "SVG12Tests" + generateTest "1.2T" "SVG12Tests"; \ + generateTest "Custom" "SVGCustomTests" update-references-snapshots: # Update .ref from .svg files swift run GenerateReferencesCLI Tests/SVGViewTests/w3c/ diff --git a/Source/Parser/SVG/Elements/SVGShapeParser.swift b/Source/Parser/SVG/Elements/SVGShapeParser.swift index 0e2e6eb..a20155b 100644 --- a/Source/Parser/SVG/Elements/SVGShapeParser.swift +++ b/Source/Parser/SVG/Elements/SVGShapeParser.swift @@ -24,6 +24,7 @@ class SVGShapeParser: SVGBaseElementParser { class SVGRectParser: SVGShapeParser { override func parseLocus(context: SVGNodeContext) -> SVGShape? { + // SVGHelper.parseDimension(attributes, "markerWidth") ?? SVGLength(percent: 100) let rx = context.optional(.rx) let ry = context.optional(.ry) return SVGRect( diff --git a/Tests/SVGViewTests/SVGCustomTests.swift b/Tests/SVGViewTests/SVGCustomTests.swift new file mode 100644 index 0000000..eaf9b70 --- /dev/null +++ b/Tests/SVGViewTests/SVGCustomTests.swift @@ -0,0 +1,16 @@ +// Generated by make generate-test-cases + +import XCTest +@testable import SVGView + +class SVGCustomTests: BaseTestCase { + + override var dir: String { + return "Custom" + } + + func testViewport01() { + compareToReference("viewport-01") + } + +} \ No newline at end of file diff --git a/Tests/SVGViewTests/w3c/Custom/refs/viewport-01.ref b/Tests/SVGViewTests/w3c/Custom/refs/viewport-01.ref new file mode 100644 index 0000000..62c6760 --- /dev/null +++ b/Tests/SVGViewTests/w3c/Custom/refs/viewport-01.ref @@ -0,0 +1,15 @@ +SVGViewport { + width: "780", + height: "950", + scaling: "none", + contents: [ + SVGRect { width: 780, height: 950, fill: "#F4F4F4" }, + SVGText { + text: "1-to-1 Meeting Template", + font: { size: 24, weight: "bold" }, + textAnchor: "middle", + fill: "black", + transform: [1, 0, 0, 1, 390, 50] + } + ] +} diff --git a/Tests/SVGViewTests/w3c/Custom/svg/viewport-01.svg b/Tests/SVGViewTests/w3c/Custom/svg/viewport-01.svg new file mode 100644 index 0000000..075fbf1 --- /dev/null +++ b/Tests/SVGViewTests/w3c/Custom/svg/viewport-01.svg @@ -0,0 +1,7 @@ + + + + + + 1-to-1 Meeting Template + From 03a5aabca993903a0f2773659fcc9c35d083c667 Mon Sep 17 00:00:00 2001 From: GoodNotesCI Date: Wed, 11 Jun 2025 23:27:37 +0800 Subject: [PATCH 4/5] Add test cases that use 100% in root node --- GenerateReferencesCLI/cli.swift | 1 + Tests/SVGViewTests/w3c/Custom/refs/viewport-02.ref | 14 ++++++++++++++ Tests/SVGViewTests/w3c/Custom/svg/viewport-02.svg | 7 +++++++ 3 files changed, 22 insertions(+) create mode 100644 Tests/SVGViewTests/w3c/Custom/refs/viewport-02.ref create mode 100644 Tests/SVGViewTests/w3c/Custom/svg/viewport-02.svg diff --git a/GenerateReferencesCLI/cli.swift b/GenerateReferencesCLI/cli.swift index 0eee756..5294b4e 100644 --- a/GenerateReferencesCLI/cli.swift +++ b/GenerateReferencesCLI/cli.swift @@ -147,6 +147,7 @@ struct cli: ParsableCommand { static let customRefs: [String] = [ "viewport-01", + "viewport-02", ] mutating func run() throws { diff --git a/Tests/SVGViewTests/w3c/Custom/refs/viewport-02.ref b/Tests/SVGViewTests/w3c/Custom/refs/viewport-02.ref new file mode 100644 index 0000000..0c426d6 --- /dev/null +++ b/Tests/SVGViewTests/w3c/Custom/refs/viewport-02.ref @@ -0,0 +1,14 @@ +SVGViewport { + width: "180%", + scaling: "none", + contents: [ + SVGRect { width: 180, height: 100, fill: "#F4F4F4" }, + SVGText { + text: "1-to-1 Meeting Template", + font: { size: 24, weight: "bold" }, + textAnchor: "middle", + fill: "black", + transform: [1, 0, 0, 1, 390, 50] + } + ] +} diff --git a/Tests/SVGViewTests/w3c/Custom/svg/viewport-02.svg b/Tests/SVGViewTests/w3c/Custom/svg/viewport-02.svg new file mode 100644 index 0000000..edce9b7 --- /dev/null +++ b/Tests/SVGViewTests/w3c/Custom/svg/viewport-02.svg @@ -0,0 +1,7 @@ + + + + + + 1-to-1 Meeting Template + From eef605734a5288f5cd1d8a4a54f3c1b51b9d70ed Mon Sep 17 00:00:00 2001 From: pacowong Date: Fri, 13 Jun 2025 00:27:18 +0800 Subject: [PATCH 5/5] Update SVGShapeParser.swift --- Source/Parser/SVG/Elements/SVGShapeParser.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/Parser/SVG/Elements/SVGShapeParser.swift b/Source/Parser/SVG/Elements/SVGShapeParser.swift index a20155b..0e2e6eb 100644 --- a/Source/Parser/SVG/Elements/SVGShapeParser.swift +++ b/Source/Parser/SVG/Elements/SVGShapeParser.swift @@ -24,7 +24,6 @@ class SVGShapeParser: SVGBaseElementParser { class SVGRectParser: SVGShapeParser { override func parseLocus(context: SVGNodeContext) -> SVGShape? { - // SVGHelper.parseDimension(attributes, "markerWidth") ?? SVGLength(percent: 100) let rx = context.optional(.rx) let ry = context.optional(.ry) return SVGRect(