Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Image in SVG doesn't work when exported to PNG #592

Open
steven-tey opened this issue Feb 15, 2024 · 13 comments · May be fixed by #593
Open

Image in SVG doesn't work when exported to PNG #592

steven-tey opened this issue Feb 15, 2024 · 13 comments · May be fixed by #593

Comments

@steven-tey
Copy link

Bug report

Description / Observed Behavior

Image in SVG doesn't seem to work when exported to PNG – it throws this error:

Cannot read properties of undefined (reading '0')

CleanShot 2024-02-15 at 15 33 37

Expected Behavior

Render the PNG the same way as it does the SVG.

Reproduction

Repro: https://d.to/k3beO8I

(click on the "PNG" tab)

Additional Context

Related PR: #472

cc: @Jackie1210

@steven-tey steven-tey linked a pull request Feb 16, 2024 that will close this issue
@fonstack
Copy link

Having same issue here.

@steven-tey any solution?

@fonstack
Copy link

@Jackie1210

https://og-playground.vercel.app/?share=xVvbbtvIlv2VwIPBeegMWjc3kpzuAzim49BjUlEsWaHRLzalUKIkO4ikSGSj_33WWruKpGwnPZcG5kHYvBSrdu37pfTHUfowmR69Ofp1Mv_2-_2LF-tNsZz-9scfvH7xYjadZ7PNmxf_aLda__6Pl_ZwN59sZo-eTebrL8vbAk8_L6d7_5TXwfzrNN3MH-7xLn1Yblf3_u3tcp7dh5vpas1X0_vN9Kt_lW_Xm_nn4vQBD--5_uHru9t0kX192N5PTh-WD1_x_t8-f_5crYqvrubl9M2LbqfxaOz28kurpad__vn7_b948et8dZtNbVu__X6Ebf1-5HZe336dfsbN5HZz-0bDf15_y37ar5b_TGe3X9fTzW_bzef_ePXPu9v19Jfeyw_v485N8bZzO77uDlavex-uwl0YnGTR8Oz4Mh-14-JkHwXJtn8VZtPz9vruPnodrmatyfuTXy6L191JN91Oymh71724vyzDXRScfEu7N_fh_O0i-fRxdjk-nt2NR6_DxX42HV8XYXA2D8-Xiw9XF1HyablMl6_2Yd77z_D05NXkfNm6Ox_9NPh00UpWr2fpapBF5eDVZffjl8n5fvkhe8jCYH8veHqSfXj_djY5z7Kb1XJ9F7TmYXmxiPLBPirC7Ab3w-HZLpr39nG5WEflchYNB9t-MDi-DM5al3nW6l_tdpfByTo6bfUu83CNse2kaG3jq155edXD_pNNdNXD2OUa9-3LPNpc5gs-w_UZrt_ll3kKmK3xrLwcnm3j-a64HIbb-Hy3jeatbdSJDM41J74bYfxkbXN9xPchYLiNbN02xnSTIb7JT4Cnh8QRuJ66OYKzHfAoo_luH532jqMSuPM9xgEX4Xd32iswFrhznZvS1hnZOoXeuf2kws_tuXswrnNWYEw7PsWeDDp62d763CPG9cfA8wp7tHkxR0Qc9hhre8O7-BT32htoUybYk4fV3vj-OCkb33AOzqd5bI0Y_BRv_HcVL27GUf5uhnWP4yHo4aHhjPccM8nBa-ACXDkXcAfc4x1lwPgkXA_eV_d9G4PxFzPsp4zKaO2hrcM1-LvOgRfx7OAnuXDz4NvU1iK9-PNrvk9Mnuy7lr7jPnnN7ztn4OGiBA88NF64OSB3Hay9j64q6GUbdOS6okkrasIKZ_I8WZO20PsK1rRLpQd3jkbx-dmeY_rBaB0Nk200zCCTjh_DM47taSxlaEwZ429AuZL-OBnl_rhXygyvuWf-jGb1NWlw7GigvcWF7RGw3iN_K78OaSk9hS5CJ0V36H8BnIeDdQXd_qCbRuMryTntQNnYD2W3RXntDyPIoofN9-RJAlpAl3K-O6MelRV-VzvIEud-B_2E3K_OYG8gNcXOw3rc6Q64XmMc5h2Dj_NeLyZO0Jt-kHi89tAX4HS27X8C_VfUDdDKeIvvMR73sAGcCzYGc-n5Avzj_DHGxrP-aW9HHnro5IFr6_sJvuuf01ZCJjiXX1u6DXpp7TPo3wnkYedhQy651gXXMrmWHNBGyh4eS0bAa9rcSh7EO-z1fKex0o9CMkC8qXc92A3iI1jLaAR7dfANZQc092tTxjFGskLamE12a5scCj-MeU8IG-TstskyxtIuD9NCdlmwogf2CJ5pjgvaoLIfiBcGKz0bQIaJI76XrJpdcHYTdI45B3B9R950-9BBD_0ckhHgFAfgZUlfVuFA-eJeAbmW-R3a9TvySvJHX2X-a8K9jUUv96NuLo1WDbtNXeH-YUMot5y7Z_5AsOK16Kr5vL5n-M28PfN67mGRDEfbeDjoXQYeVva8R3oaXx7ZSPJBOCAmka4KVjaA9jk-F13djzwX3fGDzYbMRZQdB728mw8AnwrosceDtAbPTd9heyvo9R73p63iVjjB30FPSbc-5Zj-byg70uN74kR553v43i5tBr5FfKHnbflEZ1sM1rbFZJB7zXrGQ0G_Z9xjH5Q_xQSjAjENY4J2lCPm8faNOJHO0EfiZ3ab8tO023gvmt54GXSydBA3tG-Dt_bceMl4xXw-5Yx7le45vtF-Fb2d4hPqThDVOkO7Cbp7n-L0wf2kf45v0gknM8_qH2WGuo6Yx63dac7l5UA_P562xv_oZ5oy6n_Qd-jAMANtPax0zcdyoA9-TR3kfBwfLKBHHlZ4yqdVeyaupOsq4rvHeuLsDuT6OinM_58cQxY8PPD_zl5aTOm-f8Qj88nUI9n0DHLGeJjxSB270GfKNmg9-Ursp8IffHs3ls_vCGfFXrRJpkPX5EOLuh0F8NWQOYOVz5I_jlYabzGn5MDwTWBrzJYqfgRtBy6WsH2494pbzVdqTfpUxn-I73_kf6kr0HXpPvbsYa0jiAFg-6VrKeMrD72uYS3Ko2hiMuh5ID3Ec9nOkPzpch7KftNHaQ36bNnJgYtXnE-ifVBcyzjHv4f-gG4-tjcbR5_6kTqBdRRnMvZgjMb1SV9C47X3b7AxLm_w42BzQPcgxD48rHhMG-B0n_uNzK7Crnj_bfZY8kX5l6-gf4BdMv5wTvMttNe0AZ2IORjpm0fN2F308H4b8Q5jiAL88rDat4slEL-7vMrrLfiuWKDYOZluyCppeZ8wBmV-QRrYM_kVzsec4VpzKr5hLoTnpE2UMzfxsJGjkDbE1Y8XXsg75rRzsKse1rEUaWG0oR2lflCO5Z-VK0k-Td41r9lg0l3PriHXJ7DVC9hw2vjo-HG8qFyP8iJcmEs6XeEauOY7xHHmsyXfgnUuxzHywyno52Gls_h-JnzgxyyPxT4gr8AdPsTiJs4B-mN_c4eL9Njpl-YAH_AN5ZI-CvuyvIi2y_Ii5YygPcamO8o55uhGAXJrpz-IhSy3Rk4B2sif0haB9_R5nI-6DxrNFOtLR5t5kHQa8WBFZ_BPfFF8ZXGg4h7afrONTraatMK-uZeaB6bXI-4LuFd-FzQj3fUe84FW-QLjPGzQN3j3EGGcbADXV83A6Ir1MafJw18_93RnfYHxK2KSPONY2lbJl2Ia8k_yq3t9Z7Dx_r14Sv2mL5DMYH8eVvSgDcfcrC8wPqA-Y23ihTldvGixmmAtt_Jv1Zrw7aT3QvQ-mB9zYxxk64K1EtIS-9Za7nvoHmkQpKwpyPY2vme8tI7GGWMRXC9g5xjHNesl4McK74ErZGjHPM1BPwfWpsxlyvups5bLVTpC-VZ-YjqHfTOGtVysuIb9MJkeIfaSTLcRezVlhDZjP8EP8Q14l7Uh-yVkoRcPK9knzbC3Jd6z5gR8mLPKlin2ow118qt4kH7Z4i9fH4INQd5a23nQ2ew07HFueTztB-YljYgHamZcZ1cQl1vQOimZfytvpm9jDAddw9wa19K4RPWAM-ieh9V6intVF6P9Zc5q-QntuK-1sW6lGEjyqn0wBqBu0EZQrujDwAfsB3mDs4mjVh0TU7Y4lv7G58zkfdY5GGM1gXFckp6cE_hqDcq93qkO4WIO5sC-VkZ7D33lnKoZ1vEfacp3ioGIV9alPwEPkKtW9RT5Z8qLaKocg3JAP6q8gHmZ-VPW1WhnvO9S_vY9X1PN4eo7DVi9V25d1awOa1f197QhlBkPG3i7-BZ8zDP5R4MVjxVrOt7V-b747fiuPTXzfa554XCLZ1F5ctwfjjpRnrbjYdKJyxTxCfU22vWHi3ach90Y72PqXz5oxMGWh0N-jG_y4aqDQFabdsdw6Vs-y1qa6qyYu-GzlXe42qzqR76e4OSANktxldXpnhknm-X8CHgEPfCwkhXmTMTX5TaQvwD6T70RbOipxcBuHOcZKHY3WI3juvCvltc4HriaBv1CVTt1tRBX3-gwF0CsbHauEe8arcyuKO-GbKf0hR7W46ATTm9pA62ep3VhU1g3pT3WusrF_LrylZCxuoZhNPPxPmiHvkOQQd48PKiD1-MqHeW-6NOlo9-JARirxwFsE30jfQX6AJJp1toQr7NXQNtxQt_E92X_-mH-Id99S8_ftW4D9SoO-hDsXejd6dvV7Xi_Rg8lT8qofROMeuyDoNcRQ6Yhx_RRA-AJm2M1asQz8OXyj-EG-gh-Qgfoc8sB4oKQuU4L7z3-9M0b2uikpF93fQvN0UK8TN8_2iCv555xzX0srI4Nn4X4CTYb32FP9AvwN6x5PKmfaB7mvldcT3gBT8hl0QLutNN8rlwKMRf2JX8YbRivpQVxYu0HeSH3OFcth3qmOgt9nWK45vUnXx9hXKIYljUayHpIO16SPqznN2wd5gbOiMVcv4YyRz_m-jVpFStCz-lzGRvKHuE58zz2T6xGQ79HOgYhvidNwW_YdsgEczk8q-vgFier3mr9Ddpx4i-fkJJObb8m3sPfSRaJO-aCHYPs1dfcZ8b6HnMSxibARfunvSJtmAc7_aBfwDPuE_IsP636L3jLmIJjA8UYa8kO6lj00dy389uMzc1vC0fsU2Pgs1Ubkt8hr0lT0MpkRLyGvSGu-E71DMaA1uvScx8HMGehTlH-qDNtl-sijuoVplPQ4dPdMf0972Par-f0in298_Sn07ldP3dPfbsp6_HVuCf9wLQTdUZFgt6l9QORH6I-4HS-m5ay44zZjA-k_ypq8E579fw64KPpHfIy8BrfMpaC3EOmSj6jjZzcoq-J3qVweow_7ccO_dGH8Pzmy9357nU4R2ZcXi8Q86E_OkC_tHUMmsP_gVaMjRG_Us7VCyCPYWHqWqXkDPy_yKFXjJmAm2q-pnf1tdVVpGeQUeUe5B30hnZCPuDdj_B-ddn5u3iBWLTJC_FBegX9pP3iPpBrS2-hW5JL2gPEJ8wzmtess1kvlLpE-4M9Z5RV2jk8gzy3n5e17-KLyCceDxCLe3xpC5RboA9K20nbjFGQkYovro5muIJHsBOOH6BvgwdN3lT8sLqt44fZ7f8FPz6cvm76pWfkLOrGebyEP_dy1lG-Qx21XMhqiMOIvSn5HPYbYHeY7284BnpMnuHZ32N_BuWCto607eD5MfSprPBBX_MAv-V3-fjEB8fngyLJLxbeB8f43nxwWqY4s0A763t7sqmqm7DOrbyR9px5HWtqrhYW0n-oP0Iecg-mO8pLLTZmjdr6iHgOW9G8Nj-H_bM-Ga1BQ_gPXsdBjBzKctgR6x_rJq7wOZC9AenTjb4vx09jEMbHw0VZ7Z-2K-ecqEkjvgU_LM9T7wI6Z34H8ojrLuwgr-1Mg7vGc9Z9GZedUq5HVocRfUAbXI8Yk-YJcUV_iPJc3R_Tb4zn4U-QUcnkIzk9lNtnbciggHwhz_Q6SVqFNjfws9qSj2lT1hE89LVyF4NCFtFTlP_3sOqvqcbKuMv1NAUb30OXmefqnII_N2A5a6p4k3kUeMmao79WDW7CeLTEWRr5WME6r6Le8zlyHPlgg_WZDsuXGasx9mWugWvlAsMQvPPwsB6h-kA5Up5v0OVJnIMy32L-Rr1OmrXytuwZ9A04_EjfnrMtPFeA3lNS2RbWmTQXbYV6xYgJRFfBKi9jvAVbYWdfLN9z8CAPMlup7x_Rj3Vvxu_WdzN4kNfa2Q8Xt7kc6J1oaXUT9Achn9BTxlyCtUy4_r-dvUA8WcFaLhQDICYq2e_xsO51GY9TzgF6-7oOZRE2TTVI5nzkk_ZF3tk5CdjWAb8P2Kvz0M0bJMxHpWugX-f7urX79shHP_XZT-OROfJunJOIPC979KUWu6n_wzgJeYLqr4XlCOgj8ByJepyozUDPLLdiPUk-nHzlGNlD1rSoO4MyAU0UN7bsnEGG-Fz7apnvObz_7j6f9eNhC31b1MyruIO1pmou2PvGPuCP6n24Xq32Qdvm98H9-X3YmY9qH-wHyh65fRyu9fj-_8ivJ_bye7HYc74Bfbkd4td95RsYn3t_A1vFGCaGTfLwMLbBntuozbCm48-42XkPyxd1Zgv-U-e94HPKyOJmQS-7iGnhG-2MivRDNZD-MFXt0KAfq3iJNeC3WB8yifzQQV-zeYT_j2PvZ-UE8XWZdWrfwvpSqpjE6veaH_vysNqH67_DLpUj9QEMNnCXvRi4XE59gspuqBZoPXXbL_sqwxHzX8DrjfUWcY6CuQntpcW1sH-ZnTUAPu7cxTZmr6ujOo3OjTTOMrBWxmvWC7qwQR42epuQd82jczLud8FzKJ3YzqEI-lxYPKZ_-Ku4-nE86uQx6SyLpLNfNuzN6mYFv_E-fB2qDhpmSXeB6wQ6R38S82wqcO61YYO0pp_jiQ14Zs1ncsjv6dBzfk26Ap3xtrClc1m0hYgjQXPGcy3w2UPPX52FpM6gD-Brfb5e5XrIjKPIL0LpXTdCrdjDSr7p0wPWSRv-CjUT1pU9rM-zUC4QEyyEF_uoHnp_e4j_X_Dw2dhyCF4ESaeOLTFPSf1L7JyW5oetdLDeB30dfATkB3ltBRu4q2aHcU_rg8oF4TvtjI_r78CvqNYFOG7ZuRy8Vx_Meoamc3behvi4cyI43wYdSywXkc5U8QRtlq4RdyF-rGHjzB_49vgch9VI6Z-ho9V5uv83XaEPYq3lf6Yrz_ieJ3kVzgvgbG3Ne9JDeQVzZPTpGJfaeVnFWeopWa8X-F2UlkOp7mk1QMai48jOTdHuMQdWXY3vaf9wfplxofIyxA7ypbCHnaitujfjEdd7ZS1BcRbOqLIGiPqeagHog9TnUFRfUM1BOStrp5In1nOQs8t2wt6azCjnQ3zJXJS1TfWt5bNYQ1Htkb7t_Ix-AvkJZIw6ilp60weo3qm4mz2HEfw_9oa8ALFUs6bJOLg3QJ7j4iHWIXlGjjrj7xkDH9z_vTkV6s6Wa-LsP-sc6vnpXBl7jvW5MjsrZD09f17Ln79V7Z91BPaqzG94WMW9fE_a99r9QHVOwfo8J-sQOlOpM9DAA3TLjlUnKdAjbp7ftTGMn-lneT6CYxD31fO5nhvpT96hPql66r7p09wYxnDubA_tUXVOBTFo2nHn61BDqftFh3MPeLaCc7dgH5tjfI_Y_LL1aK2GjnPBdo5COQhqSbJ7go3v7Wwy6V0iDtVaKfjW6H_prInv2zOfZ0382uWXOhsPn6J4xWDNR53Hf7bP8d_P9bq-f4L-HM8wqbeHHjTPYrXBEw8bOZ_OFTHHbltMI-j7SlWuTlrwHK2HBzTx51vtDCHzK76zc4iu9sI8z_oCjg_sDbF_XI5c_zht4kUf42Ja5Z12RoG5AvWbe4P8HJ67ZS0YY4bs71Xy1YitMJ_11kF31mMRp5WNc1A6D-ptg52Tavw3Ree2EWsrV4Sdoc3iGVAPG_0bq1-pVlP99wNroe6kcwn6nwDPH-ksJ2tP7vySztnZWTDYTS_nDvpYxtve1qBEbG69M9YKmNcSR38PXcHZTF8r_WHN55k8p3hL-ctvP3085n-RMO_8aSwXf0lX8frmqhGPdEbdBPEHfFJB3-f8IOdg3soaWD6FfYN88nzTsfKDjptn-ETWsf5Fjv8iNfKDeAl_gz2FWZpjHvgdzTlszfsB_Abyy6SbwQcPeCaC47hnP8-ParTNGm517cYgh8H_s0Aj_LPrZ_sPGP7-Vv337cXq9ms2vx8-fHnzotfCH8X-9X66XD68fDF--Lqc_PozxuIjD49eHj184R_c1kdv_jjSv8iO3rzCn8yO7D9kR296vJlM77bZ0ZvN1-305dF09ZDPh8UX_vNus9MdpuG_1M5Wd9OJDfvz5dHm9g4jZlx9x7WP_vwv

