├── sample
├── app
│ ├── .gitignore
│ ├── src
│ │ ├── main
│ │ │ ├── res
│ │ │ │ ├── values
│ │ │ │ │ ├── strings.xml
│ │ │ │ │ ├── themes.xml
│ │ │ │ │ └── colors.xml
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ ├── ic_launcher.webp
│ │ │ │ │ └── ic_launcher_round.webp
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ ├── ic_launcher.webp
│ │ │ │ │ └── ic_launcher_round.webp
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ ├── ic_launcher.webp
│ │ │ │ │ └── ic_launcher_round.webp
│ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ ├── ic_launcher.webp
│ │ │ │ │ └── ic_launcher_round.webp
│ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ ├── ic_launcher.webp
│ │ │ │ │ └── ic_launcher_round.webp
│ │ │ │ ├── mipmap-anydpi-v26
│ │ │ │ │ ├── ic_launcher.xml
│ │ │ │ │ └── ic_launcher_round.xml
│ │ │ │ └── drawable
│ │ │ │ │ ├── ic_launcher_foreground.xml
│ │ │ │ │ └── ic_launcher_background.xml
│ │ │ ├── AndroidManifest.xml
│ │ │ └── java
│ │ │ │ └── dev
│ │ │ │ └── sergiobelda
│ │ │ │ └── compose
│ │ │ │ └── vectorize
│ │ │ │ ├── ui
│ │ │ │ └── theme
│ │ │ │ │ ├── Color.kt
│ │ │ │ │ ├── Type.kt
│ │ │ │ │ └── Theme.kt
│ │ │ │ └── MainActivity.kt
│ │ └── test
│ │ │ ├── snapshots
│ │ │ └── images
│ │ │ │ ├── dev.sergiobelda.compose.vectorize_ScreenshotTest_vectorWithStrokeAttribute.png
│ │ │ │ └── dev.sergiobelda.compose.vectorize_ScreenshotTest_vectorWithThemedAttributes.png
│ │ │ └── kotlin
│ │ │ └── dev
│ │ │ └── sergiobelda
│ │ │ └── compose
│ │ │ └── vectorize
│ │ │ └── ScreenshotTest.kt
│ ├── xml-images
│ │ ├── icons
│ │ │ ├── outlined
│ │ │ │ ├── arrow_back.xml
│ │ │ │ ├── home.xml
│ │ │ │ └── add.xml
│ │ │ ├── rounded
│ │ │ │ ├── arrow_back.xml
│ │ │ │ ├── home.xml
│ │ │ │ └── add.xml
│ │ │ └── add.xml
│ │ └── illustrations
│ │ │ └── compose-multiplatform.xml
│ ├── proguard-rules.pro
│ └── build.gradle.kts
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── .gitignore
├── settings.gradle.kts
├── build.gradle.kts
├── gradle.properties
└── gradlew.bat
├── sample-mpp
├── android
│ ├── .gitignore
│ ├── src
│ │ └── main
│ │ │ ├── res
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── themes.xml
│ │ │ │ └── colors.xml
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ └── drawable
│ │ │ │ ├── ic_launcher_foreground.xml
│ │ │ │ └── ic_launcher_background.xml
│ │ │ ├── kotlin
│ │ │ └── dev
│ │ │ │ └── sergiobelda
│ │ │ │ └── compose
│ │ │ │ └── vectorize
│ │ │ │ └── sample
│ │ │ │ └── android
│ │ │ │ ├── ui
│ │ │ │ └── theme
│ │ │ │ │ ├── Color.kt
│ │ │ │ │ ├── Type.kt
│ │ │ │ │ └── Theme.kt
│ │ │ │ └── MainActivity.kt
│ │ │ └── AndroidManifest.xml
│ ├── proguard-rules.pro
│ └── build.gradle.kts
├── common
│ ├── .gitignore
│ ├── xml-images
│ │ ├── icons
│ │ │ ├── outlined
│ │ │ │ ├── arrow_back.xml
│ │ │ │ ├── home.xml
│ │ │ │ └── add.xml
│ │ │ └── rounded
│ │ │ │ ├── arrow_back.xml
│ │ │ │ ├── home.xml
│ │ │ │ └── add.xml
│ │ └── illustrations
│ │ │ └── compose-multiplatform.xml
│ ├── proguard-rules.pro
│ ├── src
│ │ ├── iosMain
│ │ │ └── kotlin
│ │ │ │ └── dev
│ │ │ │ └── sergiobelda
│ │ │ │ └── compose
│ │ │ │ └── vectorize
│ │ │ │ └── sample
│ │ │ │ └── Main.ios.kt
│ │ └── commonMain
│ │ │ └── kotlin
│ │ │ └── dev
│ │ │ └── sergiobelda
│ │ │ └── compose
│ │ │ └── vectorize
│ │ │ └── sample
│ │ │ └── MainScreen.kt
│ └── build.gradle.kts
├── desktop
│ ├── .gitignore
│ ├── src
│ │ └── jvmMain
│ │ │ └── kotlin
│ │ │ └── dev
│ │ │ └── sergiobelda
│ │ │ └── compose
│ │ │ └── vectorize
│ │ │ └── sample
│ │ │ └── desktop
│ │ │ └── Main.desktop.kt
│ └── build.gradle.kts
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── ios
│ ├── Compose Vectorize Sample
│ │ ├── Assets.xcassets
│ │ │ ├── Contents.json
│ │ │ ├── AccentColor.colorset
│ │ │ │ └── Contents.json
│ │ │ └── AppIcon.appiconset
│ │ │ │ └── Contents.json
│ │ ├── Preview Content
│ │ │ └── Preview Assets.xcassets
│ │ │ │ └── Contents.json
│ │ ├── Compose_Vectorize_SampleApp.swift
│ │ └── ContentView.swift
│ └── Compose Vectorize Sample.xcodeproj
│ │ └── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── README.md
├── build.gradle.kts
├── .gitignore
├── gradle.properties
├── web
│ ├── src
│ │ └── jsMain
│ │ │ ├── resources
│ │ │ └── index.html
│ │ │ └── kotlin
│ │ │ └── dev
│ │ │ └── sergiobelda
│ │ │ └── compose
│ │ │ └── vectorize
│ │ │ └── sample
│ │ │ └── web
│ │ │ └── Main.js.kt
│ └── build.gradle.kts
├── settings.gradle.kts
└── gradlew.bat
├── compose-vectorize-core
├── .gitignore
├── consumer-rules.pro
├── proguard-rules.pro
├── src
│ └── commonMain
│ │ └── kotlin
│ │ └── dev
│ │ └── sergiobelda
│ │ └── compose
│ │ └── vectorize
│ │ └── core
│ │ └── ImageVector.kt
└── build.gradle.kts
├── compose-vectorize-gradle-plugin
├── .gitignore
├── src
│ └── main
│ │ └── kotlin
│ │ └── dev
│ │ └── sergiobelda
│ │ └── compose
│ │ └── vectorize
│ │ ├── generator
│ │ ├── imageparser
│ │ │ ├── DpProcessor.kt
│ │ │ └── VectorColorParser.kt
│ │ ├── ImageCategories.kt
│ │ ├── utils
│ │ │ ├── KotlinPoetUtils.kt
│ │ │ └── ProcessorUtils.kt
│ │ ├── vector
│ │ │ ├── StrokeCap.kt
│ │ │ ├── StrokeJoin.kt
│ │ │ ├── FillType.kt
│ │ │ ├── Vector.kt
│ │ │ └── PathParser.kt
│ │ ├── ImageCategoriesProcessor.kt
│ │ ├── ImageCategoriesWriter.kt
│ │ ├── Image.kt
│ │ ├── ImageWriter.kt
│ │ ├── ImageCategoriesGenerator.kt
│ │ ├── task
│ │ │ └── ImageVectorGenerationTask.kt
│ │ ├── Names.kt
│ │ └── ImageProcessor.kt
│ │ └── gradle
│ │ └── plugin
│ │ └── ImageVectorGenerationPlugin.kt
└── build.gradle.kts
├── docs
├── assets
│ └── diagram.png
├── themed-attributes.md
├── usage.md
└── index.md
├── gradle
├── build-logic
│ ├── .gitignore
│ ├── gradle
│ │ └── wrapper
│ │ │ ├── gradle-wrapper.jar
│ │ │ └── gradle-wrapper.properties
│ ├── settings.gradle.kts
│ ├── convention
│ │ ├── build.gradle.kts
│ │ └── src
│ │ │ └── main
│ │ │ └── kotlin
│ │ │ └── dev
│ │ │ └── sergiobelda
│ │ │ └── gradle
│ │ │ └── buildlogic
│ │ │ └── convention
│ │ │ └── SpotlessConventionPlugin.kt
│ ├── gradlew.bat
│ └── gradlew
├── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
└── libs.versions.toml
├── renovate.json
├── .gitignore
├── settings.gradle.kts
├── gradle.properties
├── .github
└── workflows
│ ├── build.yml
│ └── deploy-docs.yml
├── mkdocs.yml
├── gradlew.bat
├── README.md
└── gradlew
/sample/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/sample-mpp/android/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/sample-mpp/common/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/sample-mpp/desktop/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/compose-vectorize-core/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/compose-vectorize-core/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/compose-vectorize-gradle-plugin/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/docs/assets/diagram.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serbelga/compose-vectorize/HEAD/docs/assets/diagram.png
--------------------------------------------------------------------------------
/sample/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | sample
3 |
--------------------------------------------------------------------------------
/gradle/build-logic/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | /.idea/
3 | /build/
4 | /convention/build/
5 | /local.properties
6 | .gradle
7 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serbelga/compose-vectorize/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/sample/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serbelga/compose-vectorize/HEAD/sample/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": [
4 | "config:recommended"
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/sample-mpp/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serbelga/compose-vectorize/HEAD/sample-mpp/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/sample-mpp/ios/Compose Vectorize Sample/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/gradle/build-logic/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serbelga/compose-vectorize/HEAD/gradle/build-logic/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/sample/app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serbelga/compose-vectorize/HEAD/sample/app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/sample/app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serbelga/compose-vectorize/HEAD/sample/app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/sample/app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serbelga/compose-vectorize/HEAD/sample/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/sample/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serbelga/compose-vectorize/HEAD/sample/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/sample/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serbelga/compose-vectorize/HEAD/sample/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/sample-mpp/README.md:
--------------------------------------------------------------------------------
1 | ## Run sample-mpp
2 |
3 | ### Run on Desktop
4 |
5 | `./gradlew :desktop:run`
6 |
7 | ### Run on Web
8 |
9 | `./gradlew :web:jsBrowserRun`
10 |
--------------------------------------------------------------------------------
/sample/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serbelga/compose-vectorize/HEAD/sample/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/sample/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serbelga/compose-vectorize/HEAD/sample/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/sample-mpp/android/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serbelga/compose-vectorize/HEAD/sample-mpp/android/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/sample-mpp/android/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serbelga/compose-vectorize/HEAD/sample-mpp/android/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/sample-mpp/ios/Compose Vectorize Sample/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/sample/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serbelga/compose-vectorize/HEAD/sample/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/sample/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serbelga/compose-vectorize/HEAD/sample/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/sample-mpp/android/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serbelga/compose-vectorize/HEAD/sample-mpp/android/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/sample-mpp/android/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serbelga/compose-vectorize/HEAD/sample-mpp/android/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/sample-mpp/android/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serbelga/compose-vectorize/HEAD/sample-mpp/android/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/sample/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serbelga/compose-vectorize/HEAD/sample/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/sample-mpp/android/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serbelga/compose-vectorize/HEAD/sample-mpp/android/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/sample-mpp/android/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serbelga/compose-vectorize/HEAD/sample-mpp/android/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/sample-mpp/android/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serbelga/compose-vectorize/HEAD/sample-mpp/android/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/sample-mpp/android/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serbelga/compose-vectorize/HEAD/sample-mpp/android/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/sample-mpp/android/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serbelga/compose-vectorize/HEAD/sample-mpp/android/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/sample/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/sample-mpp/ios/Compose Vectorize Sample.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/sample-mpp/ios/Compose Vectorize Sample/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/sample/app/src/test/snapshots/images/dev.sergiobelda.compose.vectorize_ScreenshotTest_vectorWithStrokeAttribute.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serbelga/compose-vectorize/HEAD/sample/app/src/test/snapshots/images/dev.sergiobelda.compose.vectorize_ScreenshotTest_vectorWithStrokeAttribute.png
--------------------------------------------------------------------------------
/sample/app/src/test/snapshots/images/dev.sergiobelda.compose.vectorize_ScreenshotTest_vectorWithThemedAttributes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serbelga/compose-vectorize/HEAD/sample/app/src/test/snapshots/images/dev.sergiobelda.compose.vectorize_ScreenshotTest_vectorWithThemedAttributes.png
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-all.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/sample-mpp/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.androidApplication) apply false
3 | alias(libs.plugins.androidLibrary) apply false
4 | alias(libs.plugins.composeCompiler) apply false
5 | alias(libs.plugins.composeMultiplatform) apply false
6 | alias(libs.plugins.kotlin) apply false
7 | }
8 |
--------------------------------------------------------------------------------
/sample/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 | local.properties
16 | .idea/
--------------------------------------------------------------------------------
/sample/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-all.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/sample-mpp/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-all.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/sample-mpp/ios/Compose Vectorize Sample/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "platform" : "ios",
6 | "size" : "1024x1024"
7 | }
8 | ],
9 | "info" : {
10 | "author" : "xcode",
11 | "version" : 1
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/sample-mpp/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 | local.properties
16 | /.idea/
17 |
--------------------------------------------------------------------------------
/gradle/build-logic/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/sample-mpp/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
2 | android.useAndroidX=true
3 | kotlin.code.style=official
4 | kotlin.mpp.androidSourceSetLayoutVersion=2
5 | android.nonTransitiveRClass=true
6 | org.jetbrains.compose.experimental.uikit.enabled=true
7 | org.jetbrains.compose.experimental.jscanvas.enabled=true
8 |
--------------------------------------------------------------------------------
/sample-mpp/ios/Compose Vectorize Sample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/sample-mpp/web/src/jsMain/resources/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Compose Vectorize Sample
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/sample/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/sample/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/sample-mpp/ios/Compose Vectorize Sample/Compose_Vectorize_SampleApp.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Compose_Vectorize_SampleApp.swift
3 | // Compose Vectorize Sample
4 | //
5 | // Created by Sergio Belda Galbis on 26/11/23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | @main
11 | struct Compose_Vectorize_SampleApp: App {
12 | var body: some Scene {
13 | WindowGroup {
14 | ContentView()
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/gradle/build-logic/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | rootProject.name = "build-logic"
2 |
3 | enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
4 |
5 | dependencyResolutionManagement {
6 | repositories {
7 | google()
8 | mavenCentral()
9 | }
10 | versionCatalogs {
11 | create("libs") {
12 | from(files("../libs.versions.toml"))
13 | }
14 | }
15 | }
16 |
17 | include(":convention")
18 |
--------------------------------------------------------------------------------
/sample/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 | local.properties
16 | /.idea/
17 | /sample-mpp/web/build/
18 |
19 | ## User settings
20 | **/xcuserdata/
21 |
22 | ## Xcode 8 and earlier
23 | *.xcscmblueprint
24 | *.xccheckout
25 | /.kotlin/
26 | /sample-mpp/.kotlin/
27 | /sample/.kotlin/
28 |
--------------------------------------------------------------------------------
/sample/app/xml-images/icons/outlined/arrow_back.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/sample-mpp/common/xml-images/icons/outlined/arrow_back.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | includeBuild("gradle/build-logic")
3 | repositories {
4 | google()
5 | mavenCentral()
6 | gradlePluginPortal()
7 | }
8 | }
9 | dependencyResolutionManagement {
10 | repositoriesMode.set(RepositoriesMode.PREFER_PROJECT)
11 | repositories {
12 | google()
13 | mavenCentral()
14 | maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
15 | }
16 | }
17 |
18 | rootProject.name = "compose-vectorize"
19 |
20 | include(":compose-vectorize-core")
21 | include(":compose-vectorize-gradle-plugin")
22 |
23 | enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
24 |
--------------------------------------------------------------------------------
/sample/app/xml-images/icons/outlined/home.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/sample-mpp/common/xml-images/icons/outlined/home.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/sample/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | includeBuild("../gradle/build-logic")
3 | repositories {
4 | google()
5 | mavenCentral()
6 | gradlePluginPortal()
7 | }
8 | }
9 | dependencyResolutionManagement {
10 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
11 | repositories {
12 | google()
13 | mavenCentral()
14 | }
15 | versionCatalogs {
16 | create("libs") {
17 | from(files("../gradle/libs.versions.toml"))
18 | }
19 | }
20 | }
21 |
22 | includeBuild("..")
23 |
24 | rootProject.name = "sample"
25 |
26 | include(":app")
27 |
28 | enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
29 |
--------------------------------------------------------------------------------
/gradle/build-logic/convention/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | `kotlin-dsl`
3 | }
4 |
5 | group = "dev.sergiobelda.gradle"
6 |
7 | java {
8 | toolchain {
9 | languageVersion = JavaLanguageVersion.of(17)
10 | }
11 | }
12 |
13 | dependencies {
14 | implementation(libs.android.gradlePlugin)
15 | implementation(libs.kotlin.gradlePlugin)
16 | implementation(libs.spotless.gradlePlugin)
17 | }
18 |
19 | gradlePlugin {
20 | plugins {
21 | register("spotless") {
22 | id = "dev.sergiobelda.gradle.spotless"
23 | implementationClass =
24 | "dev.sergiobelda.gradle.buildlogic.convention.SpotlessConventionPlugin"
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/sample-mpp/ios/Compose Vectorize Sample/ContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // Compose Vectorize Sample
4 | //
5 | // Created by Sergio Belda Galbis on 26/11/23.
6 | //
7 |
8 | import SwiftUI
9 | import common
10 |
11 | struct ContentView: View {
12 | var body: some View {
13 | ComposeView()
14 | .ignoresSafeArea(.keyboard) // Compose has own keyboard handler
15 | }
16 | }
17 |
18 | struct ComposeView: UIViewControllerRepresentable {
19 | func makeUIViewController(context: Context) -> UIViewController {
20 | Main_iosKt.MainViewController()
21 | }
22 |
23 | func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/sample/app/xml-images/icons/rounded/arrow_back.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/sample-mpp/android/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
13 |
14 | android
15 |
--------------------------------------------------------------------------------
/sample-mpp/common/xml-images/icons/rounded/arrow_back.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/sample-mpp/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | includeBuild("../gradle/build-logic")
3 | repositories {
4 | google()
5 | mavenCentral()
6 | gradlePluginPortal()
7 | }
8 | }
9 | dependencyResolutionManagement {
10 | repositoriesMode.set(RepositoriesMode.PREFER_PROJECT)
11 | repositories {
12 | google()
13 | mavenCentral()
14 | maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
15 | }
16 | versionCatalogs {
17 | create("libs") {
18 | from(files("../gradle/libs.versions.toml"))
19 | }
20 | }
21 | }
22 |
23 | includeBuild("..")
24 |
25 | rootProject.name = "sample-mpp"
26 |
27 | include(":android")
28 | include(":common")
29 | include(":desktop")
30 | include(":web")
31 |
32 | enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
33 |
--------------------------------------------------------------------------------
/sample/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/sample-mpp/android/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/sample-mpp/android/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/sample-mpp/common/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/compose-vectorize-core/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/compose-vectorize-gradle-plugin/src/main/kotlin/dev/sergiobelda/compose/vectorize/generator/imageparser/DpProcessor.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Sergio Belda
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package dev.sergiobelda.compose.vectorize.generator.imageparser
18 |
19 | internal fun String.processDpDimension(): String =
20 | this.replace("dp", "")
21 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
2 | android.useAndroidX=true
3 | kotlin.code.style=official
4 | android.nonTransitiveRClass=true
5 | org.jetbrains.compose.experimental.uikit.enabled=true
6 | org.jetbrains.compose.experimental.jscanvas.enabled=true
7 |
8 | POM_NAME=Compose Vectorize
9 | POM_DESCRIPTION=A Kotlin Multiplatform library to generate compose.ui.graphics.vector.ImageVector from XML files.
10 |
11 | POM_URL=https://github.com/serbelga/compose-vectorize/
12 | POM_SCM_URL=https://github.com/serbelga/compose-vectorize/
13 | POM_SCM_CONNECTION=scm:git:git://github.com/serbelga/compose-vectorize.git
14 | POM_SCM_DEV_CONNECTION=scm:git:git://github.com/serbelga/compose-vectorize.git
15 |
16 | POM_LICENCE_NAME=The Apache Software License, Version 2.0
17 | POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt
18 | POM_LICENCE_DIST=repo
19 |
20 | POM_DEVELOPER_ID=serbelga
21 | POM_DEVELOPER_NAME=Sergio Belda
22 |
--------------------------------------------------------------------------------
/compose-vectorize-gradle-plugin/src/main/kotlin/dev/sergiobelda/compose/vectorize/generator/ImageCategories.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Sergio Belda
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package dev.sergiobelda.compose.vectorize.generator
18 |
19 | import java.io.File
20 |
21 | data class ImageCategories(
22 | val categories: List,
23 | val packageName: String,
24 | )
25 |
--------------------------------------------------------------------------------
/sample/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/sample-mpp/common/src/iosMain/kotlin/dev/sergiobelda/compose/vectorize/sample/Main.ios.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Sergio Belda
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package dev.sergiobelda.compose.vectorize.sample
18 |
19 | import androidx.compose.ui.window.ComposeUIViewController
20 | import platform.UIKit.UIViewController
21 |
22 | fun MainViewController(): UIViewController = ComposeUIViewController {
23 | MainScreen()
24 | }
25 |
--------------------------------------------------------------------------------
/sample-mpp/android/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/sample-mpp/android/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/sample-mpp/android/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
13 |
14 | #FFBB86FC
15 | #FF6200EE
16 | #FF3700B3
17 | #FF03DAC5
18 | #FF018786
19 | #FF000000
20 | #FFFFFFFF
21 |
22 |
--------------------------------------------------------------------------------
/sample/app/src/main/java/dev/sergiobelda/compose/vectorize/ui/theme/Color.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Sergio Belda
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package dev.sergiobelda.compose.vectorize.ui.theme
18 |
19 | import androidx.compose.ui.graphics.Color
20 |
21 | val Purple80 = Color(0xFFD0BCFF)
22 | val PurpleGrey80 = Color(0xFFCCC2DC)
23 | val Pink80 = Color(0xFFEFB8C8)
24 |
25 | val Purple40 = Color(0xFF6650a4)
26 | val PurpleGrey40 = Color(0xFF625b71)
27 | val Pink40 = Color(0xFF7D5260)
28 |
--------------------------------------------------------------------------------
/compose-vectorize-gradle-plugin/src/main/kotlin/dev/sergiobelda/compose/vectorize/generator/utils/KotlinPoetUtils.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Sergio Belda
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package dev.sergiobelda.compose.vectorize.generator.utils
18 |
19 | import com.squareup.kotlinpoet.FileSpec
20 |
21 | /**
22 | * Sets the indent for this [FileSpec] to match that of our code style.
23 | */
24 | internal fun FileSpec.Builder.setIndent() = indent(Indent)
25 |
26 | private val Indent = " ".repeat(4)
27 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build Project
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | - develop
8 | pull_request:
9 | branches:
10 | - main
11 | - develop
12 |
13 | jobs:
14 | build:
15 |
16 | runs-on: macos-15
17 |
18 | steps:
19 | - name: Checkout the code
20 | uses: actions/checkout@v5
21 | - name: set up JDK 17
22 | uses: actions/setup-java@v5
23 | with:
24 | java-version: '17'
25 | distribution: 'temurin'
26 | cache: gradle
27 | - name: Run spotlessCheck
28 | run: ./gradlew spotlessCheck
29 | - name: Run build
30 | run: ./gradlew build
31 | - name: Run screenshot tests
32 | run: cd sample && ./gradlew app:verifyPaparazziDebug && cd ..
33 | - name: Build android sample
34 | run: cd sample && ./gradlew app:assembleDebug && cd ..
35 | - name: Build android sample-mpp
36 | run: cd sample-mpp && ./gradlew android:assembleDebug && cd ..
37 | - name: Build desktop sample-mpp
38 | run: cd sample-mpp && ./gradlew desktop:assemble && cd ..
39 |
--------------------------------------------------------------------------------
/docs/themed-attributes.md:
--------------------------------------------------------------------------------
1 | # Themed Attributes
2 |
3 | It also supports theme attributes. If you are using theme color attributes, like `?attr/colorPrimary`
4 | or `?attr/colorSecondary`, they can be converted to Compose `MaterialTheme` tokens, which means that
5 | you can update illustration colors based on the token values. It is also compatible with Material3 dynamic colors.
6 |
7 | ```xml
8 |
11 | ```
12 |
13 |
18 |
19 | !!! info
20 |
21 | It requires use Compose Material 3 dependency.
22 |
--------------------------------------------------------------------------------
/sample-mpp/android/src/main/kotlin/dev/sergiobelda/compose/vectorize/sample/android/ui/theme/Color.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Sergio Belda
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package dev.sergiobelda.compose.vectorize.sample.android.ui.theme
18 |
19 | import androidx.compose.ui.graphics.Color
20 |
21 | val Purple80 = Color(0xFFD0BCFF)
22 | val PurpleGrey80 = Color(0xFFCCC2DC)
23 | val Pink80 = Color(0xFFEFB8C8)
24 |
25 | val Purple40 = Color(0xFF6650a4)
26 | val PurpleGrey40 = Color(0xFF625b71)
27 | val Pink40 = Color(0xFF7D5260)
28 |
--------------------------------------------------------------------------------
/compose-vectorize-gradle-plugin/src/main/kotlin/dev/sergiobelda/compose/vectorize/generator/vector/StrokeCap.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Sergio Belda
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package dev.sergiobelda.compose.vectorize.generator.vector
18 |
19 | /**
20 | * Styles to use for line endings.
21 | *
22 | * This maps to [androidx.compose.ui.graphics.StrokeCap] used in the framework, and can be defined in XML
23 | * via `android:strokeLineCap`.
24 | */
25 | enum class StrokeCap {
26 | Butt,
27 | Round,
28 | Square,
29 | }
30 |
--------------------------------------------------------------------------------
/compose-vectorize-gradle-plugin/src/main/kotlin/dev/sergiobelda/compose/vectorize/generator/vector/StrokeJoin.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Sergio Belda
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package dev.sergiobelda.compose.vectorize.generator.vector
18 |
19 | /**
20 | * Styles to use for line joins.
21 | *
22 | * This maps to [androidx.compose.ui.graphics.StrokeJoin] used in the framework, and can be defined in XML
23 | * via `android:strokeLineJoin`.
24 | */
25 | enum class StrokeJoin {
26 | Bevel,
27 | Miter,
28 | Round,
29 | }
30 |
--------------------------------------------------------------------------------
/compose-vectorize-gradle-plugin/src/main/kotlin/dev/sergiobelda/compose/vectorize/generator/vector/FillType.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Sergio Belda
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package dev.sergiobelda.compose.vectorize.generator.vector
18 |
19 | /**
20 | * Determines the winding rule that decides how the interior of a [VectorNode.Path] is calculated.
21 | *
22 | * This maps to [android.graphics.Path.FillType] used in the framework, and can be defined in XML
23 | * via `android:fillType`.
24 | */
25 | enum class FillType {
26 | NonZero,
27 | EvenOdd,
28 | }
29 |
--------------------------------------------------------------------------------
/sample/app/xml-images/icons/rounded/home.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/sample-mpp/common/xml-images/icons/rounded/home.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/docs/usage.md:
--------------------------------------------------------------------------------
1 | # Usage
2 |
3 | Create a folder called `xml-images` in the module folder.
4 |
5 | `:module/xml-images`
6 |
7 | ## Android
8 |
9 |
10 |
11 | ## Multiplatform
12 |
13 |
14 |
15 | ## Generate images
16 |
17 | You can manually generate ImageVector for these XML files by calling `gradle generateImages`. This will create a Kotlin file for each XML file containing the ImageVector in the build folder.
18 |
19 |
20 |
21 |
22 | Now, you can use reference this image in the Compose code:
23 |
24 | ```kotlin
25 | Icon(Images.Icons.Add, contentDescription = null)
26 | ```
27 |
28 | Note that automatically a category called "Icons" has been created. A category is created for each subfolder in `xml-images` folder.
29 |
--------------------------------------------------------------------------------
/sample/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.androidApplication) apply false
3 | alias(libs.plugins.androidLibrary) apply false
4 | alias(libs.plugins.composeCompiler) apply false
5 | alias(libs.plugins.kotlin) apply false
6 | }
7 |
8 | // Paparazzi workaround for version > 1.3.2
9 | // see: https://github.com/cashapp/paparazzi/releases/tag/1.3.2
10 | subprojects {
11 | plugins.withId("app.cash.paparazzi") {
12 | // Defer until afterEvaluate so that testImplementation is created by Android plugin.
13 | afterEvaluate {
14 | dependencies.constraints {
15 | add("testImplementation", "com.google.guava:guava") {
16 | attributes {
17 | attribute(
18 | TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE,
19 | objects.named(TargetJvmEnvironment.STANDARD_JVM)
20 | )
21 | }
22 | because("LayoutLib and sdk-common depend on Guava's -jre published variant." +
23 | "See https://github.com/cashapp/paparazzi/issues/906.")
24 | }
25 | }
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/compose-vectorize-gradle-plugin/src/main/kotlin/dev/sergiobelda/compose/vectorize/generator/ImageCategoriesProcessor.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Sergio Belda
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package dev.sergiobelda.compose.vectorize.generator
18 |
19 | import java.io.File
20 |
21 | class ImageCategoriesProcessor(
22 | private val packageName: String,
23 | private val imagesDirectories: List,
24 | ) {
25 |
26 | fun process(): ImageCategories = loadImageCategories()
27 |
28 | private fun loadImageCategories(): ImageCategories =
29 | ImageCategories(
30 | categories = imagesDirectories,
31 | packageName = packageName,
32 | )
33 | }
34 |
--------------------------------------------------------------------------------
/compose-vectorize-gradle-plugin/src/main/kotlin/dev/sergiobelda/compose/vectorize/generator/ImageCategoriesWriter.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Sergio Belda
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package dev.sergiobelda.compose.vectorize.generator
18 |
19 | import java.io.File
20 |
21 | class ImageCategoriesWriter(private val imageCategories: ImageCategories) {
22 |
23 | fun generateTo(outputSrcDirectory: File) {
24 | val fileSpec = ImageCategoriesGenerator(
25 | imageCategoriesPackageName = imageCategories.packageName,
26 | categories = imageCategories.categories,
27 | ).createFileSpec()
28 | fileSpec.writeTo(outputSrcDirectory)
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/sample/app/xml-images/icons/add.xml:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
21 |
24 |
25 |
--------------------------------------------------------------------------------
/sample/app/xml-images/icons/outlined/add.xml:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
22 |
25 |
26 |
--------------------------------------------------------------------------------
/sample/app/xml-images/icons/rounded/add.xml:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
22 |
25 |
26 |
--------------------------------------------------------------------------------
/sample-mpp/android/src/main/kotlin/dev/sergiobelda/compose/vectorize/sample/android/MainActivity.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Sergio Belda
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package dev.sergiobelda.compose.vectorize.sample.android
18 |
19 | import android.os.Bundle
20 | import androidx.activity.ComponentActivity
21 | import androidx.activity.compose.setContent
22 | import dev.sergiobelda.compose.vectorize.sample.MainScreen
23 | import dev.sergiobelda.compose.vectorize.sample.android.ui.theme.SampleTheme
24 |
25 | class MainActivity : ComponentActivity() {
26 | override fun onCreate(savedInstanceState: Bundle?) {
27 | super.onCreate(savedInstanceState)
28 | setContent {
29 | SampleTheme {
30 | MainScreen()
31 | }
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/sample-mpp/common/xml-images/icons/rounded/add.xml:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
22 |
25 |
26 |
--------------------------------------------------------------------------------
/sample-mpp/common/xml-images/icons/outlined/add.xml:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
22 |
25 |
26 |
--------------------------------------------------------------------------------
/compose-vectorize-core/src/commonMain/kotlin/dev/sergiobelda/compose/vectorize/core/ImageVector.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Sergio Belda
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package dev.sergiobelda.compose.vectorize.core
18 |
19 | import androidx.compose.ui.graphics.vector.ImageVector
20 | import androidx.compose.ui.unit.dp
21 |
22 | inline fun imageVector(
23 | name: String,
24 | width: Float,
25 | height: Float,
26 | viewportWidth: Float,
27 | viewportHeight: Float,
28 | autoMirror: Boolean,
29 | block: ImageVector.Builder.() -> ImageVector.Builder,
30 | ): ImageVector = ImageVector.Builder(
31 | name = name,
32 | defaultWidth = width.dp,
33 | defaultHeight = height.dp,
34 | viewportWidth = viewportWidth,
35 | viewportHeight = viewportHeight,
36 | autoMirror = autoMirror,
37 | ).block().build()
38 |
--------------------------------------------------------------------------------
/sample/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Kotlin code style for this project: "official" or "obsolete":
19 | kotlin.code.style=official
20 | # Enables namespacing of each library's R class so that its R class includes only the
21 | # resources declared in the library itself and none from the library's dependencies,
22 | # thereby reducing the size of the R class for that library
23 | android.nonTransitiveRClass=true
--------------------------------------------------------------------------------
/.github/workflows/deploy-docs.yml:
--------------------------------------------------------------------------------
1 | name: Deploy docs
2 |
3 | on:
4 | push:
5 | tags:
6 | - '*'
7 | workflow_dispatch:
8 |
9 | permissions:
10 | contents: read
11 | pages: write
12 | id-token: write
13 |
14 | jobs:
15 | deploy_docs:
16 |
17 | runs-on: macos-15
18 |
19 | environment:
20 | name: github-pages
21 | url: ${{ steps.deployment.outputs.page_url }}
22 |
23 | steps:
24 | - uses: actions/checkout@v5
25 | with:
26 | lfs: 'true'
27 |
28 | - name: Setup JDK 17
29 | uses: actions/setup-java@v5
30 | with:
31 | java-version: '17'
32 | distribution: 'temurin'
33 |
34 | - name: Setup Python
35 | uses: actions/setup-python@v6
36 | with:
37 | python-version: '3.x'
38 |
39 | - name: Setup Pages
40 | uses: actions/configure-pages@v5
41 |
42 | - name: Install dependencies
43 | run: |
44 | python3 -m pip install --upgrade pip
45 | python3 -m pip install mkdocs mkdocs-material mkdocs-video "mkdocs-material[imaging]"
46 |
47 | - uses: gradle/gradle-build-action@v3
48 |
49 | - name: Build site
50 | run: mkdocs build
51 |
52 | - name: Upload artifact
53 | uses: actions/upload-pages-artifact@v4
54 | with:
55 | path: 'site'
56 |
57 | - name: Deploy to GitHub Pages
58 | id: deployment
59 | uses: actions/deploy-pages@v4
--------------------------------------------------------------------------------
/sample-mpp/web/src/jsMain/kotlin/dev/sergiobelda/compose/vectorize/sample/web/Main.js.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Sergio Belda
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package dev.sergiobelda.compose.vectorize.sample.web
18 |
19 | import androidx.compose.foundation.layout.Column
20 | import androidx.compose.foundation.layout.fillMaxSize
21 | import androidx.compose.ui.ExperimentalComposeUiApi
22 | import androidx.compose.ui.Modifier
23 | import androidx.compose.ui.window.CanvasBasedWindow
24 | import dev.sergiobelda.compose.vectorize.sample.MainScreen
25 | import org.jetbrains.skiko.wasm.onWasmReady
26 |
27 | @OptIn(ExperimentalComposeUiApi::class)
28 | fun main() {
29 | onWasmReady {
30 | CanvasBasedWindow("Compose Vectorize Sample") {
31 | Column(modifier = Modifier.fillMaxSize()) {
32 | MainScreen()
33 | }
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/sample-mpp/web/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Sergio Belda
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | plugins {
18 | kotlin("multiplatform")
19 | alias(libs.plugins.composeCompiler)
20 | alias(libs.plugins.composeMultiplatform)
21 | id("dev.sergiobelda.gradle.spotless")
22 | }
23 |
24 | group = "dev.sergiobelda.compose.vectorize.sample.web"
25 |
26 | kotlin {
27 | js(IR) {
28 | browser()
29 | binaries.executable()
30 | }
31 |
32 | sourceSets {
33 | val jsMain by getting {
34 | dependencies {
35 | implementation(projects.common)
36 | implementation(compose.foundation)
37 | implementation(compose.ui)
38 | }
39 | }
40 | val jsTest by getting
41 |
42 | all {
43 | languageSettings.optIn("kotlin.RequiresOptIn")
44 | }
45 | }
46 | }
47 |
48 | compose.experimental {
49 | web.application {}
50 | }
51 |
--------------------------------------------------------------------------------
/sample-mpp/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
22 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/sample-mpp/desktop/src/jvmMain/kotlin/dev/sergiobelda/compose/vectorize/sample/desktop/Main.desktop.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Sergio Belda
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package dev.sergiobelda.compose.vectorize.sample.desktop
18 |
19 | import androidx.compose.ui.Alignment
20 | import androidx.compose.ui.unit.DpSize
21 | import androidx.compose.ui.unit.dp
22 | import androidx.compose.ui.window.Window
23 | import androidx.compose.ui.window.WindowPosition
24 | import androidx.compose.ui.window.WindowState
25 | import androidx.compose.ui.window.application
26 | import dev.sergiobelda.compose.vectorize.sample.MainScreen
27 |
28 | fun main() = application {
29 | Window(
30 | resizable = false,
31 | onCloseRequest = ::exitApplication,
32 | title = "Compose Vectorize Sample",
33 | state = WindowState(
34 | size = DpSize(480.dp, 860.dp),
35 | position = WindowPosition.Aligned(Alignment.Center),
36 | ),
37 | ) {
38 | MainScreen()
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/compose-vectorize-gradle-plugin/src/main/kotlin/dev/sergiobelda/compose/vectorize/generator/Image.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Sergio Belda
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package dev.sergiobelda.compose.vectorize.generator
18 |
19 | /**
20 | * Represents a image's Kotlin name, image category name, processed XML file name, and XML file content.
21 | *
22 | * The [kotlinName] is typically the PascalCase equivalent of the original image name, with the
23 | * caveat that images starting with a number are prefixed with an underscore.
24 | *
25 | * @property kotlinName the name of the generated Kotlin property, for example `ArrowBack`.
26 | * @property categoryName the image category name, for example `Icons`.
27 | * @property xmlFileName the name of the processed XML file.
28 | * @property fileContent the content of the source XML file that will be parsed.
29 | */
30 | data class Image(
31 | val kotlinName: String,
32 | val packageName: String,
33 | val categoryName: String,
34 | val xmlFileName: String,
35 | val fileContent: String,
36 | )
37 |
--------------------------------------------------------------------------------
/mkdocs.yml:
--------------------------------------------------------------------------------
1 | site_name: Compose Vectorize
2 | site_description: A Kotlin Multiplatform library to generate compose.ui.graphics.vector.ImageVector from .xml files.
3 | site_author: 'Sergio Belda'
4 | site_url: 'https://github.com/serbelga/compose-vectorize'
5 | edit_uri: 'tree/main/docs/'
6 | remote_branch: gh-pages
7 |
8 | repo_name: 'compose-vectorize'
9 | repo_url: 'https://github.com/serbelga/compose-vectorize'
10 |
11 | docs_dir: docs
12 |
13 | nav:
14 | - 'Overview': 'index.md'
15 | - 'Usage': 'usage.md'
16 | - 'Themed Attributes': 'themed-attributes.md'
17 |
18 | theme:
19 | name: 'material'
20 | palette:
21 | - media: "(prefers-color-scheme: light)"
22 | scheme: default
23 | toggle:
24 | icon: material/brightness-4
25 | name: Switch to dark mode
26 | primary: white
27 | - media: "(prefers-color-scheme: dark)"
28 | scheme: slate
29 | toggle:
30 | icon: material/brightness-5
31 | name: Switch to light mode
32 | primary: black
33 | icon:
34 | logo: material/compare
35 | font:
36 | text: 'Manrope'
37 | code: 'JetBrains Mono'
38 | features:
39 | - content.code.annotate
40 | - content.code.copy
41 | - content.code.select
42 |
43 | markdown_extensions:
44 | - admonition
45 | - pymdownx.details
46 | - pymdownx.superfences
47 | - pymdownx.highlight:
48 | anchor_linenums: true
49 | line_spans: __span
50 | pygments_lang_class: true
51 | - pymdownx.inlinehilite
52 | - pymdownx.snippets
53 | - pymdownx.superfences
54 | - pymdownx.emoji:
55 | emoji_index: !!python/name:material.extensions.emoji.twemoji
56 | emoji_generator: !!python/name:material.extensions.emoji.to_svg
--------------------------------------------------------------------------------
/compose-vectorize-gradle-plugin/src/main/kotlin/dev/sergiobelda/compose/vectorize/generator/utils/ProcessorUtils.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Sergio Belda
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package dev.sergiobelda.compose.vectorize.generator.utils
18 |
19 | import org.gradle.kotlin.dsl.support.uppercaseFirstChar
20 |
21 | /**
22 | * Converts a snake_case name to a KotlinProperty name.
23 | *
24 | * If the first character of [this] is a digit, the resulting name will be prefixed with an `_`
25 | */
26 | internal fun String.toKotlinPropertyName(): String {
27 | // Replace any "_" or "-" followed by a letter or digit with the uppercase letter.
28 | // e.g. "_a" -> "A", "--a" -> "A", "__1" -> "_1", "--1" -> "_1"
29 | val pattern = "[_-]+[a-z0-9]".toRegex()
30 | val name = replace(pattern) { match ->
31 | if (match.value.last().isDigit()) {
32 | "_${match.value.last()}"
33 | } else {
34 | match.value.last().uppercase()
35 | }
36 | }
37 |
38 | // If the first character is a digit, prefix the name with an "_".
39 | return name.let {
40 | if (it.firstOrNull()?.isDigit() == true) "_$it" else it.uppercaseFirstChar()
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/sample-mpp/desktop/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Sergio Belda
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import org.jetbrains.compose.desktop.application.dsl.TargetFormat
18 |
19 | plugins {
20 | kotlin("multiplatform")
21 | alias(libs.plugins.composeCompiler)
22 | alias(libs.plugins.composeMultiplatform)
23 | id("dev.sergiobelda.gradle.spotless")
24 | }
25 |
26 | kotlin {
27 | jvm {
28 | compilations.all {
29 | kotlin {
30 | jvmToolchain(17)
31 |
32 | compilerOptions {
33 | optIn.add("kotlin.RequiresOptIn")
34 | }
35 | }
36 | }
37 | }
38 | sourceSets {
39 | val jvmMain by getting {
40 | dependencies {
41 | implementation(projects.common)
42 | implementation(compose.desktop.currentOs)
43 | }
44 | }
45 | val jvmTest by getting
46 | }
47 | }
48 |
49 | compose.desktop {
50 | application {
51 | mainClass = "dev.sergiobelda.compose.vectorize.sample.desktop.Main_desktopKt"
52 | nativeDistributions {
53 | targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/sample/app/src/main/res/drawable/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/sample/app/src/main/java/dev/sergiobelda/compose/vectorize/ui/theme/Type.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Sergio Belda
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package dev.sergiobelda.compose.vectorize.ui.theme
18 |
19 | import androidx.compose.material3.Typography
20 | import androidx.compose.ui.text.TextStyle
21 | import androidx.compose.ui.text.font.FontFamily
22 | import androidx.compose.ui.text.font.FontWeight
23 | import androidx.compose.ui.unit.sp
24 |
25 | // Set of Material typography styles to start with
26 | val Typography = Typography(
27 | bodyLarge = TextStyle(
28 | fontFamily = FontFamily.Default,
29 | fontWeight = FontWeight.Normal,
30 | fontSize = 16.sp,
31 | lineHeight = 24.sp,
32 | letterSpacing = 0.5.sp,
33 | ),
34 | /* Other default text styles to override
35 | titleLarge = TextStyle(
36 | fontFamily = FontFamily.Default,
37 | fontWeight = FontWeight.Normal,
38 | fontSize = 22.sp,
39 | lineHeight = 28.sp,
40 | letterSpacing = 0.sp
41 | ),
42 | labelSmall = TextStyle(
43 | fontFamily = FontFamily.Default,
44 | fontWeight = FontWeight.Medium,
45 | fontSize = 11.sp,
46 | lineHeight = 16.sp,
47 | letterSpacing = 0.5.sp
48 | )
49 | */
50 | )
51 |
--------------------------------------------------------------------------------
/sample-mpp/android/src/main/kotlin/dev/sergiobelda/compose/vectorize/sample/android/ui/theme/Type.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Sergio Belda
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package dev.sergiobelda.compose.vectorize.sample.android.ui.theme
18 |
19 | import androidx.compose.material3.Typography
20 | import androidx.compose.ui.text.TextStyle
21 | import androidx.compose.ui.text.font.FontFamily
22 | import androidx.compose.ui.text.font.FontWeight
23 | import androidx.compose.ui.unit.sp
24 |
25 | // Set of Material typography styles to start with
26 | val Typography = Typography(
27 | bodyLarge = TextStyle(
28 | fontFamily = FontFamily.Default,
29 | fontWeight = FontWeight.Normal,
30 | fontSize = 16.sp,
31 | lineHeight = 24.sp,
32 | letterSpacing = 0.5.sp,
33 | ),
34 | /* Other default text styles to override
35 | titleLarge = TextStyle(
36 | fontFamily = FontFamily.Default,
37 | fontWeight = FontWeight.Normal,
38 | fontSize = 22.sp,
39 | lineHeight = 28.sp,
40 | letterSpacing = 0.sp
41 | ),
42 | labelSmall = TextStyle(
43 | fontFamily = FontFamily.Default,
44 | fontWeight = FontWeight.Medium,
45 | fontSize = 11.sp,
46 | lineHeight = 16.sp,
47 | letterSpacing = 0.5.sp
48 | )
49 | */
50 | )
51 |
--------------------------------------------------------------------------------
/compose-vectorize-gradle-plugin/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Sergio Belda
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | plugins {
18 | `kotlin-dsl`
19 | alias(libs.plugins.gradlePublish)
20 | id("dev.sergiobelda.gradle.spotless")
21 | }
22 |
23 | group = "dev.sergiobelda.compose.vectorize"
24 | version = libs.versions.composeVectorize.get()
25 |
26 | java {
27 | toolchain {
28 | languageVersion = JavaLanguageVersion.of(17)
29 | }
30 | }
31 |
32 | dependencies {
33 | implementation(libs.android.gradlePlugin)
34 | implementation(libs.kotlin.gradlePlugin)
35 | implementation(libs.squareup.kotlinpoet)
36 | implementation(libs.xmlpull)
37 | implementation(libs.xpp3)
38 | }
39 |
40 | gradlePlugin {
41 | website.set("https://github.com/serbelga/compose-vectorize")
42 | vcsUrl.set("https://github.com/serbelga/compose-vectorize")
43 | plugins {
44 | create("compose-vectorize") {
45 | id = "dev.sergiobelda.compose.vectorize"
46 | implementationClass =
47 | "dev.sergiobelda.compose.vectorize.gradle.plugin.ImageVectorGenerationPlugin"
48 | displayName = "Compose Vectorize"
49 | description = "Gradle plugin to generate ImageVectors from XML files."
50 | tags.set(listOf("imagevector", "compose", "xml", "vector"))
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/compose-vectorize-gradle-plugin/src/main/kotlin/dev/sergiobelda/compose/vectorize/generator/ImageWriter.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Sergio Belda
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package dev.sergiobelda.compose.vectorize.generator
18 |
19 | import dev.sergiobelda.compose.vectorize.generator.imageparser.ImageParser
20 | import java.io.File
21 |
22 | /**
23 | * Generates programmatic representation of all [images] using [ImageVectorGenerator].
24 | *
25 | * @property images the list of [Image]s to generate Kotlin files for
26 | */
27 | class ImageWriter(private val images: List) {
28 | /**
29 | * Generates images and writes them to [outputSrcDirectory].
30 | *
31 | * @param outputSrcDirectory the directory to generate source files in
32 | */
33 | fun generateTo(
34 | outputSrcDirectory: File,
35 | ) {
36 | images.forEach { image ->
37 | if (image.fileContent.isNotEmpty()) {
38 | val vector = ImageParser(image).parse()
39 |
40 | val fileSpec = ImageVectorGenerator(
41 | image.kotlinName,
42 | image.packageName,
43 | image.categoryName,
44 | vector,
45 | ).createFileSpec()
46 |
47 | fileSpec.writeTo(outputSrcDirectory)
48 | }
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/sample/app/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.android.application")
3 | id("org.jetbrains.kotlin.android")
4 | alias(libs.plugins.composeCompiler)
5 | id("dev.sergiobelda.compose.vectorize")
6 | id("dev.sergiobelda.gradle.spotless")
7 | alias(libs.plugins.paparazzi)
8 | }
9 |
10 | group = "dev.sergiobelda.compose.vectorize"
11 |
12 | android {
13 | namespace = "dev.sergiobelda.compose.vectorize"
14 | compileSdk = libs.versions.androidCompileSdk.get().toInt()
15 |
16 | defaultConfig {
17 | applicationId = "dev.sergiobelda.compose.vectorize"
18 | minSdk = libs.versions.androidMinSdk.get().toInt()
19 | targetSdk = libs.versions.androidTargetSdk.get().toInt()
20 | versionCode = 1
21 | versionName = "1.0"
22 |
23 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
24 | vectorDrawables {
25 | useSupportLibrary = true
26 | }
27 | }
28 |
29 | buildTypes {
30 | release {
31 | isMinifyEnabled = false
32 | proguardFiles(
33 | getDefaultProguardFile("proguard-android-optimize.txt"),
34 | "proguard-rules.pro"
35 | )
36 | }
37 | }
38 | kotlin {
39 | jvmToolchain(17)
40 | }
41 | buildFeatures {
42 | compose = true
43 | }
44 | packaging {
45 | resources {
46 | excludes += "/META-INF/{AL2.0,LGPL2.1}"
47 | }
48 | }
49 | }
50 |
51 | dependencies {
52 | implementation("dev.sergiobelda.compose.vectorize:compose-vectorize-core")
53 |
54 | implementation(libs.androidx.activityCompose)
55 | implementation(libs.androidx.coreKtx)
56 | implementation(platform(libs.androidx.compose.composeBom))
57 | implementation(libs.androidx.compose.ui)
58 | implementation(libs.androidx.compose.uiGraphics)
59 | implementation(libs.androidx.compose.uiToolingPreview)
60 | implementation(libs.androidx.compose.material3)
61 | debugImplementation(libs.androidx.compose.uiTooling)
62 | }
63 |
64 | composeVectorize {
65 | packageName = "dev.sergiobelda.compose.vectorize.sample.common.images"
66 | }
67 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | # Overview
2 |
3 | A Kotlin Multiplatform library to generate `compose.ui.graphics.vector.ImageVector` from
4 | `.xml` files. It is built on top of AndroidX `material-icons` vector generator, but it allows transforming not
5 | only solid plain icons but also illustrations and creates categories for resources automatically based on folder structure.
6 |
7 |
8 |
9 | ## Download
10 |
11 | [](https://search.maven.org/search?q=g:dev.sergiobelda.compose.vectorize)
12 |
13 | ### Android
14 |
15 | ```kotlin
16 | plugins {
17 | id("com.android.application")
18 | id("org.jetbrains.kotlin.android")
19 | id("dev.sergiobelda.compose.vectorize") version "$VERSION"
20 | }
21 |
22 | dependencies {
23 | implementation("dev.sergiobelda.compose.vectorize:compose-vectorize-core:$VERSION")
24 | }
25 | ```
26 |
27 | ### Multiplatform
28 |
29 | ```kotlin
30 | plugins {
31 | kotlin("multiplatform")
32 | id("org.jetbrains.compose")
33 | id("dev.sergiobelda.compose.vectorize") version "$VERSION"
34 | }
35 |
36 | kotlin {
37 | sourceSets {
38 | val commonMain by getting {
39 | dependencies {
40 | implementation(compose.ui)
41 | implementation("dev.sergiobelda.compose.vectorize:compose-vectorize-core:$VERSION")
42 | }
43 | }
44 | }
45 | }
46 | ```
47 |
48 | ## License
49 |
50 | ```
51 | Copyright 2024 Sergio Belda
52 |
53 | Licensed under the Apache License, Version 2.0 (the "License");
54 | you may not use this file except in compliance with the License.
55 | You may obtain a copy of the License at
56 |
57 | http://www.apache.org/licenses/LICENSE-2.0
58 |
59 | Unless required by applicable law or agreed to in writing, software
60 | distributed under the License is distributed on an "AS IS" BASIS,
61 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
62 | See the License for the specific language governing permissions and
63 | limitations under the License.
64 | ```
--------------------------------------------------------------------------------
/sample/app/src/test/kotlin/dev/sergiobelda/compose/vectorize/ScreenshotTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Sergio Belda
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package dev.sergiobelda.compose.vectorize
18 |
19 | import androidx.compose.foundation.Image
20 | import androidx.compose.foundation.layout.size
21 | import androidx.compose.ui.Modifier
22 | import androidx.compose.ui.unit.dp
23 | import app.cash.paparazzi.Paparazzi
24 | import com.android.ide.common.rendering.api.SessionParams
25 | import dev.sergiobelda.compose.vectorize.sample.common.images.Images
26 | import dev.sergiobelda.compose.vectorize.sample.common.images.illustrations.ComposeMultiplatform
27 | import dev.sergiobelda.compose.vectorize.sample.common.images.illustrations.SwipeOptions
28 | import org.junit.Rule
29 | import org.junit.Test
30 |
31 | class ScreenshotTest {
32 | @get:Rule
33 | val paparazzi = Paparazzi(
34 | theme = "android:Theme.Material.Light.NoActionBar",
35 | renderingMode = SessionParams.RenderingMode.SHRINK,
36 | )
37 |
38 | @Test
39 | fun vectorWithStrokeAttribute() {
40 | paparazzi.snapshot {
41 | Image(
42 | imageVector = Images.Illustrations.ComposeMultiplatform,
43 | contentDescription = null,
44 | modifier = Modifier.size(120.dp),
45 | )
46 | }
47 | }
48 |
49 | @Test
50 | fun vectorWithThemedAttributes() {
51 | paparazzi.snapshot {
52 | Image(
53 | imageVector = Images.Illustrations.SwipeOptions,
54 | contentDescription = null,
55 | modifier = Modifier.size(120.dp),
56 | )
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/compose-vectorize-gradle-plugin/src/main/kotlin/dev/sergiobelda/compose/vectorize/generator/ImageCategoriesGenerator.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Sergio Belda
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package dev.sergiobelda.compose.vectorize.generator
18 |
19 | import com.squareup.kotlinpoet.FileSpec
20 | import com.squareup.kotlinpoet.TypeSpec
21 | import dev.sergiobelda.compose.vectorize.generator.utils.toKotlinPropertyName
22 | import java.io.File
23 |
24 | class ImageCategoriesGenerator(
25 | private val imageCategoriesPackageName: String,
26 | private val categories: List,
27 | ) {
28 |
29 | fun createFileSpec(): FileSpec {
30 | val builder = createImagesFileSpecBuilder()
31 | val imagesType = TypeSpec.objectBuilder(Images)
32 | imagesType.addImagesCategoriesTypeSpec(
33 | categories,
34 | )
35 | builder.addType(imagesType.build())
36 | return builder.build()
37 | }
38 |
39 | private fun createImagesFileSpecBuilder(): FileSpec.Builder {
40 | return FileSpec.builder(
41 | packageName = imageCategoriesPackageName,
42 | fileName = Images,
43 | )
44 | }
45 |
46 | // TODO: Rename
47 | private fun TypeSpec.Builder.addImagesCategoriesTypeSpec(files: List): TypeSpec.Builder {
48 | files.forEach { file ->
49 | if (file.isDirectory) {
50 | val type = TypeSpec.objectBuilder(file.name.toKotlinPropertyName())
51 | type.addImagesCategoriesTypeSpec(
52 | file.listFiles()?.toList() ?: emptyList(),
53 | )
54 | addType(type.build())
55 | }
56 | }
57 | return this
58 | }
59 |
60 | private companion object {
61 | // TODO: Make Images string dynamic.
62 | const val Images = "Images"
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/sample-mpp/android/src/main/res/drawable/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
13 |
19 |
20 |
21 |
27 |
30 |
33 |
34 |
35 |
36 |
42 |
43 |
--------------------------------------------------------------------------------
/compose-vectorize-core/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import com.vanniktech.maven.publish.SonatypeHost
2 |
3 | /*
4 | * Copyright 2024 Sergio Belda
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | plugins {
20 | kotlin("multiplatform")
21 | alias(libs.plugins.composeCompiler)
22 | alias(libs.plugins.composeMultiplatform)
23 | alias(libs.plugins.androidLibrary)
24 | alias(libs.plugins.vanniktechMavenPublish)
25 | id("dev.sergiobelda.gradle.spotless")
26 | }
27 |
28 | group = "dev.sergiobelda.compose.vectorize"
29 | version = libs.versions.composeVectorize.get()
30 |
31 | kotlin {
32 | androidTarget()
33 | jvm("desktop")
34 | iosX64()
35 | iosArm64()
36 | iosSimulatorArm64()
37 | js(IR) {
38 | browser()
39 | binaries.executable()
40 | }
41 |
42 | sourceSets {
43 | val commonMain by getting {
44 | dependencies {
45 | implementation(compose.ui)
46 | }
47 | }
48 | val commonTest by getting
49 | val androidMain by getting
50 | val androidUnitTest by getting
51 | val desktopMain by getting
52 | val desktopTest by getting
53 | val iosX64Main by getting
54 | val iosArm64Main by getting
55 | val iosSimulatorArm64Main by getting
56 | val iosMain by creating
57 | val iosTest by creating
58 | val jsMain by getting
59 | val jsTest by getting
60 |
61 | all {
62 | languageSettings.optIn("kotlin.RequiresOptIn")
63 | }
64 | }
65 | }
66 |
67 | android {
68 | namespace = "dev.sergiobelda.compose.vectorize.core"
69 | compileSdk = libs.versions.androidCompileSdk.get().toInt()
70 |
71 | defaultConfig {
72 | minSdk = libs.versions.androidMinSdk.get().toInt()
73 | }
74 |
75 | kotlin {
76 | jvmToolchain(17)
77 | }
78 | }
79 |
80 | mavenPublishing {
81 | publishToMavenCentral(true)
82 |
83 | signAllPublications()
84 | }
85 |
--------------------------------------------------------------------------------
/sample-mpp/android/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Sergio Belda
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | plugins {
18 | alias(libs.plugins.androidApplication)
19 | alias(libs.plugins.composeCompiler)
20 | alias(libs.plugins.kotlin)
21 | id("dev.sergiobelda.gradle.spotless")
22 | }
23 |
24 | android {
25 | namespace = "dev.sergiobelda.compose.vectorize.sample.android"
26 | compileSdk = libs.versions.androidCompileSdk.get().toInt()
27 |
28 | defaultConfig {
29 | applicationId = "dev.sergiobelda.compose.vectorize.sample.android"
30 | minSdk = libs.versions.androidMinSdk.get().toInt()
31 | targetSdk = libs.versions.androidTargetSdk.get().toInt()
32 | versionCode = 1
33 | versionName = "1.0"
34 |
35 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
36 | vectorDrawables {
37 | useSupportLibrary = true
38 | }
39 | }
40 |
41 | buildTypes {
42 | release {
43 | isMinifyEnabled = false
44 | proguardFiles(
45 | getDefaultProguardFile("proguard-android-optimize.txt"),
46 | "proguard-rules.pro"
47 | )
48 | }
49 | }
50 | kotlin {
51 | jvmToolchain(17)
52 | }
53 | buildFeatures {
54 | compose = true
55 | }
56 | packaging {
57 | resources {
58 | excludes += "/META-INF/{AL2.0,LGPL2.1}"
59 | }
60 | }
61 | }
62 |
63 | dependencies {
64 | implementation(projects.common)
65 |
66 | implementation(libs.androidx.activityCompose)
67 | implementation(libs.androidx.coreKtx)
68 | implementation(platform(libs.androidx.compose.composeBom))
69 | implementation(libs.androidx.compose.ui)
70 | implementation(libs.androidx.compose.uiGraphics)
71 | implementation(libs.androidx.compose.uiToolingPreview)
72 | implementation(libs.androidx.compose.material3)
73 | debugImplementation(libs.androidx.compose.uiTooling)
74 | }
75 |
--------------------------------------------------------------------------------
/gradle/libs.versions.toml:
--------------------------------------------------------------------------------
1 | [versions]
2 | activityCompose = "1.11.0"
3 | androidCompileSdk = "36"
4 | androidCore = "1.17.0"
5 | androidMinSdk = "24"
6 | androidTargetSdk = "36"
7 | androidGradlePlugin = "8.13.0"
8 | composeBom = "2025.11.00"
9 | composeMultiplatform = "1.9.3"
10 | composeVectorize = "1.1.0"
11 | gradlePublishGradlePlugin = "2.0.0"
12 | kotlin = "2.2.21"
13 | kotlinpoet = "2.2.0"
14 | ktlint = "0.50.0"
15 | paparazzi = "2.0.0-alpha02"
16 | spotless = "8.0.0"
17 | vanniktechMavenPublishPlugin = "0.33.0"
18 |
19 | [libraries]
20 | android-gradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" }
21 |
22 | androidx-activityCompose = { module = "androidx.activity:activity-compose", version.ref = "activityCompose" }
23 | androidx-compose-composeBom = { module = "androidx.compose:compose-bom", version.ref = "composeBom" }
24 | androidx-compose-material3 = { module = "androidx.compose.material3:material3" }
25 | androidx-compose-ui = { module = "androidx.compose.ui:ui" }
26 | androidx-compose-uiGraphics = { module = "androidx.compose.ui:ui-graphics" }
27 | androidx-compose-uiTooling = { module = "androidx.compose.ui:ui-tooling" }
28 | androidx-compose-uiToolingPreview = { module = "androidx.compose.ui:ui-tooling-preview" }
29 | androidx-coreKtx = { module = "androidx.core:core-ktx", version.ref = "androidCore" }
30 |
31 | kotlin-gradlePlugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
32 |
33 | spotless-gradlePlugin = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless" }
34 |
35 | squareup-kotlinpoet = { module = "com.squareup:kotlinpoet", version.ref = "kotlinpoet" }
36 |
37 | xpp3 = { module = "xpp3:xpp3", version = "1.1.4c" }
38 | xmlpull = { module = "xmlpull:xmlpull", version = "1.1.3.4a" }
39 |
40 | [plugins]
41 | androidApplication = { id = "com.android.application", version.ref = "androidGradlePlugin" }
42 | androidLibrary = { id = "com.android.library", version.ref = "androidGradlePlugin" }
43 | composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
44 | composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "composeMultiplatform" }
45 | gradlePublish = { id = "com.gradle.plugin-publish", version.ref = "gradlePublishGradlePlugin" }
46 | kotlin = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
47 | paparazzi = { id = "app.cash.paparazzi", version.ref = "paparazzi" }
48 | spotless = { id = "com.diffplug.spotless", version.ref = "spotless" }
49 | vanniktechMavenPublish = { id = "com.vanniktech.maven.publish", version.ref = "vanniktechMavenPublishPlugin" }
50 |
--------------------------------------------------------------------------------
/sample-mpp/common/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Sergio Belda
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | plugins {
18 | kotlin("multiplatform")
19 | alias(libs.plugins.androidLibrary)
20 | alias(libs.plugins.composeCompiler)
21 | alias(libs.plugins.composeMultiplatform)
22 | id("dev.sergiobelda.compose.vectorize")
23 | id("dev.sergiobelda.gradle.spotless")
24 | }
25 |
26 | group = "dev.sergiobelda.compose.vectorize.sample.common"
27 |
28 | kotlin {
29 | androidTarget()
30 | jvm("desktop")
31 | listOf(
32 | iosX64(),
33 | iosArm64(),
34 | iosSimulatorArm64()
35 | ).forEach { iosTarget ->
36 | iosTarget.binaries.framework {
37 | baseName = "common"
38 | isStatic = true
39 | }
40 | }
41 | js(IR) {
42 | browser()
43 | binaries.executable()
44 | }
45 |
46 | sourceSets {
47 | val commonMain by getting {
48 | dependencies {
49 | implementation(compose.material3)
50 | implementation(compose.ui)
51 | implementation("dev.sergiobelda.compose.vectorize:compose-vectorize-core")
52 | }
53 | }
54 | val commonTest by getting
55 | val androidMain by getting
56 | val androidUnitTest by getting
57 | val desktopMain by getting
58 | val desktopTest by getting
59 | val iosMain by creating
60 | val iosTest by creating
61 | val jsMain by getting
62 | val jsTest by getting
63 |
64 | all {
65 | languageSettings.optIn("kotlin.RequiresOptIn")
66 | }
67 | }
68 | }
69 |
70 | android {
71 | namespace = "dev.sergiobelda.compose.vectorize.sample.common"
72 | compileSdk = libs.versions.androidCompileSdk.get().toInt()
73 |
74 | defaultConfig {
75 | minSdk = libs.versions.androidMinSdk.get().toInt()
76 | }
77 |
78 | kotlin {
79 | jvmToolchain(17)
80 | }
81 | }
82 |
83 | composeVectorize {
84 | packageName = "dev.sergiobelda.compose.vectorize.sample.common.images"
85 | }
86 |
--------------------------------------------------------------------------------
/sample/app/src/main/java/dev/sergiobelda/compose/vectorize/ui/theme/Theme.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Sergio Belda
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package dev.sergiobelda.compose.vectorize.ui.theme
18 |
19 | import android.app.Activity
20 | import android.os.Build
21 | import androidx.compose.foundation.isSystemInDarkTheme
22 | import androidx.compose.material3.MaterialTheme
23 | import androidx.compose.material3.darkColorScheme
24 | import androidx.compose.material3.dynamicDarkColorScheme
25 | import androidx.compose.material3.dynamicLightColorScheme
26 | import androidx.compose.material3.lightColorScheme
27 | import androidx.compose.runtime.Composable
28 | import androidx.compose.runtime.SideEffect
29 | import androidx.compose.ui.graphics.toArgb
30 | import androidx.compose.ui.platform.LocalContext
31 | import androidx.compose.ui.platform.LocalView
32 | import androidx.core.view.WindowCompat
33 |
34 | private val DarkColorScheme = darkColorScheme()
35 |
36 | private val LightColorScheme = lightColorScheme()
37 |
38 | @Composable
39 | fun SampleTheme(
40 | darkTheme: Boolean = isSystemInDarkTheme(),
41 | // Dynamic color is available on Android 12+
42 | dynamicColor: Boolean = true,
43 | content: @Composable () -> Unit,
44 | ) {
45 | val colorScheme = when {
46 | dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
47 | val context = LocalContext.current
48 | if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
49 | }
50 |
51 | darkTheme -> DarkColorScheme
52 | else -> LightColorScheme
53 | }
54 | val view = LocalView.current
55 | if (!view.isInEditMode) {
56 | SideEffect {
57 | val window = (view.context as Activity).window
58 | window.statusBarColor = colorScheme.primary.toArgb()
59 | WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
60 | }
61 | }
62 |
63 | MaterialTheme(
64 | colorScheme = colorScheme,
65 | typography = Typography,
66 | content = content,
67 | )
68 | }
69 |
--------------------------------------------------------------------------------
/compose-vectorize-gradle-plugin/src/main/kotlin/dev/sergiobelda/compose/vectorize/generator/imageparser/VectorColorParser.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Sergio Belda
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package dev.sergiobelda.compose.vectorize.generator.imageparser
18 |
19 | import dev.sergiobelda.compose.vectorize.generator.vector.VectorColor
20 |
21 | /**
22 | * Process a color depending on its format:
23 | * - If is Hexadecimal, it is converted to a ARGB [VectorColor.Hexadecimal].
24 | * - Is is an Android Attribute, e.g. "?attr/colorPrimary",
25 | * it is converted to a readable [VectorColor.Attribute] by the Image Vector Generator.
26 | */
27 | internal fun String.colorValueToVectorColor(): VectorColor {
28 | return when {
29 | this.startsWith(HEXADECIMAL_SIGN) -> {
30 | val diff = ARGB_HEXADECIMAL_COLOR_LENGTH - this.length
31 | VectorColor.Hexadecimal(
32 | if (diff > 0) {
33 | // Add as many "F" as needed to complete the hexadecimal color length.
34 | // e.g. "#0A0A0A" -> "#FF0A0A0A"
35 | this.replace(
36 | HEXADECIMAL_SIGN,
37 | "$HEXADECIMAL_SIGN${"F".repeat(diff)}",
38 | ).uppercase()
39 | } else {
40 | this.uppercase()
41 | },
42 | )
43 | }
44 | this.startsWith(ATTRIBUTE_SIGN) -> {
45 | VectorColor.Attribute(
46 | if (this.startsWith(ATTRIBUTE_PREFIX)) {
47 | // Replace the attribute prefix with the attribute indicator.
48 | // e.g. "?attr/colorPrimary" -> "?colorPrimary"
49 | this.replace(ATTRIBUTE_PREFIX, ATTRIBUTE_SIGN)
50 | } else {
51 | this
52 | },
53 | )
54 | }
55 | else -> {
56 | VectorColor.Hexadecimal()
57 | }
58 | }
59 | }
60 |
61 | private const val ARGB_HEXADECIMAL_COLOR_LENGTH = 9
62 | private const val ATTRIBUTE_PREFIX = "?attr/"
63 | private const val ATTRIBUTE_SIGN = "?"
64 | private const val HEXADECIMAL_SIGN = "#"
65 |
--------------------------------------------------------------------------------
/compose-vectorize-gradle-plugin/src/main/kotlin/dev/sergiobelda/compose/vectorize/generator/task/ImageVectorGenerationTask.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Sergio Belda
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package dev.sergiobelda.compose.vectorize.generator.task
18 |
19 | import dev.sergiobelda.compose.vectorize.generator.Image
20 | import dev.sergiobelda.compose.vectorize.generator.ImageCategories
21 | import dev.sergiobelda.compose.vectorize.generator.ImageCategoriesProcessor
22 | import dev.sergiobelda.compose.vectorize.generator.ImageCategoriesWriter
23 | import dev.sergiobelda.compose.vectorize.generator.ImageProcessor
24 | import dev.sergiobelda.compose.vectorize.generator.ImageWriter
25 | import org.gradle.api.DefaultTask
26 | import org.gradle.api.tasks.CacheableTask
27 | import org.gradle.api.tasks.InputFiles
28 | import org.gradle.api.tasks.Internal
29 | import org.gradle.api.tasks.OutputDirectory
30 | import org.gradle.api.tasks.PathSensitive
31 | import org.gradle.api.tasks.PathSensitivity
32 | import org.gradle.api.tasks.TaskAction
33 | import java.io.File
34 |
35 | @CacheableTask
36 | open class ImageVectorGenerationTask : DefaultTask() {
37 |
38 | @PathSensitive(PathSensitivity.RELATIVE)
39 | @InputFiles
40 | val imagesDirectory = project.projectDir.resolve("xml-images")
41 |
42 | @Internal
43 | fun getImageDirectories(): List =
44 | imagesDirectory.listFiles()?.filter { it.isDirectory } ?: emptyList()
45 |
46 | @OutputDirectory
47 | lateinit var buildDirectory: File
48 |
49 | @Internal
50 | lateinit var packageName: String
51 |
52 | @Internal
53 | lateinit var generatedSrcMain: String
54 |
55 | private fun loadImageCategories(): ImageCategories =
56 | ImageCategoriesProcessor(
57 | packageName,
58 | getImageDirectories(),
59 | ).process()
60 |
61 | private fun loadImages(): List =
62 | ImageProcessor(
63 | packageName,
64 | getImageDirectories(),
65 | ).process()
66 |
67 | @get:OutputDirectory
68 | val generatedSrcMainDirectory: File
69 | get() = buildDirectory.resolve(generatedSrcMain)
70 |
71 | @TaskAction
72 | fun run() {
73 | ImageCategoriesWriter(loadImageCategories()).generateTo(generatedSrcMainDirectory)
74 | ImageWriter(loadImages()).generateTo(generatedSrcMainDirectory)
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/compose-vectorize-gradle-plugin/src/main/kotlin/dev/sergiobelda/compose/vectorize/generator/vector/Vector.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Sergio Belda
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package dev.sergiobelda.compose.vectorize.generator.vector
18 |
19 | /**
20 | * Simplified representation of a vector, with root [nodes].
21 | *
22 | * @param nodes may either be a singleton list of the root group, or a list of root paths / groups
23 | * if there are multiple top level declaration
24 | */
25 | class Vector(
26 | val width: String,
27 | val height: String,
28 | val viewportWidth: Float,
29 | val viewportHeight: Float,
30 | val autoMirror: Boolean,
31 | val nodes: List,
32 | ) {
33 | companion object {
34 | internal const val DefaultAutoMirror = false
35 | internal const val DefaultWidth = "24.0"
36 | internal const val DefaultHeight = "24.0"
37 | internal const val DefaultViewportWidth = 24f
38 | internal const val DefaultViewportHeight = 24f
39 | }
40 | }
41 |
42 | sealed class VectorColor(open val value: String) {
43 | class Attribute(
44 | override val value: String,
45 | ) : VectorColor(value)
46 | class Hexadecimal(
47 | override val value: String = DefaultHexadecimalColor,
48 | ) : VectorColor(value) {
49 | companion object {
50 | private const val DefaultHexadecimalColor = "#FF000000"
51 | }
52 | }
53 | }
54 |
55 | sealed class VectorNode {
56 | class Group(val paths: MutableList = mutableListOf()) : VectorNode()
57 | class Path(
58 | val fillAlpha: Float,
59 | val fillColor: VectorColor?,
60 | val fillType: FillType,
61 | val nodes: List,
62 | val strokeAlpha: Float,
63 | val strokeCap: StrokeCap,
64 | val strokeColor: VectorColor?,
65 | val strokeLineJoin: StrokeJoin,
66 | val strokeLineMiter: Float,
67 | val strokeWidth: Float,
68 | ) : VectorNode() {
69 |
70 | companion object {
71 | const val DefaultFillAlpha = 1.0f
72 | val DefaultFillType = FillType.NonZero
73 | const val DefaultStrokeAlpha = 1.0f
74 | val DefaultStrokeCap = StrokeCap.Butt
75 | val DefaultStrokeLineJoin = StrokeJoin.Miter
76 | const val DefaultStrokeLineMiter = 4.0f
77 | const val DefaultStrokeWidth = 0.0f
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/sample-mpp/android/src/main/kotlin/dev/sergiobelda/compose/vectorize/sample/android/ui/theme/Theme.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Sergio Belda
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package dev.sergiobelda.compose.vectorize.sample.android.ui.theme
18 |
19 | import android.app.Activity
20 | import android.os.Build
21 | import androidx.compose.foundation.isSystemInDarkTheme
22 | import androidx.compose.material3.MaterialTheme
23 | import androidx.compose.material3.darkColorScheme
24 | import androidx.compose.material3.dynamicDarkColorScheme
25 | import androidx.compose.material3.dynamicLightColorScheme
26 | import androidx.compose.material3.lightColorScheme
27 | import androidx.compose.runtime.Composable
28 | import androidx.compose.runtime.SideEffect
29 | import androidx.compose.ui.graphics.toArgb
30 | import androidx.compose.ui.platform.LocalContext
31 | import androidx.compose.ui.platform.LocalView
32 | import androidx.core.view.WindowCompat
33 |
34 | private val DarkColorScheme = darkColorScheme(
35 | primary = Purple80,
36 | secondary = PurpleGrey80,
37 | tertiary = Pink80,
38 | )
39 |
40 | private val LightColorScheme = lightColorScheme(
41 | primary = Purple40,
42 | secondary = PurpleGrey40,
43 | tertiary = Pink40,
44 |
45 | /* Other default colors to override
46 | background = Color(0xFFFFFBFE),
47 | surface = Color(0xFFFFFBFE),
48 | onPrimary = Color.White,
49 | onSecondary = Color.White,
50 | onTertiary = Color.White,
51 | onBackground = Color(0xFF1C1B1F),
52 | onSurface = Color(0xFF1C1B1F),
53 | */
54 | )
55 |
56 | @Composable
57 | fun SampleTheme(
58 | darkTheme: Boolean = isSystemInDarkTheme(),
59 | // Dynamic color is available on Android 12+
60 | dynamicColor: Boolean = true,
61 | content: @Composable () -> Unit,
62 | ) {
63 | val colorScheme = when {
64 | dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
65 | val context = LocalContext.current
66 | if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
67 | }
68 |
69 | darkTheme -> DarkColorScheme
70 | else -> LightColorScheme
71 | }
72 | val view = LocalView.current
73 | if (!view.isInEditMode) {
74 | SideEffect {
75 | val window = (view.context as Activity).window
76 | window.statusBarColor = colorScheme.primary.toArgb()
77 | WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
78 | }
79 | }
80 |
81 | MaterialTheme(
82 | colorScheme = colorScheme,
83 | typography = Typography,
84 | content = content,
85 | )
86 | }
87 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 | @rem SPDX-License-Identifier: Apache-2.0
17 | @rem
18 |
19 | @if "%DEBUG%"=="" @echo off
20 | @rem ##########################################################################
21 | @rem
22 | @rem Gradle startup script for Windows
23 | @rem
24 | @rem ##########################################################################
25 |
26 | @rem Set local scope for the variables with windows NT shell
27 | if "%OS%"=="Windows_NT" setlocal
28 |
29 | set DIRNAME=%~dp0
30 | if "%DIRNAME%"=="" set DIRNAME=.
31 | @rem This is normally unused
32 | set APP_BASE_NAME=%~n0
33 | set APP_HOME=%DIRNAME%
34 |
35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
37 |
38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
40 |
41 | @rem Find java.exe
42 | if defined JAVA_HOME goto findJavaFromJavaHome
43 |
44 | set JAVA_EXE=java.exe
45 | %JAVA_EXE% -version >NUL 2>&1
46 | if %ERRORLEVEL% equ 0 goto execute
47 |
48 | echo. 1>&2
49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
50 | echo. 1>&2
51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
52 | echo location of your Java installation. 1>&2
53 |
54 | goto fail
55 |
56 | :findJavaFromJavaHome
57 | set JAVA_HOME=%JAVA_HOME:"=%
58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
59 |
60 | if exist "%JAVA_EXE%" goto execute
61 |
62 | echo. 1>&2
63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
64 | echo. 1>&2
65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
66 | echo location of your Java installation. 1>&2
67 |
68 | goto fail
69 |
70 | :execute
71 | @rem Setup the command line
72 |
73 |
74 |
75 | @rem Execute Gradle
76 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
77 |
78 | :end
79 | @rem End local scope for the variables with windows NT shell
80 | if %ERRORLEVEL% equ 0 goto mainEnd
81 |
82 | :fail
83 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
84 | rem the _cmd.exe /c_ return code!
85 | set EXIT_CODE=%ERRORLEVEL%
86 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
87 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
88 | exit /b %EXIT_CODE%
89 |
90 | :mainEnd
91 | if "%OS%"=="Windows_NT" endlocal
92 |
93 | :omega
94 |
--------------------------------------------------------------------------------
/gradle/build-logic/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 | @rem SPDX-License-Identifier: Apache-2.0
17 | @rem
18 |
19 | @if "%DEBUG%"=="" @echo off
20 | @rem ##########################################################################
21 | @rem
22 | @rem Gradle startup script for Windows
23 | @rem
24 | @rem ##########################################################################
25 |
26 | @rem Set local scope for the variables with windows NT shell
27 | if "%OS%"=="Windows_NT" setlocal
28 |
29 | set DIRNAME=%~dp0
30 | if "%DIRNAME%"=="" set DIRNAME=.
31 | @rem This is normally unused
32 | set APP_BASE_NAME=%~n0
33 | set APP_HOME=%DIRNAME%
34 |
35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
37 |
38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
40 |
41 | @rem Find java.exe
42 | if defined JAVA_HOME goto findJavaFromJavaHome
43 |
44 | set JAVA_EXE=java.exe
45 | %JAVA_EXE% -version >NUL 2>&1
46 | if %ERRORLEVEL% equ 0 goto execute
47 |
48 | echo. 1>&2
49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
50 | echo. 1>&2
51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
52 | echo location of your Java installation. 1>&2
53 |
54 | goto fail
55 |
56 | :findJavaFromJavaHome
57 | set JAVA_HOME=%JAVA_HOME:"=%
58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
59 |
60 | if exist "%JAVA_EXE%" goto execute
61 |
62 | echo. 1>&2
63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
64 | echo. 1>&2
65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
66 | echo location of your Java installation. 1>&2
67 |
68 | goto fail
69 |
70 | :execute
71 | @rem Setup the command line
72 |
73 |
74 |
75 | @rem Execute Gradle
76 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
77 |
78 | :end
79 | @rem End local scope for the variables with windows NT shell
80 | if %ERRORLEVEL% equ 0 goto mainEnd
81 |
82 | :fail
83 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
84 | rem the _cmd.exe /c_ return code!
85 | set EXIT_CODE=%ERRORLEVEL%
86 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
87 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
88 | exit /b %EXIT_CODE%
89 |
90 | :mainEnd
91 | if "%OS%"=="Windows_NT" endlocal
92 |
93 | :omega
94 |
--------------------------------------------------------------------------------
/sample/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 | @rem SPDX-License-Identifier: Apache-2.0
17 | @rem
18 |
19 | @if "%DEBUG%"=="" @echo off
20 | @rem ##########################################################################
21 | @rem
22 | @rem Gradle startup script for Windows
23 | @rem
24 | @rem ##########################################################################
25 |
26 | @rem Set local scope for the variables with windows NT shell
27 | if "%OS%"=="Windows_NT" setlocal
28 |
29 | set DIRNAME=%~dp0
30 | if "%DIRNAME%"=="" set DIRNAME=.
31 | @rem This is normally unused
32 | set APP_BASE_NAME=%~n0
33 | set APP_HOME=%DIRNAME%
34 |
35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
37 |
38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
40 |
41 | @rem Find java.exe
42 | if defined JAVA_HOME goto findJavaFromJavaHome
43 |
44 | set JAVA_EXE=java.exe
45 | %JAVA_EXE% -version >NUL 2>&1
46 | if %ERRORLEVEL% equ 0 goto execute
47 |
48 | echo. 1>&2
49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
50 | echo. 1>&2
51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
52 | echo location of your Java installation. 1>&2
53 |
54 | goto fail
55 |
56 | :findJavaFromJavaHome
57 | set JAVA_HOME=%JAVA_HOME:"=%
58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
59 |
60 | if exist "%JAVA_EXE%" goto execute
61 |
62 | echo. 1>&2
63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
64 | echo. 1>&2
65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
66 | echo location of your Java installation. 1>&2
67 |
68 | goto fail
69 |
70 | :execute
71 | @rem Setup the command line
72 |
73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
74 |
75 |
76 | @rem Execute Gradle
77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
78 |
79 | :end
80 | @rem End local scope for the variables with windows NT shell
81 | if %ERRORLEVEL% equ 0 goto mainEnd
82 |
83 | :fail
84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
85 | rem the _cmd.exe /c_ return code!
86 | set EXIT_CODE=%ERRORLEVEL%
87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
89 | exit /b %EXIT_CODE%
90 |
91 | :mainEnd
92 | if "%OS%"=="Windows_NT" endlocal
93 |
94 | :omega
95 |
--------------------------------------------------------------------------------
/sample-mpp/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 | @rem SPDX-License-Identifier: Apache-2.0
17 | @rem
18 |
19 | @if "%DEBUG%"=="" @echo off
20 | @rem ##########################################################################
21 | @rem
22 | @rem Gradle startup script for Windows
23 | @rem
24 | @rem ##########################################################################
25 |
26 | @rem Set local scope for the variables with windows NT shell
27 | if "%OS%"=="Windows_NT" setlocal
28 |
29 | set DIRNAME=%~dp0
30 | if "%DIRNAME%"=="" set DIRNAME=.
31 | @rem This is normally unused
32 | set APP_BASE_NAME=%~n0
33 | set APP_HOME=%DIRNAME%
34 |
35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
37 |
38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
40 |
41 | @rem Find java.exe
42 | if defined JAVA_HOME goto findJavaFromJavaHome
43 |
44 | set JAVA_EXE=java.exe
45 | %JAVA_EXE% -version >NUL 2>&1
46 | if %ERRORLEVEL% equ 0 goto execute
47 |
48 | echo. 1>&2
49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
50 | echo. 1>&2
51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
52 | echo location of your Java installation. 1>&2
53 |
54 | goto fail
55 |
56 | :findJavaFromJavaHome
57 | set JAVA_HOME=%JAVA_HOME:"=%
58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
59 |
60 | if exist "%JAVA_EXE%" goto execute
61 |
62 | echo. 1>&2
63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
64 | echo. 1>&2
65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
66 | echo location of your Java installation. 1>&2
67 |
68 | goto fail
69 |
70 | :execute
71 | @rem Setup the command line
72 |
73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
74 |
75 |
76 | @rem Execute Gradle
77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
78 |
79 | :end
80 | @rem End local scope for the variables with windows NT shell
81 | if %ERRORLEVEL% equ 0 goto mainEnd
82 |
83 | :fail
84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
85 | rem the _cmd.exe /c_ return code!
86 | set EXIT_CODE=%ERRORLEVEL%
87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
89 | exit /b %EXIT_CODE%
90 |
91 | :mainEnd
92 | if "%OS%"=="Windows_NT" endlocal
93 |
94 | :omega
95 |
--------------------------------------------------------------------------------
/compose-vectorize-gradle-plugin/src/main/kotlin/dev/sergiobelda/compose/vectorize/generator/Names.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Sergio Belda
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package dev.sergiobelda.compose.vectorize.generator
18 |
19 | import com.squareup.kotlinpoet.ClassName
20 | import com.squareup.kotlinpoet.MemberName
21 |
22 | /**
23 | * Package names used for image generation.
24 | */
25 | enum class PackageNames(val packageName: String) {
26 | ComposeVectorizeCore("dev.sergiobelda.compose.vectorize.core"),
27 | GraphicsPackage("androidx.compose.ui.graphics"),
28 | Material3Package("androidx.compose.material3"),
29 | VectorPackage(GraphicsPackage.packageName + ".vector"),
30 | }
31 |
32 | /**
33 | * [ClassName]s used for image generation.
34 | */
35 | object ClassNames {
36 | val ImageVector = PackageNames.VectorPackage.className("ImageVector")
37 |
38 | val PathFillType = PackageNames.GraphicsPackage.className("PathFillType", "Companion")
39 | val StrokeCapType = PackageNames.GraphicsPackage.className("StrokeCap", "Companion")
40 | val StrokeJoinType = PackageNames.GraphicsPackage.className("StrokeJoin", "Companion")
41 |
42 | val MaterialTheme = PackageNames.Material3Package.className("MaterialTheme")
43 | }
44 |
45 | /**
46 | * [ClassName]s used as Annotation for image generation.
47 | */
48 | object AnnotationNames {
49 | val Composable = ClassName("androidx.compose.runtime", "Composable")
50 | }
51 |
52 | /**
53 | * [MemberName]s used for image generation.
54 | */
55 | object MemberNames {
56 | val Material3ColorScheme = MemberName(ClassNames.MaterialTheme, "colorScheme")
57 |
58 | val ImageVector = MemberName(
59 | PackageNames.ComposeVectorizeCore.packageName,
60 | "imageVector",
61 | )
62 |
63 | val Color = MemberName(PackageNames.GraphicsPackage.packageName, "Color")
64 | val SolidColor = MemberName(PackageNames.GraphicsPackage.packageName, "SolidColor")
65 |
66 | val Group = MemberName(PackageNames.VectorPackage.packageName, "group")
67 | val Path = MemberName(PackageNames.VectorPackage.packageName, "path")
68 |
69 | object PathFillType {
70 | val EvenOdd = MemberName(ClassNames.PathFillType, "EvenOdd")
71 | }
72 |
73 | object StrokeCapType {
74 | val Butt = MemberName(ClassNames.StrokeCapType, "Butt")
75 | val Round = MemberName(ClassNames.StrokeCapType, "Round")
76 | val Square = MemberName(ClassNames.StrokeCapType, "Square")
77 | }
78 |
79 | object StrokeJoinType {
80 | val Bevel = MemberName(ClassNames.StrokeJoinType, "Bevel")
81 | val Miter = MemberName(ClassNames.StrokeJoinType, "Miter")
82 | val Round = MemberName(ClassNames.StrokeJoinType, "Round")
83 | }
84 | }
85 |
86 | /**
87 | * @return the [ClassName] of the given [classNames] inside this package.
88 | */
89 | fun PackageNames.className(vararg classNames: String) = ClassName(this.packageName, *classNames)
90 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Compose Vectorize
2 |
3 | [](https://search.maven.org/search?q=g:dev.sergiobelda.compose.vectorize)
4 |
5 | A Kotlin Multiplatform library to generate `compose.ui.graphics.vector.ImageVector` from `.xml` files.
6 |
7 | Visit the [project website](https://sergiobelda.dev/compose-vectorize/) for documentation.
8 |
9 | ## Usage
10 |
11 | Create a folder called `xml-images` in the module folder.
12 |
13 | `:module/xml-images`
14 |
15 | ### Android
16 |
17 |
18 |
19 | ### Multiplatform
20 |
21 |
22 |
23 | ### Generate images
24 |
25 | You can manually generate ImageVector for these XML files by calling `gradle generateImages`. This will create a Kotlin file for each XML file containing the ImageVector in the build folder.
26 |
27 |
28 |
29 |
30 | Now, you can use reference this image in the Compose code:
31 |
32 | ```kotlin
33 | Icon(Images.Icons.Add, contentDescription = null)
34 | ```
35 |
36 | Note that automatically a category called "Icons" has been created. A category is created for each subfolder in `xml-images` folder.
37 |
38 | -------------------
39 |
40 | ## Themed Attributes
41 |
42 | It also supports theme attributes. If you are using theme color attributes, like `?attr/colorPrimary`
43 | or `?attr/colorSecondary`, they can be converted to Compose `MaterialTheme` tokens, which means that
44 | you can update illustration colors based on the token values. It is also compatible with Material3 dynamic colors.
45 |
46 | It also supports theme attributes. If you are using theme color attributes, like `?attr/colorPrimary`
47 | or `?attr/colorSecondary`, they can be converted to Compose `MaterialTheme` tokens, which means that
48 | you can update illustration colors based on the token values. It is also compatible with Material3 dynamic colors.
49 |
50 | ```xml
51 |
54 | ```
55 |
56 |
61 |
62 | > [!NOTE]
63 | > It requires use Compose Material 3 dependency.
64 |
65 | -------------------
66 |
67 | ## License
68 |
69 | ```
70 | Copyright 2024 Sergio Belda
71 |
72 | Licensed under the Apache License, Version 2.0 (the "License");
73 | you may not use this file except in compliance with the License.
74 | You may obtain a copy of the License at
75 |
76 | http://www.apache.org/licenses/LICENSE-2.0
77 |
78 | Unless required by applicable law or agreed to in writing, software
79 | distributed under the License is distributed on an "AS IS" BASIS,
80 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
81 | See the License for the specific language governing permissions and
82 | limitations under the License.
83 | ```
84 |
--------------------------------------------------------------------------------
/compose-vectorize-gradle-plugin/src/main/kotlin/dev/sergiobelda/compose/vectorize/generator/ImageProcessor.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Sergio Belda
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package dev.sergiobelda.compose.vectorize.generator
18 |
19 | import dev.sergiobelda.compose.vectorize.generator.utils.toKotlinPropertyName
20 | import java.io.File
21 | import java.util.Locale
22 |
23 | class ImageProcessor(
24 | private val packageName: String,
25 | private val imagesDirectories: List,
26 | ) {
27 |
28 | fun process(): List = loadImages()
29 |
30 | private fun loadImages(): List {
31 | val images = mutableListOf()
32 | imagesDirectories.forEach { file ->
33 | images.addImages(file)
34 | }
35 |
36 | // Ensure image names are unique when accounting for case insensitive filesystems
37 | images
38 | .groupBy { it.categoryName.plus(it.kotlinName.lowercase(Locale.ROOT)) }
39 | .filter { it.value.size > 1 }
40 | .forEach { entry ->
41 | throw IllegalStateException(
42 | """Found multiple images with the same case-insensitive filename:
43 | | ${entry.value.firstOrNull()?.xmlFileName}. Generating images with the same
44 | | case-insensitive filename will cause issues on devices without
45 | | a case sensitive filesystem (OSX / Windows).
46 | """.trimMargin(),
47 | )
48 | }
49 | return images
50 | }
51 |
52 | private fun MutableList.addImages(
53 | file: File,
54 | categoryPath: String = "",
55 | ) {
56 | if (file.isDirectory) {
57 | val category = categoryPath.plus("${file.name.toKotlinPropertyName()}.")
58 | file.listFiles()?.forEach { child ->
59 | addImages(child, category)
60 | }
61 | } else {
62 | val filename = file.nameWithoutExtension
63 | val kotlinName = filename.toKotlinPropertyName()
64 | val fileContent = file.readText()
65 | add(
66 | Image(
67 | kotlinName = kotlinName,
68 | packageName = packageName,
69 | categoryName = categoryPath.toKotlinPropertyName(),
70 | xmlFileName = filename,
71 | fileContent = processXmlFile(fileContent),
72 | ),
73 | )
74 | }
75 | }
76 | }
77 |
78 | /**
79 | * Processes the given [fileContent] by removing android theme attributes and values.
80 | */
81 | private fun processXmlFile(fileContent: String): String {
82 | // Remove any defined tint for paths that use theme attributes
83 | val tintAttribute = Regex.escape("""android:tint="?attr/colorControlNormal"""")
84 | val tintRegex = """\n.*?$tintAttribute""".toRegex(RegexOption.MULTILINE)
85 |
86 | return fileContent
87 | .replace(tintRegex, "")
88 | // The imported images have white as the default path color, so let's change it to be
89 | // black as is typical on Android.
90 | .replace("@android:color/white", "#FF000000")
91 | }
92 |
--------------------------------------------------------------------------------
/compose-vectorize-gradle-plugin/src/main/kotlin/dev/sergiobelda/compose/vectorize/gradle/plugin/ImageVectorGenerationPlugin.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Sergio Belda
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package dev.sergiobelda.compose.vectorize.gradle.plugin
18 |
19 | import com.android.build.gradle.BaseExtension
20 | import dev.sergiobelda.compose.vectorize.generator.task.ImageVectorGenerationTask
21 | import org.gradle.api.Plugin
22 | import org.gradle.api.Project
23 | import org.gradle.api.provider.Property
24 | import org.gradle.api.tasks.SourceSet
25 | import org.gradle.kotlin.dsl.create
26 | import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
27 | import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
28 | import java.io.File
29 |
30 | interface ImageVectorGenerationPluginExtension {
31 | val packageName: Property
32 | }
33 |
34 | open class ImageVectorGenerationPlugin : Plugin {
35 | override fun apply(project: Project) {
36 | val extension =
37 | project.extensions.create(EXTENSION_PLUGIN_NAME)
38 |
39 | val buildDirectory =
40 | project.layout.buildDirectory.asFile.get().resolve(IMAGES_RELATIVE_PATH)
41 |
42 | val generatedSrcMain = project.generatedSrcMain()
43 |
44 | val task =
45 | project.registerImageVectorGenerationTask(extension, buildDirectory, generatedSrcMain)
46 |
47 | if (project.isMultiplatform) {
48 | val sourceSet = project.multiplatformExtension?.sourceSets?.find {
49 | it.name == KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME
50 | }
51 | val generatedSrcMainDirectory = buildDirectory.resolve(generatedSrcMain)
52 | sourceSet?.kotlin?.srcDir(project.files(generatedSrcMainDirectory).builtBy(task))
53 | } else {
54 | project.tasks
55 | .matching { it.name.startsWith("compile") && it.name.endsWith("Kotlin") }
56 | .configureEach { dependsOn(task) }
57 |
58 | val sourceSet = project.baseExtension?.sourceSets?.find {
59 | it.name == SourceSet.MAIN_SOURCE_SET_NAME
60 | }
61 | val generatedSrcMainDirectory = buildDirectory.resolve(generatedSrcMain)
62 | sourceSet?.kotlin?.srcDir(project.files(generatedSrcMainDirectory).builtBy(task))
63 | }
64 | }
65 |
66 | private fun Project.registerImageVectorGenerationTask(
67 | extension: ImageVectorGenerationPluginExtension,
68 | buildDirectory: File,
69 | generatedSrcMain: String,
70 | ) = tasks.register(TASK_NAME, ImageVectorGenerationTask::class.java) {
71 | this.packageName = extension.packageName.getOrElse(DEFAULT_PACKAGE_NAME)
72 | this.buildDirectory = buildDirectory
73 | this.generatedSrcMain = generatedSrcMain
74 | }
75 |
76 | private companion object {
77 | const val DEFAULT_PACKAGE_NAME = "dev.sergiobelda.compose.vectorize.images"
78 | const val EXTENSION_PLUGIN_NAME = "composeVectorize"
79 | const val IMAGES_RELATIVE_PATH = "images"
80 | const val TASK_NAME = "generateImages"
81 | }
82 | }
83 |
84 | private val Project.isMultiplatform: Boolean
85 | get() = plugins.hasPlugin("org.jetbrains.kotlin.multiplatform")
86 |
87 | private fun Project.generatedSrcMain(): String =
88 | if (isMultiplatform) {
89 | "src/commonMain/kotlin"
90 | } else {
91 | "src/main/kotlin"
92 | }
93 |
94 | private val Project.baseExtension
95 | get() = extensions.findByType(BaseExtension::class.java)
96 |
97 | private val Project.multiplatformExtension
98 | get() = extensions.findByType(KotlinMultiplatformExtension::class.java)
99 |
--------------------------------------------------------------------------------
/gradle/build-logic/convention/src/main/kotlin/dev/sergiobelda/gradle/buildlogic/convention/SpotlessConventionPlugin.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Sergio Belda
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package dev.sergiobelda.gradle.buildlogic.convention
18 |
19 | import com.diffplug.gradle.spotless.SpotlessExtension
20 | import org.gradle.api.Plugin
21 | import org.gradle.api.Project
22 | import org.gradle.api.artifacts.VersionCatalogsExtension
23 | import org.gradle.kotlin.dsl.configure
24 | import org.gradle.kotlin.dsl.getByType
25 |
26 | class SpotlessConventionPlugin : Plugin {
27 | override fun apply(target: Project) {
28 | with(target) {
29 | val libs = extensions.getByType().named("libs")
30 | val ktlintVersion = libs.findVersion("ktlint").get().toString()
31 |
32 | pluginManager.apply("com.diffplug.spotless")
33 |
34 | extensions.configure {
35 | kotlin {
36 | target("**/*.kt")
37 | targetExclude("**/build/**/*.kt")
38 | ktlint(ktlintVersion)
39 | licenseHeader(licenseHeaderKotlin)
40 | }
41 | format("kts") {
42 | target("**/*.kts")
43 | targetExclude("**/build/**/*.kts")
44 | // Look for the first line that doesn't have a block comment (assumed to be the license)
45 | licenseHeader(licenseHeaderKotlin, "(^(?![\\/ ]\\*).*$)")
46 | }
47 | format("xml") {
48 | target("**/*.xml")
49 | targetExclude("**/build/**/*.xml")
50 | // Look for the first XML tag that isn't a comment (")
91 | }
92 |
--------------------------------------------------------------------------------
/sample/app/xml-images/illustrations/compose-multiplatform.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
8 |
10 |
13 |
16 |
19 |
20 |
--------------------------------------------------------------------------------
/sample-mpp/common/xml-images/illustrations/compose-multiplatform.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
8 |
10 |
13 |
16 |
19 |
20 |
--------------------------------------------------------------------------------
/sample-mpp/common/src/commonMain/kotlin/dev/sergiobelda/compose/vectorize/sample/MainScreen.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Sergio Belda
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package dev.sergiobelda.compose.vectorize.sample
18 |
19 | import androidx.compose.foundation.Image
20 | import androidx.compose.foundation.layout.Arrangement
21 | import androidx.compose.foundation.layout.Box
22 | import androidx.compose.foundation.layout.Column
23 | import androidx.compose.foundation.layout.fillMaxSize
24 | import androidx.compose.foundation.layout.fillMaxWidth
25 | import androidx.compose.foundation.layout.padding
26 | import androidx.compose.foundation.layout.size
27 | import androidx.compose.foundation.rememberScrollState
28 | import androidx.compose.foundation.verticalScroll
29 | import androidx.compose.material3.Card
30 | import androidx.compose.material3.Icon
31 | import androidx.compose.material3.Scaffold
32 | import androidx.compose.material3.Text
33 | import androidx.compose.runtime.Composable
34 | import androidx.compose.ui.Alignment
35 | import androidx.compose.ui.Modifier
36 | import androidx.compose.ui.unit.dp
37 | import dev.sergiobelda.compose.vectorize.sample.common.images.Images
38 | import dev.sergiobelda.compose.vectorize.sample.common.images.icons.outlined.Home
39 | import dev.sergiobelda.compose.vectorize.sample.common.images.icons.rounded.ArrowBack
40 | import dev.sergiobelda.compose.vectorize.sample.common.images.icons.rounded.Home
41 | import dev.sergiobelda.compose.vectorize.sample.common.images.illustrations.ComposeMultiplatform
42 | import dev.sergiobelda.compose.vectorize.sample.common.images.illustrations.SwipeOptions
43 |
44 | @Composable
45 | fun MainScreen() {
46 | Scaffold { paddingValues ->
47 | Column(
48 | modifier = Modifier
49 | .fillMaxSize()
50 | .padding(paddingValues)
51 | .verticalScroll(rememberScrollState()),
52 | horizontalAlignment = Alignment.CenterHorizontally,
53 | verticalArrangement = Arrangement.spacedBy(16.dp),
54 | ) {
55 | SampleCard(
56 | title = "Images.Icons.Outlined.Home",
57 | modifier = Modifier.padding(top = 16.dp)
58 | ) {
59 | Icon(
60 | imageVector = Images.Icons.Outlined.Home,
61 | contentDescription = null,
62 | )
63 | }
64 | SampleCard(
65 | title = "Images.Icons.Rounded.Home",
66 | ) {
67 | Icon(
68 | imageVector = Images.Icons.Rounded.Home,
69 | contentDescription = null,
70 | )
71 | }
72 | SampleCard(
73 | title = "Images.Icons.Rounded.ArrowBack",
74 | ) {
75 | Icon(
76 | imageVector = Images.Icons.Rounded.ArrowBack,
77 | contentDescription = null,
78 | )
79 | }
80 | SampleCard(
81 | title = "Images.Illustrations.ComposeMultiplatform",
82 | ) {
83 | Image(
84 | imageVector = Images.Illustrations.ComposeMultiplatform,
85 | contentDescription = null,
86 | modifier = Modifier.size(120.dp),
87 | )
88 | }
89 | SampleCard(
90 | title = "Images.Illustrations.SwipeOptions",
91 | ) {
92 | Image(
93 | imageVector = Images.Illustrations.SwipeOptions,
94 | contentDescription = null,
95 | modifier = Modifier.size(240.dp),
96 | )
97 | }
98 | }
99 | }
100 | }
101 |
102 | @Composable
103 | fun SampleCard(
104 | title: String,
105 | modifier: Modifier = Modifier,
106 | content: @Composable () -> Unit,
107 | ) {
108 | Card(modifier = modifier.fillMaxWidth().padding(horizontal = 12.dp)) {
109 | Column(modifier = Modifier.padding(12.dp)) {
110 | Text(title)
111 | Box(
112 | contentAlignment = Alignment.Center,
113 | modifier = Modifier.fillMaxWidth().padding(12.dp),
114 | ) {
115 | content()
116 | }
117 | }
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/sample/app/src/main/java/dev/sergiobelda/compose/vectorize/MainActivity.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Sergio Belda
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package dev.sergiobelda.compose.vectorize
18 |
19 | import android.os.Bundle
20 | import androidx.activity.ComponentActivity
21 | import androidx.activity.compose.setContent
22 | import androidx.compose.foundation.Image
23 | import androidx.compose.foundation.layout.Arrangement
24 | import androidx.compose.foundation.layout.Box
25 | import androidx.compose.foundation.layout.Column
26 | import androidx.compose.foundation.layout.fillMaxSize
27 | import androidx.compose.foundation.layout.fillMaxWidth
28 | import androidx.compose.foundation.layout.padding
29 | import androidx.compose.foundation.layout.size
30 | import androidx.compose.foundation.rememberScrollState
31 | import androidx.compose.foundation.verticalScroll
32 | import androidx.compose.material3.Card
33 | import androidx.compose.material3.Icon
34 | import androidx.compose.material3.Scaffold
35 | import androidx.compose.material3.Text
36 | import androidx.compose.runtime.Composable
37 | import androidx.compose.ui.Alignment
38 | import androidx.compose.ui.Modifier
39 | import androidx.compose.ui.unit.dp
40 | import dev.sergiobelda.compose.vectorize.sample.common.images.Images
41 | import dev.sergiobelda.compose.vectorize.sample.common.images.icons.outlined.Home
42 | import dev.sergiobelda.compose.vectorize.sample.common.images.icons.rounded.ArrowBack
43 | import dev.sergiobelda.compose.vectorize.sample.common.images.icons.rounded.Home
44 | import dev.sergiobelda.compose.vectorize.sample.common.images.illustrations.ComposeMultiplatform
45 | import dev.sergiobelda.compose.vectorize.sample.common.images.illustrations.SwipeOptions
46 | import dev.sergiobelda.compose.vectorize.ui.theme.SampleTheme
47 |
48 | class MainActivity : ComponentActivity() {
49 | override fun onCreate(savedInstanceState: Bundle?) {
50 | super.onCreate(savedInstanceState)
51 | setContent {
52 | SampleTheme {
53 | MainScreen()
54 | }
55 | }
56 | }
57 | }
58 |
59 | @Composable
60 | fun MainScreen() {
61 | Scaffold { paddingValues ->
62 | Column(
63 | modifier = Modifier
64 | .fillMaxSize()
65 | .padding(paddingValues)
66 | .verticalScroll(rememberScrollState()),
67 | horizontalAlignment = Alignment.CenterHorizontally,
68 | verticalArrangement = Arrangement.spacedBy(16.dp),
69 | ) {
70 | SampleCard(
71 | title = "Images.Icons.Outlined.Home",
72 | modifier = Modifier.padding(top = 16.dp),
73 | ) {
74 | Icon(
75 | imageVector = Images.Icons.Outlined.Home,
76 | contentDescription = null,
77 | )
78 | }
79 | SampleCard(
80 | title = "Images.Icons.Rounded.Home",
81 | ) {
82 | Icon(
83 | imageVector = Images.Icons.Rounded.Home,
84 | contentDescription = null,
85 | )
86 | }
87 | SampleCard(
88 | title = "Images.Icons.Rounded.ArrowBack",
89 | ) {
90 | Icon(
91 | imageVector = Images.Icons.Rounded.ArrowBack,
92 | contentDescription = null,
93 | )
94 | }
95 | SampleCard(
96 | title = "Images.Illustrations.ComposeMultiplatform",
97 | ) {
98 | Image(
99 | imageVector = Images.Illustrations.ComposeMultiplatform,
100 | contentDescription = null,
101 | modifier = Modifier.size(120.dp),
102 | )
103 | }
104 | SampleCard(
105 | title = "Images.Illustrations.SwipeOptions",
106 | ) {
107 | Image(
108 | imageVector = Images.Illustrations.SwipeOptions,
109 | contentDescription = null,
110 | modifier = Modifier.size(240.dp),
111 | )
112 | }
113 | }
114 | }
115 | }
116 |
117 | @Composable
118 | fun SampleCard(
119 | title: String,
120 | modifier: Modifier = Modifier,
121 | content: @Composable () -> Unit,
122 | ) {
123 | Card(modifier = modifier.fillMaxWidth().padding(horizontal = 12.dp)) {
124 | Column(modifier = Modifier.padding(12.dp)) {
125 | Text(title)
126 | Box(
127 | contentAlignment = Alignment.Center,
128 | modifier = Modifier.fillMaxWidth().padding(12.dp),
129 | ) {
130 | content()
131 | }
132 | }
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/sample/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/compose-vectorize-gradle-plugin/src/main/kotlin/dev/sergiobelda/compose/vectorize/generator/vector/PathParser.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Sergio Belda
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package dev.sergiobelda.compose.vectorize.generator.vector
18 |
19 | import kotlin.math.min
20 |
21 | /**
22 | * Trimmed down copy of PathParser that doesn't handle interacting with Paths, and only is
23 | * responsible for parsing path strings.
24 | */
25 | object PathParser {
26 | /**
27 | * Parses the path string to create a collection of PathNode instances with their corresponding
28 | * arguments
29 | * throws an IllegalArgumentException or NumberFormatException if the parameters are invalid
30 | */
31 | fun parsePathString(pathData: String): List {
32 | val nodes = mutableListOf()
33 |
34 | fun addNode(cmd: Char, args: FloatArray) {
35 | nodes.addAll(cmd.toPathNodes(args))
36 | }
37 |
38 | var start = 0
39 | var end = 1
40 | while (end < pathData.length) {
41 | end = nextStart(pathData, end)
42 | val s = pathData.substring(start, end).trim { it <= ' ' }
43 | if (s.isNotEmpty()) {
44 | val args = getFloats(s)
45 | addNode(s[0], args)
46 | }
47 |
48 | start = end
49 | end++
50 | }
51 | if (end - start == 1 && start < pathData.length) {
52 | addNode(pathData[start], FloatArray(0))
53 | }
54 |
55 | return nodes
56 | }
57 |
58 | private fun nextStart(s: String, end: Int): Int {
59 | var index = end
60 | var c: Char
61 |
62 | while (index < s.length) {
63 | c = s[index]
64 | // Note that 'e' or 'E' are not valid path commands, but could be
65 | // used for floating point numbers' scientific notation.
66 | // Therefore, when searching for next command, we should ignore 'e'
67 | // and 'E'.
68 | if (((c - 'A') * (c - 'Z') <= 0 || (c - 'a') * (c - 'z') <= 0) &&
69 | c != 'e' && c != 'E'
70 | ) {
71 | return index
72 | }
73 | index++
74 | }
75 | return index
76 | }
77 |
78 | @Throws(NumberFormatException::class)
79 | private fun getFloats(s: String): FloatArray {
80 | if (s[0] == 'z' || s[0] == 'Z') {
81 | return FloatArray(0)
82 | }
83 | val results = FloatArray(s.length)
84 | var count = 0
85 | var startPosition = 1
86 | var endPosition: Int
87 |
88 | val result =
89 | ExtractFloatResult()
90 | val totalLength = s.length
91 |
92 | // The startPosition should always be the first character of the
93 | // current number, and endPosition is the character after the current
94 | // number.
95 | while (startPosition < totalLength) {
96 | extract(s, startPosition, result)
97 | endPosition = result.endPosition
98 |
99 | if (startPosition < endPosition) {
100 | results[count++] = java.lang.Float.parseFloat(
101 | s.substring(startPosition, endPosition),
102 | )
103 | }
104 |
105 | startPosition = if (result.endWithNegativeOrDot) {
106 | // Keep the '-' or '.' sign with next number.
107 | endPosition
108 | } else {
109 | endPosition + 1
110 | }
111 | }
112 | return copyOfRange(results, 0, count)
113 | }
114 |
115 | private fun copyOfRange(original: FloatArray, start: Int, end: Int): FloatArray {
116 | if (start > end) {
117 | throw IllegalArgumentException()
118 | }
119 | val originalLength = original.size
120 | if (start < 0 || start > originalLength) {
121 | throw ArrayIndexOutOfBoundsException()
122 | }
123 | val resultLength = end - start
124 | val copyLength = min(resultLength, originalLength - start)
125 | val result = FloatArray(resultLength)
126 | original.copyInto(result, 0, start, start + copyLength)
127 | return result
128 | }
129 |
130 | private fun extract(s: String, start: Int, result: ExtractFloatResult) {
131 | // Now looking for ' ', ',', '.' or '-' from the start.
132 | var currentIndex = start
133 | var foundSeparator = false
134 | result.endWithNegativeOrDot = false
135 | var secondDot = false
136 | var isExponential = false
137 | while (currentIndex < s.length) {
138 | val isPrevExponential = isExponential
139 | isExponential = false
140 | when (s[currentIndex]) {
141 | ' ', ',' -> foundSeparator = true
142 | '-' ->
143 | // The negative sign following a 'e' or 'E' is not a separator.
144 | if (currentIndex != start && !isPrevExponential) {
145 | foundSeparator = true
146 | result.endWithNegativeOrDot = true
147 | }
148 |
149 | '.' ->
150 | if (!secondDot) {
151 | secondDot = true
152 | } else {
153 | // This is the second dot, and it is considered as a separator.
154 | foundSeparator = true
155 | result.endWithNegativeOrDot = true
156 | }
157 |
158 | 'e', 'E' -> isExponential = true
159 | }
160 | if (foundSeparator) {
161 | break
162 | }
163 | currentIndex++
164 | }
165 | // When there is nothing found, then we put the end position to the end
166 | // of the string.
167 | result.endPosition = currentIndex
168 | }
169 |
170 | private data class ExtractFloatResult(
171 | // We need to return the position of the next separator and whether the
172 | // next float starts with a '-' or a '.'.
173 | var endPosition: Int = 0,
174 | var endWithNegativeOrDot: Boolean = false,
175 | )
176 | }
177 |
--------------------------------------------------------------------------------
/sample-mpp/android/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
13 |
18 |
21 |
26 |
31 |
36 |
41 |
46 |
51 |
56 |
61 |
66 |
71 |
76 |
81 |
86 |
91 |
96 |
101 |
106 |
111 |
116 |
121 |
126 |
131 |
136 |
141 |
146 |
151 |
156 |
161 |
166 |
171 |
176 |
181 |
182 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 | #
20 |
21 | ##############################################################################
22 | #
23 | # Gradle start up script for POSIX generated by Gradle.
24 | #
25 | # Important for running:
26 | #
27 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
28 | # noncompliant, but you have some other compliant shell such as ksh or
29 | # bash, then to run this script, type that shell name before the whole
30 | # command line, like:
31 | #
32 | # ksh Gradle
33 | #
34 | # Busybox and similar reduced shells will NOT work, because this script
35 | # requires all of these POSIX shell features:
36 | # * functions;
37 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
38 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
39 | # * compound commands having a testable exit status, especially «case»;
40 | # * various built-in commands including «command», «set», and «ulimit».
41 | #
42 | # Important for patching:
43 | #
44 | # (2) This script targets any POSIX shell, so it avoids extensions provided
45 | # by Bash, Ksh, etc; in particular arrays are avoided.
46 | #
47 | # The "traditional" practice of packing multiple parameters into a
48 | # space-separated string is a well documented source of bugs and security
49 | # problems, so this is (mostly) avoided, by progressively accumulating
50 | # options in "$@", and eventually passing that to Java.
51 | #
52 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
53 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
54 | # see the in-line comments for details.
55 | #
56 | # There are tweaks for specific operating systems such as AIX, CygWin,
57 | # Darwin, MinGW, and NonStop.
58 | #
59 | # (3) This script is generated from the Groovy template
60 | # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
61 | # within the Gradle project.
62 | #
63 | # You can find Gradle at https://github.com/gradle/gradle/.
64 | #
65 | ##############################################################################
66 |
67 | # Attempt to set APP_HOME
68 |
69 | # Resolve links: $0 may be a link
70 | app_path=$0
71 |
72 | # Need this for daisy-chained symlinks.
73 | while
74 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
75 | [ -h "$app_path" ]
76 | do
77 | ls=$( ls -ld "$app_path" )
78 | link=${ls#*' -> '}
79 | case $link in #(
80 | /*) app_path=$link ;; #(
81 | *) app_path=$APP_HOME$link ;;
82 | esac
83 | done
84 |
85 | # This is normally unused
86 | # shellcheck disable=SC2034
87 | APP_BASE_NAME=${0##*/}
88 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
89 | APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
90 |
91 | # Use the maximum available, or set MAX_FD != -1 to use that value.
92 | MAX_FD=maximum
93 |
94 | warn () {
95 | echo "$*"
96 | } >&2
97 |
98 | die () {
99 | echo
100 | echo "$*"
101 | echo
102 | exit 1
103 | } >&2
104 |
105 | # OS specific support (must be 'true' or 'false').
106 | cygwin=false
107 | msys=false
108 | darwin=false
109 | nonstop=false
110 | case "$( uname )" in #(
111 | CYGWIN* ) cygwin=true ;; #(
112 | Darwin* ) darwin=true ;; #(
113 | MSYS* | MINGW* ) msys=true ;; #(
114 | NONSTOP* ) nonstop=true ;;
115 | esac
116 |
117 |
118 |
119 | # Determine the Java command to use to start the JVM.
120 | if [ -n "$JAVA_HOME" ] ; then
121 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
122 | # IBM's JDK on AIX uses strange locations for the executables
123 | JAVACMD=$JAVA_HOME/jre/sh/java
124 | else
125 | JAVACMD=$JAVA_HOME/bin/java
126 | fi
127 | if [ ! -x "$JAVACMD" ] ; then
128 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
129 |
130 | Please set the JAVA_HOME variable in your environment to match the
131 | location of your Java installation."
132 | fi
133 | else
134 | JAVACMD=java
135 | if ! command -v java >/dev/null 2>&1
136 | then
137 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
138 |
139 | Please set the JAVA_HOME variable in your environment to match the
140 | location of your Java installation."
141 | fi
142 | fi
143 |
144 | # Increase the maximum file descriptors if we can.
145 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
146 | case $MAX_FD in #(
147 | max*)
148 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
149 | # shellcheck disable=SC2039,SC3045
150 | MAX_FD=$( ulimit -H -n ) ||
151 | warn "Could not query maximum file descriptor limit"
152 | esac
153 | case $MAX_FD in #(
154 | '' | soft) :;; #(
155 | *)
156 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
157 | # shellcheck disable=SC2039,SC3045
158 | ulimit -n "$MAX_FD" ||
159 | warn "Could not set maximum file descriptor limit to $MAX_FD"
160 | esac
161 | fi
162 |
163 | # Collect all arguments for the java command, stacking in reverse order:
164 | # * args from the command line
165 | # * the main class name
166 | # * -classpath
167 | # * -D...appname settings
168 | # * --module-path (only if needed)
169 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
170 |
171 | # For Cygwin or MSYS, switch paths to Windows format before running java
172 | if "$cygwin" || "$msys" ; then
173 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
174 |
175 | JAVACMD=$( cygpath --unix "$JAVACMD" )
176 |
177 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
178 | for arg do
179 | if
180 | case $arg in #(
181 | -*) false ;; # don't mess with options #(
182 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
183 | [ -e "$t" ] ;; #(
184 | *) false ;;
185 | esac
186 | then
187 | arg=$( cygpath --path --ignore --mixed "$arg" )
188 | fi
189 | # Roll the args list around exactly as many times as the number of
190 | # args, so each arg winds up back in the position where it started, but
191 | # possibly modified.
192 | #
193 | # NB: a `for` loop captures its iteration list before it begins, so
194 | # changing the positional parameters here affects neither the number of
195 | # iterations, nor the values presented in `arg`.
196 | shift # remove old arg
197 | set -- "$@" "$arg" # push replacement arg
198 | done
199 | fi
200 |
201 |
202 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
203 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
204 |
205 | # Collect all arguments for the java command:
206 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
207 | # and any embedded shellness will be escaped.
208 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
209 | # treated as '${Hostname}' itself on the command line.
210 |
211 | set -- \
212 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
213 | -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
214 | "$@"
215 |
216 | # Stop when "xargs" is not available.
217 | if ! command -v xargs >/dev/null 2>&1
218 | then
219 | die "xargs is not available"
220 | fi
221 |
222 | # Use "xargs" to parse quoted args.
223 | #
224 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
225 | #
226 | # In Bash we could simply go:
227 | #
228 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
229 | # set -- "${ARGS[@]}" "$@"
230 | #
231 | # but POSIX shell has neither arrays nor command substitution, so instead we
232 | # post-process each arg (as a line of input to sed) to backslash-escape any
233 | # character that might be a shell metacharacter, then use eval to reverse
234 | # that process (while maintaining the separation between arguments), and wrap
235 | # the whole thing up as a single "set" statement.
236 | #
237 | # This will of course break if any of these variables contains a newline or
238 | # an unmatched quote.
239 | #
240 |
241 | eval "set -- $(
242 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
243 | xargs -n1 |
244 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
245 | tr '\n' ' '
246 | )" '"$@"'
247 |
248 | exec "$JAVACMD" "$@"
249 |
--------------------------------------------------------------------------------
/gradle/build-logic/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 | #
20 |
21 | ##############################################################################
22 | #
23 | # Gradle start up script for POSIX generated by Gradle.
24 | #
25 | # Important for running:
26 | #
27 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
28 | # noncompliant, but you have some other compliant shell such as ksh or
29 | # bash, then to run this script, type that shell name before the whole
30 | # command line, like:
31 | #
32 | # ksh Gradle
33 | #
34 | # Busybox and similar reduced shells will NOT work, because this script
35 | # requires all of these POSIX shell features:
36 | # * functions;
37 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
38 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
39 | # * compound commands having a testable exit status, especially «case»;
40 | # * various built-in commands including «command», «set», and «ulimit».
41 | #
42 | # Important for patching:
43 | #
44 | # (2) This script targets any POSIX shell, so it avoids extensions provided
45 | # by Bash, Ksh, etc; in particular arrays are avoided.
46 | #
47 | # The "traditional" practice of packing multiple parameters into a
48 | # space-separated string is a well documented source of bugs and security
49 | # problems, so this is (mostly) avoided, by progressively accumulating
50 | # options in "$@", and eventually passing that to Java.
51 | #
52 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
53 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
54 | # see the in-line comments for details.
55 | #
56 | # There are tweaks for specific operating systems such as AIX, CygWin,
57 | # Darwin, MinGW, and NonStop.
58 | #
59 | # (3) This script is generated from the Groovy template
60 | # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
61 | # within the Gradle project.
62 | #
63 | # You can find Gradle at https://github.com/gradle/gradle/.
64 | #
65 | ##############################################################################
66 |
67 | # Attempt to set APP_HOME
68 |
69 | # Resolve links: $0 may be a link
70 | app_path=$0
71 |
72 | # Need this for daisy-chained symlinks.
73 | while
74 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
75 | [ -h "$app_path" ]
76 | do
77 | ls=$( ls -ld "$app_path" )
78 | link=${ls#*' -> '}
79 | case $link in #(
80 | /*) app_path=$link ;; #(
81 | *) app_path=$APP_HOME$link ;;
82 | esac
83 | done
84 |
85 | # This is normally unused
86 | # shellcheck disable=SC2034
87 | APP_BASE_NAME=${0##*/}
88 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
89 | APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
90 |
91 | # Use the maximum available, or set MAX_FD != -1 to use that value.
92 | MAX_FD=maximum
93 |
94 | warn () {
95 | echo "$*"
96 | } >&2
97 |
98 | die () {
99 | echo
100 | echo "$*"
101 | echo
102 | exit 1
103 | } >&2
104 |
105 | # OS specific support (must be 'true' or 'false').
106 | cygwin=false
107 | msys=false
108 | darwin=false
109 | nonstop=false
110 | case "$( uname )" in #(
111 | CYGWIN* ) cygwin=true ;; #(
112 | Darwin* ) darwin=true ;; #(
113 | MSYS* | MINGW* ) msys=true ;; #(
114 | NONSTOP* ) nonstop=true ;;
115 | esac
116 |
117 |
118 |
119 | # Determine the Java command to use to start the JVM.
120 | if [ -n "$JAVA_HOME" ] ; then
121 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
122 | # IBM's JDK on AIX uses strange locations for the executables
123 | JAVACMD=$JAVA_HOME/jre/sh/java
124 | else
125 | JAVACMD=$JAVA_HOME/bin/java
126 | fi
127 | if [ ! -x "$JAVACMD" ] ; then
128 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
129 |
130 | Please set the JAVA_HOME variable in your environment to match the
131 | location of your Java installation."
132 | fi
133 | else
134 | JAVACMD=java
135 | if ! command -v java >/dev/null 2>&1
136 | then
137 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
138 |
139 | Please set the JAVA_HOME variable in your environment to match the
140 | location of your Java installation."
141 | fi
142 | fi
143 |
144 | # Increase the maximum file descriptors if we can.
145 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
146 | case $MAX_FD in #(
147 | max*)
148 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
149 | # shellcheck disable=SC2039,SC3045
150 | MAX_FD=$( ulimit -H -n ) ||
151 | warn "Could not query maximum file descriptor limit"
152 | esac
153 | case $MAX_FD in #(
154 | '' | soft) :;; #(
155 | *)
156 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
157 | # shellcheck disable=SC2039,SC3045
158 | ulimit -n "$MAX_FD" ||
159 | warn "Could not set maximum file descriptor limit to $MAX_FD"
160 | esac
161 | fi
162 |
163 | # Collect all arguments for the java command, stacking in reverse order:
164 | # * args from the command line
165 | # * the main class name
166 | # * -classpath
167 | # * -D...appname settings
168 | # * --module-path (only if needed)
169 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
170 |
171 | # For Cygwin or MSYS, switch paths to Windows format before running java
172 | if "$cygwin" || "$msys" ; then
173 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
174 |
175 | JAVACMD=$( cygpath --unix "$JAVACMD" )
176 |
177 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
178 | for arg do
179 | if
180 | case $arg in #(
181 | -*) false ;; # don't mess with options #(
182 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
183 | [ -e "$t" ] ;; #(
184 | *) false ;;
185 | esac
186 | then
187 | arg=$( cygpath --path --ignore --mixed "$arg" )
188 | fi
189 | # Roll the args list around exactly as many times as the number of
190 | # args, so each arg winds up back in the position where it started, but
191 | # possibly modified.
192 | #
193 | # NB: a `for` loop captures its iteration list before it begins, so
194 | # changing the positional parameters here affects neither the number of
195 | # iterations, nor the values presented in `arg`.
196 | shift # remove old arg
197 | set -- "$@" "$arg" # push replacement arg
198 | done
199 | fi
200 |
201 |
202 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
203 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
204 |
205 | # Collect all arguments for the java command:
206 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
207 | # and any embedded shellness will be escaped.
208 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
209 | # treated as '${Hostname}' itself on the command line.
210 |
211 | set -- \
212 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
213 | -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
214 | "$@"
215 |
216 | # Stop when "xargs" is not available.
217 | if ! command -v xargs >/dev/null 2>&1
218 | then
219 | die "xargs is not available"
220 | fi
221 |
222 | # Use "xargs" to parse quoted args.
223 | #
224 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
225 | #
226 | # In Bash we could simply go:
227 | #
228 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
229 | # set -- "${ARGS[@]}" "$@"
230 | #
231 | # but POSIX shell has neither arrays nor command substitution, so instead we
232 | # post-process each arg (as a line of input to sed) to backslash-escape any
233 | # character that might be a shell metacharacter, then use eval to reverse
234 | # that process (while maintaining the separation between arguments), and wrap
235 | # the whole thing up as a single "set" statement.
236 | #
237 | # This will of course break if any of these variables contains a newline or
238 | # an unmatched quote.
239 | #
240 |
241 | eval "set -- $(
242 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
243 | xargs -n1 |
244 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
245 | tr '\n' ' '
246 | )" '"$@"'
247 |
248 | exec "$JAVACMD" "$@"
249 |
--------------------------------------------------------------------------------