Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
30 changes: 15 additions & 15 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,32 @@ name: Tests

on:
push:
branches: [ main ]
branches: [main]
pull_request:
branches: [ main ]
branches: [main]

jobs:
test-linux:
name: Test on Linux
runs-on: ubuntu-latest
container:
image: swift:5.9

steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Run tests
run: swift test --enable-test-discovery
- name: Checkout code
uses: actions/checkout@v4

- name: Run tests
run: swift test --filter CoreGraphicsPolyfillTests

test-macos:
name: Test on macOS
runs-on: macos-latest

steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Run tests
run: swift test --enable-test-discovery
- name: Checkout code
uses: actions/checkout@v4

- name: Run tests
run: swift test --enable-test-discovery

189 changes: 189 additions & 0 deletions GenerateReferencesCLI/cli.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
import Foundation
import ArgumentParser

@testable import SVGView

#if os(macOS)
@main
struct cli: ParsableCommand {
@Argument(help: "Path to a folder that contains 1.1F2/ and 1.2T/")
var input: String

static let v11Refs: [String] = [
"color-prop-01-b",
"color-prop-02-f",
"color-prop-03-t",
"color-prop-04-t",
"color-prop-05-t",
"coords-coord-01-t",
"coords-coord-02-t",
"coords-trans-01-b",
"coords-trans-02-t",
"coords-trans-03-t",
"coords-trans-04-t",
"coords-trans-05-t",
"coords-trans-06-t",
"coords-trans-07-t",
"coords-trans-08-t",
"coords-trans-09-t",
"coords-trans-10-f",
"coords-trans-11-f",
"coords-trans-12-f",
"coords-trans-13-f",
"coords-trans-14-f",
"coords-transformattr-01-f",
"coords-transformattr-02-f",
"coords-transformattr-03-f",
"coords-transformattr-04-f",
"coords-transformattr-05-f",
"coords-units-02-b",
"coords-units-03-b",
"masking-opacity-01-b",
"painting-control-02-f",
"painting-control-03-f",
"painting-marker-01-f",
"painting-fill-01-t",
"painting-fill-02-t",
"painting-fill-03-t",
"painting-fill-04-t",
"painting-fill-05-b",
"painting-stroke-01-t",
"painting-stroke-02-t",
"painting-stroke-03-t",
"painting-stroke-04-t",
"painting-stroke-05-t",
"painting-stroke-07-t",
"painting-stroke-08-t",
"painting-stroke-09-t",
"paths-data-01-t",
"paths-data-02-t",
"paths-data-03-f",
"paths-data-04-t",
"paths-data-05-t",
"paths-data-06-t",
"paths-data-07-t",
"paths-data-08-t",
"paths-data-09-t",
"paths-data-10-t",
"paths-data-12-t",
"paths-data-13-t",
"paths-data-14-t",
"paths-data-15-t",
"paths-data-16-t",
"paths-data-17-f",
"paths-data-18-f",
"paths-data-19-f",
"paths-data-20-f",
"pservers-grad-01-b",
"pservers-grad-02-b",
"pservers-grad-04-b",
"pservers-grad-05-b",
"pservers-grad-07-b",
"pservers-grad-09-b",
"render-elems-01-t",
"render-elems-02-t",
"render-elems-03-t",
"shapes-circle-01-t",
"shapes-circle-02-t",
"shapes-ellipse-01-t",
"shapes-ellipse-02-t",
"shapes-ellipse-03-f",
"shapes-grammar-01-f",
"shapes-intro-01-t",
"shapes-line-01-t",
"shapes-line-02-f",
"shapes-polygon-01-t",
"shapes-polygon-02-t",
"shapes-polygon-03-t",
"shapes-polyline-01-t",
"shapes-polyline-02-t",
"shapes-rect-02-t",
"shapes-rect-04-f",
"shapes-rect-05-f",
"shapes-rect-06-f",
"struct-defs-01-t",
"struct-frag-01-t",
"struct-frag-06-t",
"struct-group-01-t",
"struct-image-01-t",
"struct-image-04-t",
"struct-use-03-t",
"styling-class-01-f",
"styling-css-01-b",
"styling-pres-01-t",
"types-basic-01-f",
]

static let v12Refs: [String] = [
"coords-trans-01-t",
"coords-trans-02-t",
"coords-trans-03-t",
"coords-trans-04-t",
"coords-trans-05-t",
"coords-trans-06-t",
"coords-trans-07-t",
"coords-trans-08-t",
"coords-trans-09-t",
"paint-color-03-t",
"paint-color-201-t",
"paint-fill-04-t",
"paint-fill-06-t",
"paint-stroke-01-t",
"paths-data-01-t",
"paths-data-02-t",
"render-elems-01-t",
"render-elems-02-t",
"render-elems-03-t",
"shapes-circle-01-t",
"shapes-ellipse-01-t",
"shapes-line-01-t",
"shapes-polygon-01-t",
"shapes-polyline-01-t",
"shapes-rect-02-t",
"struct-defs-01-t",
"struct-frag-01-t",
"struct-use-03-t",
]

mutating func run() throws {
let inputURL = URL(fileURLWithPath: input)

guard FileManager.default.fileExists(atPath: input) else {
throw ValidationError("Input path '\(input)' does not exist")
}

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)'")
}

