Skip to content

Commit

Permalink
Use android tools for Vector Drawable to SVG conversion (#47)
Browse files Browse the repository at this point in the history
* Parse vector drawable with android tools

* Fix some issues

* Fix style issues

* Remove distribution action parameter

* Remove duplicates in JAR

* Avoid treating a directory file as a file

* Bump the library

* Format code

* Reintroduce distribution

* Handle errors in conversion

* Format code

* Fix unreleased section in changelog

* Revert changelog header tweak

* Use standard header format

* Update workflows

* Standardize headers

* Add debug info

* Test

* Does this work

* Please work

* Deal with case sensitive file systems

* Format script
  • Loading branch information
jzbrooks authored Jul 27, 2024
1 parent 456a3dc commit c198ad1
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 79 deletions.
11 changes: 6 additions & 5 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,15 @@ jobs:
id: properties
shell: bash
run: |
cat changelog.md
PROPERTIES="$(./gradlew properties --console=plain -q)"
VERSION="$(echo "$PROPERTIES" | grep "^version:" | cut -f2- -d ' ')"
CHANGELOG="$(./gradlew getChangelog --unreleased --no-header --console=plain -q)"
CHANGELOG="${CHANGELOG//'%'/'%25'}"
CHANGELOG="${CHANGELOG//$'\n'/'%0A'}"
CHANGELOG="${CHANGELOG//$'\r'/'%0D'}"
echo "::set-output name=version::$VERSION"
echo "::set-output name=changelog::$CHANGELOG"
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "changelog<<EOF" >> $GITHUB_OUTPUT
echo "$CHANGELOG" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: build
run: ./gradlew build
Expand Down
23 changes: 20 additions & 3 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ jobs:
uses: actions/setup-java@v4
with:
java-version: 21
distribution: zulu

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v3
Expand All @@ -42,11 +43,27 @@ jobs:
SONATYPE_PROFILE_ID: ${{ secrets.SONATYPE_PROFILE_ID }}
run: ./gradlew -Psigning.keyId=${{ secrets.SIGNING_KEY_ID}} -Psigning.password=${{ secrets.SIGNING_KEY_PASSWORD }} -Psigning.secretKeyRingFile=${{ secrets.SIGNING_KEY_FILE_PATH }} --no-configuration-cache publishAndReleaseToMavenCentral

- name: Patch Changelog
# Set environment variables
- name: Export Properties
id: properties
shell: bash
run: |
./gradlew patchChangelog --release-note="`cat << EOM
CHANGELOG="$(cat << 'EOM' | sed -e 's/^[[:space:]]*$//g' -e '/./,$!d'
${{ github.event.release.body }}
EOM`"
EOM
)"
echo "changelog<<EOF" >> $GITHUB_OUTPUT
echo "$CHANGELOG" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
# Update Unreleased section with the current release note
- name: Patch Changelog
if: ${{ steps.properties.outputs.changelog != '' }}
env:
CHANGELOG: ${{ steps.properties.outputs.changelog }}
run: |
./gradlew patchChangelog --release-note="$CHANGELOG"
- name: Open PR for Changelog Update
env:
Expand Down
8 changes: 2 additions & 6 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.jetbrains.changelog.date
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension
import org.jlleitschuh.gradle.ktlint.KtlintExtension
Expand All @@ -14,6 +13,8 @@ plugins {

version = property("VERSION_NAME").toString()

changelog.path.set("changelog.md")

subprojects {
apply<KtlintPlugin>()
configure<KtlintExtension> {
Expand Down Expand Up @@ -84,8 +85,3 @@ subprojects {
)
}
}

changelog {
header.set(provider { "${version.get()}\n_${date()}_\n" })
groups.set(listOf("New", "Improvement", "Fix", "Upgrade"))
}
57 changes: 14 additions & 43 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,27 @@

## Unreleased

### New
- Use Android Studio conversion tools are used for SVG → vector drawable conversions
### Added

### Improvement
- More robust SVG → vector conversions by Android Studio tools

### Fix

### Upgrade

## 2.1.0

_09-14-2021_
## 2.1.0 - 09-14-2021

- New: Simplified optimization machinery with `ElementVisitor`
- Improvement: `MergePaths` no longer requires its own tree traversal
- Improvement: Attribute values omit leading zeros where possible
- Fix: theme referenced colors like `?attrs/dark` no longer cause crashes
- Upgrade: Build tools

## 2.0.2

_06.02.2021_
## 2.0.2 - 06.02.2021

- Fixed: Vector Drawable shorthand hex colors (like #FFF) are properly handled

## 2.0.1

_06.01.2021_
## 2.0.1 - 06.01.2021

- Fixed: Groups that wrap clip paths are no longer removed

## 2.0.0

_05.29.2021_
## 2.0.0 - 05.29.2021

- New, breaking(vgo-core): Structured graphic element attributes.
* This greatly simplifies the conversions between formats
Expand All @@ -48,59 +35,43 @@ _05.29.2021_
- Fixed: Test failures on Windows due to path handling
- Upgrade: Build with Kotlin 1.5.10

## 1.4.1

_02.16.2021_
## 1.4.1 - 02.16.2021

- Added: A new type for the `shrinkVectorArtwork` task

## 1.4.0

_02.15.2021_
## 1.4.0 - 02.15.2021

- Added: Gradle plugin
- Improved: Reworked the gradle modules to better be published as a library (vgo-core) and thin application wrapper (vgo).
- Added: Sonatype publishing

## 1.3.0

_01.18.2021_
## 1.3.0 - 01.18.2021

- Added: Collapse multiple Bézier curves into elliptical arcs when possible
- Improvement: Target JVM 11

## 1.2.2

_10.20.2020_
## 1.2.2 - 10.20.2020

- Improvement: Show filenames with statistics with multiple file inputs and `--stats`
- Improvement: Remove Kotlin metadata from the output jar
- Fixed: Temporarily removed an optimization that distorted some images

## 1.2.1

_10.01.2020_
## 1.2.1 - 10.01.2020

- Fixed: Some images with curves that lie on a circle omit any representation of that circle in the output
- Fixed: Modifying files in-place sometimes results in destroying non-vector files.

## 1.2.0

_09.28.2020_
## 1.2.0 - 09.28.2020

- Improvement: Resort to distribution via a fat jar. Requires managing fewer files and results in a smaller installation since R8 can operate on classes from dependencies as well.
- Improvement: Use R8 for optimization. R8 produces a slightly smaller jar and in some cases faster code as well.

## 1.1.1

_07.13.2020_
## 1.1.1 - 07.13.2020

- Fixed: A crash when running on a file in the current directory
- Improvement: Report an error when an input file doesn't exist

## 1.1.0

_06.20.2020_
## 1.1.0 - 06.20.2020

- New: Remove redundant close path commands
- Improvement: Use the Gradle Application Plugin to build application distrobutions, simplifying installation and making running the tool a little simpler.
18 changes: 13 additions & 5 deletions vgo/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,13 @@ plugins {
id("com.vanniktech.maven.publish")
}

sourceSets {
main {
java.srcDirs("src/generated/kotlin")
}
}
kotlin.sourceSets.getByName("main").kotlin.srcDir("src/generated/kotlin")

val r8: Configuration by configurations.creating

dependencies {
implementation(project(":vgo-core"))
implementation("com.android.tools:sdk-common:31.5.1")

testImplementation("com.willowtreeapps.assertk:assertk-jvm:0.28.1")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.3")
Expand Down Expand Up @@ -55,6 +52,17 @@ tasks {
"**/module-info.class",
"META-INF/maven/**",
"META-INF/*.version",
"META-INF/LICENSE*",
"META-INF/LGPL2.1",
"META-INF/DEPENDENCIES",
"META-INF/AL2.0",
"META-INF/BCKEY.DSA",
"META-INF/BC2048KE.DSA",
"META-INF/BCKEY.SF",
"META-INF/BC2048KE.SF",
"**/NOTICE*",
"javax/activation/**",
"xsd/catalog.xml",
)
}
}
Expand Down
62 changes: 45 additions & 17 deletions vgo/src/main/kotlin/com/jzbrooks/vgo/Application.kt
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
package com.jzbrooks.vgo

import com.android.ide.common.vectordrawable.Svg2Vector
import com.jzbrooks.BuildConstants
import com.jzbrooks.vgo.core.Writer
import com.jzbrooks.vgo.svg.ScalableVectorGraphic
import com.jzbrooks.vgo.svg.ScalableVectorGraphicWriter
import com.jzbrooks.vgo.svg.SvgOptimizationRegistry
import com.jzbrooks.vgo.svg.parse
import com.jzbrooks.vgo.svg.toVectorDrawable
import com.jzbrooks.vgo.util.xml.asSequence
import com.jzbrooks.vgo.vd.VectorDrawable
import com.jzbrooks.vgo.vd.VectorDrawableOptimizationRegistry
import com.jzbrooks.vgo.vd.VectorDrawableWriter
import com.jzbrooks.vgo.vd.toSvg
import org.w3c.dom.Document
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.File
import javax.xml.parsers.DocumentBuilderFactory
import kotlin.math.absoluteValue
Expand Down Expand Up @@ -63,11 +65,11 @@ class Application {
if (inputs.isEmpty()) {
require(outputs.isEmpty())

var path = readLine()
var path = readlnOrNull()
val standardInPaths = mutableListOf<String>()
while (path != null) {
standardInPaths.add(path)
path = readLine()
path = readlnOrNull()
}

inputs = standardInPaths
Expand Down Expand Up @@ -139,30 +141,54 @@ class Application {
input.inputStream().use { inputStream ->
val sizeBefore = inputStream.channel.size()

val document =
DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(input).apply {
documentElement.normalize()
}
val document = DOCUMENT_BUILDER_FACTORY.newDocumentBuilder().parse(input)
document.documentElement.normalize()

val rootNodes = document.childNodes.asSequence().filter { it.nodeType == Document.ELEMENT_NODE }.toList()

var graphic =
when {
rootNodes.any { it.nodeName == "svg" || input.extension == "svg" } -> parse(rootNodes.first())
rootNodes.any { it.nodeName == "vector" && input.extension == "xml" } ->
com.jzbrooks.vgo.vd.parse(
rootNodes.first(),
)
rootNodes.any { it.nodeName == "svg" || input.extension == "svg" } -> {
if (outputFormat == "vd") {
ByteArrayOutputStream().use { pipeOrigin ->
val errors = Svg2Vector.parseSvgToXml(input.toPath(), pipeOrigin)
if (errors != "") {
System.err.println(
"""
Skipping ${input.path}
$errors
""".trimIndent(),
)
null
} else {
val pipeTerminal = ByteArrayInputStream(pipeOrigin.toByteArray())
val convertedDocument =
DOCUMENT_BUILDER_FACTORY.newDocumentBuilder().parse(pipeTerminal)
convertedDocument.documentElement.normalize()

val documentRoot =
convertedDocument.childNodes.asSequence().first {
it.nodeType == Document.ELEMENT_NODE
}

com.jzbrooks.vgo.vd.parse(documentRoot)
}
}
} else {
parse(rootNodes.first())
}
}
rootNodes.any { it.nodeName == "vector" && input.extension == "xml" } -> {
com.jzbrooks.vgo.vd.parse(rootNodes.first())
}
else -> if (input == output) return else null
}

if (graphic is VectorDrawable && outputFormat == "svg") {
graphic = graphic.toSvg()
}

if (graphic is ScalableVectorGraphic && outputFormat == "vd") {
graphic = graphic.toVectorDrawable()
}

val optimizationRegistry =
when (graphic) {
is VectorDrawable -> VectorDrawableOptimizationRegistry()
Expand Down Expand Up @@ -217,9 +243,10 @@ class Application {
assert(input.isDirectory)
assert(output.isDirectory || !output.exists())

input.listFiles { file -> !file.isHidden }?.forEach { file ->
for (file in input.walkTopDown().filter { file -> !file.isHidden && !file.isDirectory }) {
handleFile(file, File(output, file.name), options)
}

if (printStats) {
val message = "| Total bytes saved: ${(totalBytesBefore - totalBytesAfter).roundToInt()} |"
val border = "-".repeat(message.length)
Expand Down Expand Up @@ -262,6 +289,7 @@ class Application {
get() = key.isDirectory && (value.isDirectory || !value.exists())

companion object {
private val DOCUMENT_BUILDER_FACTORY = DocumentBuilderFactory.newInstance()
private const val HELP_MESSAGE = """
> vgo [options] [file/directory]
Expand Down

0 comments on commit c198ad1

Please sign in to comment.