Perfect—here’s how we handle both cleanly:

A) Infographics: keep SVG previews (done)

No changes needed to the PPT pipeline. The library cards show fast SVG schematics; the real slides are generated on Download or Generate Template.

B) Importing icons from a screenshot → clean SVGs in your Icon Library

We’ll add a tiny “Icon Importer” tool:

Upload a screenshot containing many icons.

Tell it the grid (rows/cols) and optional margins/padding.

It slices the screenshot, vectorizes each icon (black shapes), and returns SVGs.

You select which ones to save into the Icons DB (as SVG-only).

0) Install deps (backend)
npm i express multer sharp potrace

1) Backend route — /api/icons/import-from-screenshot

Saves nothing by default; just returns SVGs for review. You can then POST selected ones to your existing /api/icons create endpoint.

server/api/icons/import-from-screenshot.ts

import { Router } from "express";
import multer from "multer";
import sharp from "sharp";
import { Potrace } from "potrace";

const upload = multer({ storage: multer.memoryStorage(), limits: { fileSize: 15 * 1024 * 1024 } });
const router = Router();

/**
 * Body (multipart/form-data):
 * - file: screenshot (png/jpg)
 * - cols, rows: grid
 * - margin: outer px crop (optional, default 0)
 * - cellPad: inner px padding inside each cell before trace (optional, default 6)
 * - threshold: 0–255 binarize (optional, default 180)
 * - invert: "true"/"false" – if icons are dark on light, leave false; if light on dark, set true
 */
router.post("/", upload.single("file"), async (req, res) => {
  try {
    if (!req.file) return res.status(400).json({ error: "No file uploaded" });

    const cols = parseInt(String(req.body.cols || "6"), 10);
    const rows = parseInt(String(req.body.rows || "6"), 10);
    const margin = parseInt(String(req.body.margin || "0"), 10);
    const cellPad = parseInt(String(req.body.cellPad || "6"), 10);
    const threshold = parseInt(String(req.body.threshold || "180"), 10);
    const invert = String(req.body.invert || "false") === "true";

    const base = sharp(req.file.buffer);
    const meta = await base.metadata();
    const W = (meta.width || 0) - margin * 2;
    const H = (meta.height || 0) - margin * 2;
    if (W <= 0 || H <= 0) return res.status(400).json({ error: "Image too small" });

    const cellW = Math.floor(W / cols);
    const cellH = Math.floor(H / rows);

    async function traceToSvg(buf: Buffer): Promise<string> {
      // Potrace wants a file or buffer; we’ll use the buffer
      return new Promise((resolve, reject) => {
        const p = new Potrace({
          threshold,        // binarization threshold
          turdSize: 2,      // remove tiny specks
          turnPolicy: Potrace.TURNPOLICY_MINORITY,
          color: "#000000", // output fill
          background: "#FFFFFF",
        });
        p.loadImage(buf, (err: any) => {
          if (err) return reject(err);
          p.setParameter({ threshold, turdSize: 2, turnPolicy: Potrace.TURNPOLICY_MINORITY, color: "#000000", background: "#FFFFFF" });
          p.getSVG((err2: any, svg: string) => (err2 ? reject(err2) : resolve(svg)));
        });
      });
    }

    const svgs: { id: string; svg: string }[] = [];
    for (let r = 0; r < rows; r++) {
      for (let c = 0; c < cols; c++) {
        const left = margin + c * cellW + cellPad;
        const top  = margin + r * cellH + cellPad;
        const width = Math.max(1, cellW - cellPad * 2);
        const height = Math.max(1, cellH - cellPad * 2);

        // Prepare a high-contrast, single-channel buffer
        let img = await base.extract({ left, top, width, height })
          .ensureAlpha()
          .toColourspace("b-w")
          .toBuffer();

        // Binarize and invert if needed
        img = await sharp(img)
          .threshold(threshold)
          .negate({ alpha: false, // if icons are dark on light, invert=false; set true only if needed
                    })[invert ? "toBuffer" : "toBuffer"]();

        const svg = await traceToSvg(img);
        const id = `r${r}c${c}`;
        svgs.push({ id, svg });
      }
    }

    res.json({ svgs, cols, rows });
  } catch (e: any) {
    console.error("[icons/import-from-screenshot]", e);
    res.status(500).json({ error: "Failed to vectorize icons" });
  }
});

export default router;