for ref in Self.v11Refs {
let svgURL = v11FolderURL.appending(path: "svg/\(ref).svg")
let svgContent = try serialize(inputURL: svgURL)
let refURL = v11FolderURL.appending(path: "refs/\(ref).ref")
try svgContent.write(to: refURL, atomically: true, encoding: .utf8)
}

for ref in Self.v12Refs {
let svgURL = v12FolderURL.appending(path: "svg/\(ref).svg")
let svgContent = try serialize(inputURL: svgURL)
let refURL = v12FolderURL.appending(path: "refs/\(ref).ref")
try svgContent.write(to: refURL, atomically: true, encoding: .utf8)
}
}

private func serialize(inputURL: URL) throws -> String {
guard FileManager.default.fileExists(atPath: input) else {
throw ValidationError("Input path '\(input)' does not exist")
}

guard let node = SVGParser.parse(contentsOf: inputURL) else {
throw ValidationError("Failed to parse SVG file")
}

return Serializer.serialize(node)
}
}
#endif
47 changes: 47 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Derived values (DO NOT TOUCH).
CURRENT_MAKEFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST)))
CURRENT_MAKEFILE_DIR := $(patsubst %/,%,$(dir $(CURRENT_MAKEFILE_PATH)))

# If no target is specified, display help
.DEFAULT_GOAL := help

help: # Display this help.
@-+echo "Run make with one of the following targets:"
@-+echo
@-+grep -Eh "^[a-z-]+:.*#" $(CURRENT_MAKEFILE_PATH) | sed -E 's/^(.*:)(.*#+)(.*)/ \1 @@@ \3 /' | column -t -s "@@@"

test: # Run tests
swift test

generate-test-cases: # Generate test cases from w3c reference files
@cd Tests/SVGViewTests && \
generateTest() { \
local dir=$$1; \
local class=$$2; \
printf "// Generated by make generate-test-cases\n\n" > ../SVGViewTests/$$class.swift; \
printf "import XCTest\n" >> ../SVGViewTests/$$class.swift; \
printf "@testable import SVGView\n\n" >> ../SVGViewTests/$$class.swift; \
printf "class $$class: BaseTestCase {\n\n" >> ../SVGViewTests/$$class.swift; \
printf " override var dir: String {\n" >> ../SVGViewTests/$$class.swift; \
printf " return \"$$dir\"\n" >> ../SVGViewTests/$$class.swift; \
printf " }\n\n" >> ../SVGViewTests/$$class.swift; \
find "w3c/$$dir/refs/" -type f -regex '.*\.ref$$' | sort | while read ref_file; do \
name=$$(basename "$${ref_file%.*}"); \
test_name=""; \
IFS='-' read -ra arr <<< "$$name"; \
for part in "$${arr[@]}"; do \
test_name+=$$(printf "%s" "$${part:0:1}" | tr '[:lower:]' '[:upper:]')$${part:1}; \
done; \
printf " func test$$test_name() {\n" >> ../SVGViewTests/$$class.swift; \
printf " compareToReference(\"$$name\")\n" >> ../SVGViewTests/$$class.swift; \
printf " }\n\n" >> ../SVGViewTests/$$class.swift; \
done; \
printf "}" >> ../SVGViewTests/$$class.swift; \
}; \
generateTest "1.1F2" "SVG11Tests"; \
generateTest "1.2T" "SVG12Tests"