@kiwiyou
Copy link
Contributor

kiwiyou commented Feb 29, 2024

When the svg document contains a , (comma) inside, satori's regex greedily matches the group encodingType up to the last occurrence of ,. This results in incorrect parsing of encodingType and dataString.

@steven-tey
Copy link
Author

not sure if that's the root case @kiwiyou, just tested out the preview URL in the PR and it doesn't seem to work: playground link

CleanShot 2024-03-05 at 18 46 00@2x

@kiwiyou
Copy link
Contributor

kiwiyou commented Mar 5, 2024

@steven-tey After some inspecting, it turns out that there are two problems in satori's image rendering process:

  • Invalid parsing of plaintext data URI for svgs.
  • Invalid caching of remote images.

The issue @fonstack reported seems to be the first case, which is explained and resolved, and this issue the other. Check #597 and #596 for the former.

The root cause of the second case is that satori clears the cache for image sizes on every invocation without clearing the cache for remote image fetching. The first invocation of satori actually does render the image well. But rendering with the same remote image URL again makes it assume that the image has already fetched and its size has been cached, only to fail because of the cleared cache. In the other words, it is not that images in SVG doesn't work when exported to PNG, but that images in SVG doesn't work when rendered again. Please correct me if I'm wrong.

Some reference:

  • satori clearing the image size cache

    satori/src/satori.ts

    Lines 92 to 93 in 9bc47fd

    cache.clear()
    await preProcessNode(element)
  • satori's global fetching cache
    const inflightRequests = new Map<string, Promise<ResolvedImageData>>()
  • satori updating image size cache only on the first fetch

    satori/src/handler/image.ts

    Lines 226 to 228 in 9bc47fd

    if (inflightRequests.has(src)) {
    return inflightRequests.get(src)
    }

    satori/src/handler/image.ts

    Lines 235 to 271 in 9bc47fd

    const promise = fetch(url)
    .then((res): Promise<string | ArrayBuffer> => {
    const type = res.headers.get('content-type')
    // Handle SVG specially
    if (type === 'image/svg+xml' || type === 'application/svg+xml') {
    return res.text()
    }
    return res.arrayBuffer()
    })
    .then((data) => {
    if (typeof data === 'string') {
    try {
    const newSrc = `data:image/svg+xml;base64,${btoa(data)}`
    // Parse the SVG image size
    const imageSize = parseSvgImageSize(url, data)
    return [newSrc, ...imageSize] as ResolvedImageData
    } catch (e) {
    throw new Error(`Failed to parse SVG image: ${e.message}`)
    }
    }
    const [newSrc, imageSize] = arrayBufferToDataUri(data)
    return [newSrc, ...imageSize] as ResolvedImageData
    })
    .then((result) => {
    cache.set(url, result)
    return result
    })
    .catch((err) => {
    console.error(`Can't load image ${url}: ` + err.message)
    cache.set(url, [])
    return [] as const
    })
    inflightRequests.set(url, promise)

