Skip to content

feat: add SVG support to image preloading#145

Open
draggie wants to merge 1 commit intocallstackincubator:mainfrom
draggie:feat/svg-image-preloading
Open

feat: add SVG support to image preloading#145
draggie wants to merge 1 commit intocallstackincubator:mainfrom
draggie:feat/svg-image-preloading

Conversation

@draggie
Copy link
Copy Markdown

@draggie draggie commented May 6, 2026

Closes #132.

Summary

Adds MVP SVG support to image preloading on iOS and Android by rasterizing SVG input into PNG data before storing it in the existing preloaded image cache.

  • Adds inline SVG preload input via svg, width, and height
  • Detects remote SVG responses in the existing URL preload path
  • Rasterizes SVGs with WebKit on iOS and AndroidSVG on Android
  • Keeps widgets using the existing Voltra.Image / preloaded asset rendering path
  • Updates iOS and Android examples with SVG widget test controls
  • Updates public docs and package types
  • Adds Android unit coverage for inline SVG preloading

Validation

  • git diff --check
  • npm run format:check
  • npm run typecheck --workspace voltra
  • npm run typecheck --workspace @use-voltra/ios
  • npm run typecheck --workspace @use-voltra/android
  • npm run test:kotlin --workspace voltra
  • Manual iOS example app widget test
  • Manual Android example app widget test

Notes

The iOS implementation uses WebKit for runtime SVG rasterization, so the podspec now links WebKit. SVG inputs require explicit positive width and height for predictable raster output.

Preview

iOS
simulator_screenshot_18FEA446-A034-4A1D-87E0-96B48538ABF3

simulator_screenshot_8890B93A-C3A8-4EB9-BEFD-9A80268A1F1A

Android
Screenshot_1778071875
Screenshot_1778071883

@draggie draggie marked this pull request as ready for review May 7, 2026 08:52
@V3RON
Copy link
Copy Markdown
Contributor

V3RON commented May 7, 2026

Good work! I have a couple of ideas for improving code structure.

  1. Split URL and SVG preload paths
    The current preload implementation mixes two different concerns in the same native function: downloading from URL and processing inline SVGs. This makes the control flow harder to follow and maintain.
    Recommendation:
    Keep one public JS API, but split native handling into separate URL and SVG-specific functions. JS can dispatch internally so the user still sees a single preloadImages(...) API.

  2. Preserve the current JS ergonomics
    There is no need to expose two APIs publicly. The public API can remain a single function, with the JS layer choosing the proper native path based on whether an item contains url or svg.

  3. Extract Android image cache/storage responsibilities
    On Android, storage concerns are mixed into VoltraImageManager. The class currently handles:

  • fetching/downloading
  • SVG detection/rasterization
  • cache directory lifecycle
  • file writes
  • URI generation
  • SharedPreferences index management
  • stale file cleanup

@Angelk90
Copy link
Copy Markdown

Angelk90 commented May 8, 2026

@draggie : It would be possible to use it this way, for example with an input field where the user enters a name, an SVG file is generated based on the text, and it can then be injected into the code.

See the example below.

image
function createLogoSVG(text) {
  return `
    <svg viewBox="0 0 600 120" xmlns="http://www.w3.org/2000/svg">
      <defs>
        <linearGradient id="a" x1="0" x2="1">
          <stop offset="0" stop-color="#00f7ff"/>
          <stop offset="1" stop-color="#7a00ff"/>
        </linearGradient>

        <filter id="b">
          <feGaussianBlur stdDeviation="6" result="blur"/>
          <feMerge>
            <feMergeNode in="blur"/>
            <feMergeNode in="SourceGraphic"/>
          </feMerge>
        </filter>
      </defs>

      <rect width="100%" height="100%" fill="#fff"/>

      <text
        x="50%"
        y="55%"
        text-anchor="middle"
        font-size="44"
        fill="url(#a)"
        filter="url(#b)"
      >
        ${text}
      </text>
    </svg>
  `;
}

const svg = createLogoSVG("Draggie");

You can use it to create a chart using svg.

image
<svg width="700" height="450" viewBox="0 0 700 450" xmlns="http://www.w3.org/2000/svg">
  <rect width="100%" height="100%" fill="#f3f3f3"/>
  <defs>
    <linearGradient id="a" x1="0" y1="0" x2="0" y2="1">
      <stop offset="0%" stop-color="#9aa0a6" stop-opacity=".35"/>
      <stop offset="100%" stop-color="#9aa0a6" stop-opacity=".05"/>
    </linearGradient>
  </defs>
  <path stroke="#d9d9d9" d="M90 60h560M90 170h560M90 280h560"/>
  <g fill="#6f6f6f" font-size="16" font-family="Arial">
    <text x="10" y="65">0,000584</text>
    <text x="10" y="175">0,000582</text>
    <text x="10" y="285">0,000580</text>
    <text x="10" y="395">0,000578</text>
  </g>
  <path stroke="#7c7c7c" stroke-width="2" d="M90 390h560"/>
  <path d="M90 390v10m46-10v10m46-10v10m46-10v10m46-10v10m46-10v10m46-10v10m46-10v10m46-10v10m46-10v10m46-10v10m46-10v10m46-10v10" stroke="#7c7c7c"/>
  <g fill="#6f6f6f" font-size="10" font-family="Arial" text-anchor="middle">
    <text x="90" y="420">00:00</text>
    <text x="136" y="420">01:00</text>
    <text x="182" y="420">02:00</text>
    <text x="228" y="420">03:00</text>
    <text x="274" y="420">04:00</text>
    <text x="320" y="420">05:00</text>
    <text x="366" y="420">06:00</text>
    <text x="412" y="420">07:00</text>
    <text x="458" y="420">08:00</text>
    <text x="504" y="420">09:00</text>
    <text x="550" y="420">10:00</text>
    <text x="596" y="420">11:00</text>
    <text x="642" y="420">12:00</text>
  </g>
  <path d="m90 70 10 40 10-15 10 35 10-10 10 25 10-10 10 20 10-15 10 20 10-10 10 25 10-10 10 25 10 20 10-10 10 15 10-10 10 25 10 30 10-15 10-25 10 15 10-10 10 15 10-25 10 35 10-15 10 10 10-15 10-10 10 40 10-5 10-15 10-15 10 10 10-15 10 25 10-15 10 10 10-35 10 10 10-15 10 10 10-15 10 10 10-15 10-15 10-10 10 5 10-10 10 15 10-10 10 5 10-2 10 7v215H90Z" fill="url(#a)"/>
  <path d="m90 70 10 40 10-15 10 35 10-10 10 25 10-10 10 20 10-15 10 20 10-10 10 25 10-10 10 25 10 20 10-10 10 15 10-10 10 25 10 30 10-15 10-25 10 15 10-10 10 15 10-25 10 35 10-15 10 10 10-15 10-10 10 40 10-5 10-15 10-15 10 10 10-15 10 25 10-15 10 10 10-35 10 10 10-15 10 10 10-15 10 10 10-15 10-15 10-10 10 5 10-10 10 15 10-10 10 5 10-2 10 7" fill="none" stroke="#787d84" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
  <circle cx="640" cy="175" r="8" fill="#787d84"/>
</svg>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SVG support

3 participants