SVG to Compose

repository·main·Indexed 42 days ago

https://github.com/rafaeltonholo/svg-to-compose

A suite of tools, including a standalone CLI (s2c) and a Gradle plugin, designed to convert SVG and Android Vector Drawable (AVD/XML) files into Jetpack Compose ImageVector objects for Android and Kotlin Multiplatform applications.

Tokens
16.5K
Snippets
54
Records
73
Agent score
50%

What's inside SVG to Compose

  1. Use the svg-to-compose Gradle plugin for automated conversions

    main

    The svg-to-compose-gradle-plugin is a Gradle plugin designed to automate the conversion of SVG and Android Vector Drawable (AVD/XML) files into Jetpack Compose ImageVector code during your build process.

    Key features include:

    • Integration with the svg-to-compose core library.
    • Support for incremental builds to optimize build times.
    • Icon caching.
    • Configurable parsing options via a DSL.
  2. Overview of the SVG-to-Compose module layout

    main

    The project is organized into distinct modules depending on how you intend to use it:

    • svg-to-compose (core library): A Kotlin Multiplatform (KMP) library containing the parsing, conversion, and code generation logic. It has no executable entry points and is intended to be used as a dependency.
    • modules/cli (CLI module): The command-line entry point built with Clikt. It depends on the core library and produces native executables for macOS arm64, Linux x64, and Windows mingwX64, as well as a JVM fat JAR.
    • svg-to-compose-gradle-plugin: A Gradle plugin that also depends on the core library for integration into Android/Kotlin build pipelines.
  3. Understand Template Fallback Behavior

    main

    The template system is modular; every component is independently optional. If a specific template piece is missing from your s2c.template.toml file, the tool falls back to the default ImageVectorEmitter output.

    | Component | Template present | Template absent | | :--- | :--- | : | | file_header | Emitted before package | No header | | icon_template | Use template | Default backing-field pattern | | path_builder | Use fragment | Default path(...) call | | group_builder | Use fragment | Default group(...) call | | icon_builder | Use fragment | Default ImageVector.Builder(...) | | chunk_function_name | Use fragment | {name}Chunk{index} | | chunk_function_definition | Use fragment | private fun ImageVector.Builder.{name}() | | preview | Use preview template | Use CLI/Gradle flags | | receiver | Template definition | CLI --receiver-type |

  4. Understand Kobweb page and app discovery

    main

    Kobweb uses specific annotations to discover components and pages:

    • @App: Annotate a composable method (e.g., in AppEntry.kt) to define the entry-point that is called for ALL pages. This is handled automatically by the framework.
    • @Page: Annotate a composable (e.g., in pages/Index.kt) to define it as a discoverable page. The file/method name determines the URL route (e.g., Index.kt maps to /).
    • Markdown: Files placed in resources/markdown/ (e.g., About.md) are automatically converted into pages at compile time (e.g., /about).
  5. Understand the Playground Project Structure

    main

    The playground-kmp project is a Kotlin Multiplatform (KMP) project targeting Android, iOS, Web, and Desktop (JVM). The code is organized into shared and platform-specific directories:

    • /composeApp/src: Contains the shared Compose Multiplatform code.
      • commonMain: Code shared across all targets.
      • iosMain: Platform-specific Kotlin code for iOS (e.g., calling Apple's CoreCrypto).
      • jvmMain: Platform-specific Kotlin code for Desktop (JVM).
    • /iosApp: Contains the iOS application entry point and is the location for adding SwiftUI code.
  6. Determine Template Precedence

    main

    When multiple sources define the same output component, the tool follows this precedence order (from highest to lowest):

    1. CLI flags / Gradle DSL explicit values: Explicitly passed arguments or DSL configuration.
    2. Template file definitions: Values defined in your s2c.template.toml.
    3. Hardcoded defaults: The built-in fallback behavior.

    Note on receiver: The definitions.receiver key in your template file is only used if no CLI --receiver-type flag is provided.

  7. Configure formatting via `FormatConfig` or `.editorconfig`

    main

    Formatting (indentation, line length, etc.) can be managed in three ways:

    1. Automatic Resolution: If ParserConfig.formatConfig is null, the Processor automatically resolves settings from .editorconfig files by walking up from the output directory.
    2. Programmatic Override: Explicitly setting formatConfig in ParserConfig skips .editorconfig resolution.
    3. CLI Flags: Use flags to override settings during command-line execution.

    Supported .editorconfig properties:

    EditorConfig PropertyFormatConfig Field
    indent_sizeindentSize (default: 4)
    indent_styleindentStyle (default: SPACE)
    max_line_lengthmaxLineLength (default: 120)
    insert_final_newlineinsertFinalNewline (default: true)
    val config = ParserConfig(
        pkg = "com.example",
        theme = "MyTheme",
        optimize = true,
        formatConfig = FormatConfig(
            indentSize = 2,
            indentStyle = IndentStyle.SPACE,
        ),
    )
  8. Understand `OutputFormat` and future expansion

    main

    The OutputFormat enum determines the type of code generated by the emitter. Currently, only one format is supported:

    • IMAGE_VECTOR: Generates ImageVector.Builder Kotlin code (default).

    Future versions will introduce CANVAS and VECTOR_PAINTER formats. You can specify the format when creating an emitter via the factory.

    // Future usage
    val emitter = emitterFactory.create(
        outputFormat = OutputFormat.CANVAS,
        formatConfig = FormatConfig(indentSize = 2),
    )
  9. Use placeholders in templates and fragments

    main

    Templates and fragments use the syntax ${namespace:key} to inject values.

    Namespaces

    • icon: Metadata from the icon (e.g., ${icon:name}, ${icon:property_name}, ${icon:visibility}, ${icon:body}). Scope: icon_template, icon_builder.
    • path: Parameters from path nodes (e.g., ${path:fill}, ${path:fill_alpha}, ${path:stroke}). Scope: path_builder fragment only.
    • group: Parameters from group nodes (e.g., ${group:rotate}, ${group:pivot_x}). Scope: group_builder fragment only.
    • chunk: Context from chunk functions (e.g., ${chunk:index}, ${chunk:name}, ${chunk:body}). Scope: chunk_function_* fragments.
    • template: Output of a rendered fragment (e.g., ${template:icon_builder}). Scope: icon_template.
    • def: Simple name of an import defined in [definitions.imports] (e.g., ${def:theme}). Scope: Any template or fragment.

    Null Handling

    If a variable resolves to null, the entire line containing only that parameter assignment is removed from the output. For example, if ${path:fill_alpha} is null, the line fillAlpha = ${path:fill_alpha}, will be elided.

  10. How the SVG/XML to Compose plugin works

    main

    The plugin automates the conversion of SVG and Android Vector Drawable (AVG) files into Jetpack Compose ImageVector objects.

    Processing Flow:

    1. Configuration Parsing: Reads settings from the svgToCompose extension.
    2. Icon Scanning: Scans specified directories for SVG/AVG files.
    3. Icon Processing: Applies options like optimization and minification.
    4. Code Generation: Generates ImageVector objects in the target package.
    5. Build Integration: Integrates code into the project during the build.

    Caching & Performance:

    • The plugin uses a built-in cache to determine if an icon needs regeneration or deletion.
    • Changing the plugin configuration invalidates the cache.
    • It uses the Gradle Worker API to support parallel processing via useParallelism(parallelism = n).