@kiwiyou
Copy link
Contributor

kiwiyou commented Mar 5, 2024

Maybe the correct workaround is one of two:

  • Clear the inflightRequests cache on every invocation.
  • Update cache on early return based on inflightRequests cache.

@fonstack
Copy link

Hey @kiwiyou @steven-tey @Jackie1210 any update on this? I need this to be fixed in order to launch a product in production :)

@kiwiyou
Copy link
Contributor

kiwiyou commented Mar 12, 2024

@fonstack Your problem should be fixed in #596.

@fonstack
Copy link

fonstack commented Mar 12, 2024

@fonstack Your problem should be fixed in #596.

thanks! any estimated time for the merge of the PR? @kiwiyou

@kiwiyou
Copy link
Contributor

kiwiyou commented Mar 12, 2024

I have neither privilege nor enough information for the estimation. Would you refer to the relevant issue #597 and PR #596, instead of this issue #592, and wait for the maintainer's response? Your problem doesn't seem to be directly related to this issue. (Check comment #592 (comment))

@seth2810
Copy link

#592 (comment)

The root cause of the second case is that satori clears the cache for image sizes on every invocation without clearing the cache for remote image fetching

It also leads to memory leak for each image being fetched by satori in our case. Maybe it's worth applying the configurable LRU cache here, or export the method to clear inflightRequests cache so that it can be cleared from the client code when it makes sense?

Screenshot 2024-08-16 at 20 35 46

Screenshot 2024-08-16 at 20 38 14

@kiwiyou @shuding

@seth2810
Copy link

I found a workaround for this - all images need to be preloaded and added as base64 data to the content of rendered component.

@steven-tey
Copy link
Author

@seth2810 just tried the base64 workaround – while the error is no longer there, the image still doesn't seem to be showing up in the PNG mode: https://d.to/qAR1HrH

How were you able to get it to work? 👀

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 a pull request may close this issue.

4 participants