update-references-snapshots: # Update .ref from .svg files
swift run GenerateReferencesCLI Tests/SVGViewTests/w3c/

.PHONY: help test generate-test-cases update-references-snapshots
14 changes: 14 additions & 0 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 26 additions & 4 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,48 @@ import PackageDescription

let package = Package(
name: "SVGView",
platforms: [
.macOS(.v14),
platforms: [
.macOS(.v14),
.iOS(.v14),
.watchOS(.v7)
],
products: [
.library(
name: "SVGView",
targets: ["SVGView"]
)
),
.executable(
name: "GenerateReferencesCLI",
targets: ["GenerateReferencesCLI"]
)
],
dependencies: [
.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.5.0"),
],
targets: [
.executableTarget(
name: "GenerateReferencesCLI",
dependencies: [
"SVGView",
.product(name: "ArgumentParser", package: "swift-argument-parser"),
],
path: "GenerateReferencesCLI"
),
.target(
name: "SVGView",
path: "Source"
),
.testTarget(
name: "CoreGraphicsPolyfillTests",
dependencies: ["SVGView"]
)
),
.testTarget(
name: "SVGViewTests",
dependencies: ["SVGView"],
resources: [
.copy("w3c")
]
),
],
swiftLanguageVersions: [.v5]
)
28 changes: 12 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,19 @@
<a href="https://exyte.com/"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/exyte/media/master/common/header-dark.png"><img src="https://raw.githubusercontent.com/exyte/media/master/common/header-light.png"></picture></a>

<a href="https://exyte.com/"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/exyte/media/master/common/our-site-dark.png" width="80" height="16"><img src="https://raw.githubusercontent.com/exyte/media/master/common/our-site-light.png" width="80" height="16"></picture></a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="https://twitter.com/exyteHQ"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/exyte/media/master/common/twitter-dark.png" width="74" height="16"><img src="https://raw.githubusercontent.com/exyte/media/master/common/twitter-light.png" width="74" height="16">
</picture></a> <a href="https://exyte.com/contacts"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/exyte/media/master/common/get-in-touch-dark.png" width="128" height="24" align="right"><img src="https://raw.githubusercontent.com/exyte/media/master/common/get-in-touch-light.png" width="128" height="24" align="right"></picture></a>

<p><h1 align="left">SVGView</h1></p>

<p><h4>SVG parser written in SwiftUI</h4></p>

[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fexyte%2FSVGView%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/exyte/SVGView)
[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fexyte%2FSVGView%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/exyte/SVGView)
[![SPM Compatible](https://img.shields.io/badge/SwiftPM-Compatible-brightgreen.svg)](https://swiftpackageindex.com/exyte/SVGView)
[![Cocoapods Compatible](https://img.shields.io/badge/cocoapods-Compatible-brightgreen.svg)](https://cocoapods.org/pods/SVGView)
[![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-brightgreen.svg?style=flat)](https://github.com/Carthage/Carthage)
[![License: MIT](https://img.shields.io/badge/License-MIT-black.svg)](https://opensource.org/licenses/MIT)
# SVGView

# Overview
This is a fork of [exyte/SVGView](https://github.com/exyte/SVGView) that tailored to Goodnotes's specific needs:
- Crossplatform compatible (at least the parser logic)
- Add support for some custom SVG tags

# Development

The goal of this project is to bring the full power of SVG to Apple platforms. Our framework can parse SVG files and represent their content in SwiftUI. It provides you with the ability to not only render SVG files, but also add interactivity to them, handle user input and use SwiftUI to put your art into motion.
This uses `make` heavily for relevant scripts. Run `make` without arguments to see the available commands.

## To add a new SVG test:
- Update `cli.swift` to include the svg file path
- `make generate-test-cases` to generate the unit test files
- `make update-references-snapshots` to update the .ref snapshots.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how to use


# Usage

Get started with `SVGView` in a few lines of code:
Expand Down
Loading
Loading