Saturday 17 July 2021

Using Beautiful soup to create a looping svg animation from multiple svg frames

I'm trying to use Beautiful soup to create a looping SVG animation.

I have 3 svgs created from the same picture with some nodes adjusted. I'm trying to find all the path tags that share the same id between each of the 3 svgs, but which contain different values for the d attribute.

I would like this program to create a looping svg animation from the 3 svgs that I give as input to it


Here is a simple example where I give the three svgs at this fiddle

And receive this svg as a result

<svg height="30%" width="30%" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;" version="1.1" viewBox="0 0 1425 1235" width="100%" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:serif="http://www.serif.com/" xmlns:xlink="http://www.w3.org/1999/xlink">
<g>
<path d="M90.388,477.489c7.93,5.522 42.72,23.778 123.168,25.247l45.723,-146.361c35.197,-88.293 103.007,-173.582 188.525,-226.095c195.6,-120.109 443.676,-217.47 662.8,28.084c104.856,117.503 230.626,438.387 191.262,597.334c-7.603,30.699 -25.306,55.182 -28.377,78.869c32.654,102.706 75.639,172.731 131.01,239.734c42.352,51.247 -23.008,47.671 -73.889,22.326c-42.381,-21.111 -117.313,-82.062 -151.526,-129.06c-23.69,21.907 -48.312,43.978 -72.72,75.938c-5.334,6.985 -13.545,14.662 -24.216,22.653c-60.06,-5.641 -138.855,-2.492 -223.856,10.82c-119.253,18.677 -220.758,52.665 -272.729,87.741c-4.774,-1.651 -9.453,-3.461 -14.054,-5.403c-100.972,-42.615 -148.831,-90.767 -202.921,-151.15c-4.588,81.511 -47.64,156.094 -73.572,182.59c-48.323,49.376 -110.427,65.954 -65.61,-50.043c32.486,-84.083 42.849,-170.372 27.032,-257.969c4.406,9.107 7.802,14.023 15.683,21.643c-4.451,-4.367 -9.035,-8.794 -13.756,-13.276c-33.672,-31.964 -55.079,-100.363 -59.596,-171.002c-63.473,37.984 -111.095,23.103 -168.621,-0.648c-80.364,-123.877 66.105,-87.749 60.24,-241.972c-2.541,-1.752 -2.338,-2.214 -0.01,-0.416l0.01,0.416Z" id="body" style="fill:#e1e1c5;stroke:#000;stroke-width:12px;"><animate attributeName="d" begin="0s;body3.end" dur="1000ms" fill="freeze" id="body1" to="M90.388,477.489c7.93,5.522 42.72,23.778 123.168,25.247l45.723,-146.361c35.197,-88.293 103.007,-173.582 188.525,-226.095c195.6,-120.109 443.676,-217.47 662.8,28.084c104.856,117.503 230.626,438.387 191.262,597.334c-7.603,30.699 -16.034,56.471 -25.146,78.55c28.108,106.676 75.609,192.707 28.097,269.683c-34.29,52.099 -53.98,44.382 -64.302,-1.716c-4.855,-35.336 -40.168,-71.575 -61.431,-134.648c-23.69,21.907 -48.312,43.978 -72.72,75.938c-5.334,6.985 -13.545,14.662 -24.216,22.653c-60.06,-5.641 -138.855,-2.492 -223.856,10.82c-119.253,18.677 -220.758,52.665 -272.729,87.741c-4.774,-1.651 -9.453,-3.461 -14.054,-5.403c-100.972,-42.615 -148.831,-90.767 -202.921,-151.15c1.683,67.906 24.023,110.639 41.511,140.039c31.774,53.42 24.85,94.64 -62.422,23.645c-84.554,-89.521 -119.559,-192.668 -99.02,-288.78c7.752,6.502 15.583,13.697 23.464,21.317c-4.451,-4.367 -9.035,-8.794 -13.756,-13.276c-33.672,-31.964 -55.079,-100.363 -59.596,-171.002c-63.473,37.984 -111.095,23.103 -168.621,-0.648c-80.364,-123.877 66.105,-87.749 60.24,-241.972c-2.541,-1.752 -2.338,-2.214 -0.01,-0.416l0.01,0.416Z"/><animate attributeName="d" begin="body1.end" dur="1000ms" fill="freeze" id="body2" to="M90.388,477.489c7.93,5.522 42.72,23.778 123.168,25.247l45.723,-146.361c35.197,-88.293 103.007,-173.582 188.525,-226.095c195.6,-120.109 443.676,-217.47 662.8,28.084c104.856,117.503 230.626,438.387 191.262,597.334c-7.603,30.699 -25.306,55.182 -28.377,78.869c54.824,95.73 123.021,166.489 97.108,253.157c-19.597,59.211 -40.615,56.868 -62.545,15.027c-13.857,-32.867 -77.195,-101.72 -128.968,-135.184c-23.69,21.907 -48.312,43.978 -72.72,75.938c-5.334,6.985 -13.545,14.662 -24.216,22.653c-60.06,-5.641 -138.855,-2.492 -223.856,10.82c-119.253,18.677 -220.758,52.665 -272.729,87.741c-4.774,-1.651 -9.453,-3.461 -14.054,-5.403c-100.972,-42.615 -148.831,-90.767 -202.921,-151.15c-45.305,34.069 -77.472,148.605 -73.572,182.59c7.086,61.75 -16.126,96.51 -66.629,-4.019c-40.43,-116.313 -30.079,-224.746 28.051,-303.993c4.406,9.107 7.802,14.023 15.683,21.643c-4.451,-4.367 -9.035,-8.794 -13.756,-13.276c-33.672,-31.964 -55.079,-100.363 -59.596,-171.002c-63.473,37.984 -111.095,23.103 -168.621,-0.648c-80.364,-123.877 66.105,-87.749 60.24,-241.972c-2.541,-1.752 -2.338,-2.214 -0.01,-0.416l0.01,0.416Z"/><animate attributeName="d" begin="body2.end" dur="1000ms" fill="freeze" id="body3" to="M90.388,477.489c7.93,5.522 42.72,23.778 123.168,25.247l45.723,-146.361c35.197,-88.293 103.007,-173.582 188.525,-226.095c195.6,-120.109 443.676,-217.47 662.8,28.084c104.856,117.503 230.626,438.387 191.262,597.334c-7.603,30.699 -25.306,55.182 -28.377,78.869c32.654,102.706 75.639,172.731 131.01,239.734c42.352,51.247 -23.008,47.671 -73.889,22.326c-42.381,-21.111 -117.313,-82.062 -151.526,-129.06c-23.69,21.907 -48.312,43.978 -72.72,75.938c-5.334,6.985 -13.545,14.662 -24.216,22.653c-60.06,-5.641 -138.855,-2.492 -223.856,10.82c-119.253,18.677 -220.758,52.665 -272.729,87.741c-4.774,-1.651 -9.453,-3.461 -14.054,-5.403c-100.972,-42.615 -148.831,-90.767 -202.921,-151.15c-4.588,81.511 -47.64,156.094 -73.572,182.59c-48.323,49.376 -110.427,65.954 -65.61,-50.043c32.486,-84.083 42.849,-170.372 27.032,-257.969c4.406,9.107 7.802,14.023 15.683,21.643c-4.451,-4.367 -9.035,-8.794 -13.756,-13.276c-33.672,-31.964 -55.079,-100.363 -59.596,-171.002c-63.473,37.984 -111.095,23.103 -168.621,-0.648c-80.364,-123.877 66.105,-87.749 60.24,-241.972c-2.541,-1.752 -2.338,-2.214 -0.01,-0.416l0.01,0.416Z"/></path>
<path d="M585.563,1164.72c51.971,-35.076 153.476,-69.064 272.729,-87.741c85.001,-13.312 163.796,-16.461 223.856,-10.82c-86.627,65.685 -334.207,156.146 -496.585,98.561Z" style="fill:#eeeede;stroke:#000;stroke-width:8.5px;"/>
<path d="M491.04,1016.28c25.012,82.237 -161.376,-72.451 -218.335,-247.372c-68.632,-210.764 57.284,-360.766 52.17,-274.215c-8.661,146.603 41.575,221.509 78.637,257.145c42.743,41.098 -9.601,103.399 87.528,264.442Z" style="fill:#c4c59b;"/>
<path d="M608.243,298.276c286.93,-208.386 579.038,53.527 581.314,239.179c1.311,106.901 -46.786,260.387 -181.162,379.856c-137.592,122.327 -372.812,92.552 -453.901,-2.045c-189.563,-221.139 -39.975,-548.922 53.749,-616.99Z" style="fill:#e0d4bc;stroke:#000;stroke-width:8.5px;"/>
<path d="M608.243,298.276c286.93,-208.386 579.038,53.527 581.314,239.179c1.311,106.901 -46.786,260.387 -181.162,379.856c-137.592,122.327 -372.812,92.552 -453.901,-2.045c-189.563,-221.139 -39.975,-548.922 53.749,-616.99Z" style="fill:#e0d4bc;stroke:#000;stroke-width:8.5px;"/>
<path d="M565.155,377.691c-23.615,-75.845 254.333,-256.51 424.936,-115.441c145.506,120.315 126.534,227.928 46.861,172.366c-57.471,-40.079 -48.051,54.217 -83.708,90.688c-35.657,36.471 -29.422,-9.567 -21.867,-19.041c7.554,-9.473 23.436,-96.936 1.747,-124.383c-58.176,-73.624 -198.545,-148.967 -367.969,-4.189" style="fill:#434132;"/>
<path d="M361.774,452.286c15.776,39.392 326.932,-421.84 620.369,-234.755c101.254,64.556 54.669,-74.266 44.171,-104.085c-78.269,-222.301 -821.486,-53.05 -664.54,338.84Z" style="fill:#9b9365;"/>
<path d="M662.652,556.91c0,0 81.041,73.154 119.613,41.832c72.749,-59.073 164.032,-3.563 145.686,-29.31c-58.111,-81.55 -170.816,-64.419 -186.627,-92.966c-46.561,-84.068 23.43,-97.087 104.307,-84.059c46.312,7.46 12.796,-60.085 -72.787,-51.897c-85.584,8.189 -59.975,-36.02 -213,113.129c-45.652,44.497 -43.024,107.692 -14.766,131.964c24.351,20.917 76.667,11.144 117.574,-28.693Z" style="fill:#bbae84;"/>
<circle cx="761.392" cy="595.569" r="569.218" style="fill:none;"/>
<ellipse cx="761.348" cy="588.604" rx="573.642" ry="587.275" style="fill:none;"/>
<path d="M732.491,416.621c0,-0 88.324,-88.758 120.589,57.231c2.426,10.974 -25.186,-8.243 -43.227,-6.769c-16.291,1.331 -99.375,10.412 -77.362,-50.462Z" style="stroke:#000;stroke-width:1px;"/>
<path d="M732.491,416.621c0,-0 88.324,-88.758 120.589,57.231c2.426,10.974 -25.186,-8.243 -43.227,-6.769c-16.291,1.331 -99.375,10.412 -77.362,-50.462Z" style="stroke:#000;stroke-width:1px;"/>
<path d="M815.097,462.006c-0,-0 14.765,-10.241 16.439,-19.155c0.565,-3.008 4.131,24.514 -16.439,19.155Z" style="fill:#fff;stroke:#000;stroke-width:1px;"/>
<path d="M815.097,462.006c-0,-0 14.765,-10.241 16.439,-19.155c0.565,-3.008 4.131,24.514 -16.439,19.155Z" style="fill:#fff;stroke:#000;stroke-width:1px;"/>
<path d="M1019.77,559.134c8.027,-34.362 95.3,-29.021 72.988,41.685c-18.787,59.537 -77.6,-21.939 -72.988,-41.685Z" style="stroke:#000;stroke-width:1px;"/>
<path d="M1019.77,559.134c8.027,-34.362 95.3,-29.021 72.988,41.685c-18.787,59.537 -77.6,-21.939 -72.988,-41.685Z" style="stroke:#000;stroke-width:1px;"/>
<use height="28px" transform="matrix(-0.951159,0.308701,-0.308701,-0.951159,3393.28,2047.46)" width="15px" x="1782" xlink:href="#_Image1" y="2126"/>
<use height="28px" transform="matrix(-0.951159,0.308701,-0.308701,-0.951159,3393.28,2047.46)" width="15px" x="1782" xlink:href="#_Image1" y="2126"/>
<path d="M658.11,415.392c0,-0 69.359,-48.596 37.383,-54.906c-8.234,-1.625 -37.383,54.906 -37.383,54.906Z" style="fill:#090000;stroke:#000;stroke-width:1px;"/>
<path d="M887.503,728.479c0,0 15.894,-29.445 14.615,-42.536c-1.279,-13.091 -67.177,-73.041 -39.424,-101.493c27.69,-28.388 69.532,29.34 83.942,33.43c29.391,8.341 66.001,19.317 60.459,45.084c-4.119,19.147 -69.188,26.07 -82.039,29.221c-12.852,3.151 -15.381,3.779 -37.553,36.294Z" style="fill:#434343;stroke:#000;stroke-width:1px;"/>
<g style="filter:url(#_Effect2);">
<path d="M887.503,728.479c0,0 15.894,-29.445 14.615,-42.536c-1.279,-13.091 -67.177,-73.041 -39.424,-101.493c27.69,-28.388 69.532,29.34 83.942,33.43c29.391,8.341 66.001,19.317 60.459,45.084c-4.119,19.147 -69.188,26.07 -82.039,29.221c-12.852,3.151 -15.381,3.779 -37.553,36.294Z" style="fill:#434343;stroke:#000;stroke-width:1px;"/>
</g>
</g>
<path d="M672.368,633.498c-0,-0 31.856,103.495 119.981,87.051c88.125,-16.444 129.545,73.261 127.572,80.472" style="fill:none;stroke:#000;stroke-width:9px;"/>
<path d="M941.027,678.956c-0,-0 43.242,-18.237 49.164,-24.159c0.697,-0.697 -20.115,8.077 -28.937,11.448c-4.846,1.851 -25.415,12.711 -20.227,12.711Zm-65.763,-84.565c0.981,0.981 9.94,15.582 18.407,30.183c8.468,14.601 16.444,29.202 15.463,30.184c-1.963,1.963 -43.225,-69.722 -33.87,-60.367Z" id="nostrils" style="stroke:#000;stroke-width:1px;"><animate attributeName="d" begin="0s;nostrils3.end" dur="1000ms" fill="freeze" id="nostrils1" to="M941.027,678.956c-0,-0 44.385,0.596 49.164,-18.604c4.779,-19.201 -25.353,-17.04 -34.175,-13.67c-4.846,1.852 -26.057,29.547 -14.989,32.274Zm-65.763,-84.565c9.954,-1.864 25.298,4.262 34.715,15.336c9.417,11.075 12.907,27.098 -0.845,45.031c-16.643,21.702 -46.874,-57.933 -33.87,-60.367Z"/><animate attributeName="d" begin="nostrils1.end" dur="1000ms" fill="freeze" id="nostrils2" to="M941.027,678.956c-0,-0 44.385,0.596 49.164,-18.604c4.779,-19.201 -25.353,-17.04 -34.175,-13.67c-4.846,1.852 -26.057,29.547 -14.989,32.274Zm-65.763,-84.565c9.954,-1.864 25.298,4.262 34.715,15.336c9.417,11.075 12.907,27.098 -0.845,45.031c-16.643,21.702 -46.874,-57.933 -33.87,-60.367Z"/><animate attributeName="d" begin="nostrils2.end" dur="1000ms" fill="freeze" id="nostrils3" to="M941.027,678.956c-0,-0 43.242,-18.237 49.164,-24.159c0.697,-0.697 -20.115,8.077 -28.937,11.448c-4.846,1.851 -25.415,12.711 -20.227,12.711Zm-65.763,-84.565c0.981,0.981 9.94,15.582 18.407,30.183c8.468,14.601 16.444,29.202 15.463,30.184c-1.963,1.963 -43.225,-69.722 -33.87,-60.367Z"/></path>
<path d="M378.262,391.259c-44.772,30.19 -16.022,-68.092 32.522,-113.601c53.645,-50.29 -9.824,-42.449 -10.424,-74.046c-0.599,-31.597 300.539,-278.18 459.189,-160.546c200.64,148.769 107.416,117.626 -56.71,107.656c-109.157,-6.631 -312.99,165.291 -424.577,240.537Z" style="fill:#434132;"/>
<path d="M1018.06,639.992c-0,0 -83.794,-70.286 -34.784,-137.771c49.011,-67.485 34.15,-0.258 127.493,-27.957c49.156,-14.586 15.332,-39.879 11.529,-87.767c-3.803,-47.888 58.398,140.546 17.567,163.167c-40.83,22.621 -22.8,-44.471 -64.735,-39.486c-41.936,4.985 -82.338,20.343 -60.925,77.961c21.413,57.618 19.007,44.188 3.855,51.853Z" style="fill:#c7caab;"/>
<path d="M1235.07,510.204c-22.142,50.713 -99.804,-256.368 -168.88,-248.605c-49.132,5.522 36.505,-105.472 -4.805,-146.448c-41.31,-40.976 130.645,93.481 136.838,216.169c6.192,122.688 72.566,97.075 36.847,178.884Z" style="fill:#c7caab;"/>
<path d="M52.356,689.354c0,-0 18.398,-62.886 70.355,-66.715c52.272,-3.852 -49.634,-48.569 51.28,-108.221c44.556,-26.338 20.83,66.235 35.391,126.714c14.561,60.478 -58.286,86.654 -65.027,68.89c-6.741,-17.765 -50.149,49.164 -91.999,-20.668Z" style="fill:#799f90;"/>
<path d="M1243.04,891.109c-0,0 19.388,-10.356 30.649,12.061c11.261,22.416 18.509,29.142 -0.674,35.51c-19.183,6.368 -18.397,-3.773 -39.132,-27.278c-7.505,-8.508 -12.926,-14.875 9.157,-20.293Z" style="fill:#a3bca6;"/>
<path d="M820.715,636.96c3.651,0 10.509,1.258 12.136,5.182c2.529,6.098 2.631,13.246 3.683,19.589c7.669,46.217 -19.897,-5.243 -13.25,-18.766" style="fill:none;"/>
<path d="M826.565,644.266l-0,-0.505" style="fill:none;stroke:#000;stroke-width:8.5px;"/>
<path d="M803.97,664.549l0.496,-0" style="fill:none;stroke:#000;stroke-width:8.5px;"/>
<path d="M788.903,639.898l0.994,0" style="fill:none;stroke:#000;stroke-width:8.5px;"/>
<path d="M972.19,708.925l0.497,0" style="fill:none;stroke:#000;stroke-width:8.5px;"/>
<path d="M992.729,727.674l0,1.491" style="fill:none;stroke:#000;stroke-width:8.5px;"/>
<path d="M967.291,734.835c-0,1.643 -0.777,5.473 0.993,5.473" style="fill:none;stroke:#000;stroke-width:8.5px;"/>
<path d="M774.437,665.011l-0,-0.505" style="fill:none;stroke:#000;stroke-width:8.5px;"/>
<path d="M754.18,627.564l0,-1.01" style="fill:none;stroke:#000;stroke-width:8.5px;"/>
<path d="M996.541,765.558c-0,-0.743 0.119,-0.386 -0.505,-1.01" style="fill:none;stroke:#000;stroke-width:8.5px;"/>
<path d="M1026.8,747.332l-0,-2.022" style="fill:none;stroke:#000;stroke-width:8.5px;"/>
<defs>
<image height="28px" id="_Image1" width="15px" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAcCAYAAAC+lOV/AAAACXBIWXMAAA7EAAAOxAGVKw4bAAACDElEQVQ4ja2TT6sSURiHf47j3PfMnzs6YeQiGYVE2olQu0GFFhGRpLvSlbSxpQQtXAZBqz5Aq+7OlYvyOwQuXIkLIQkU2ugdzVSGOW00rle9o9Rvd857nvO8B94D/EP8XgcYY68dx5EBfD/5dkEQFgA4AHmn5gXruv513UH9ZDMRfaxWq5yI5gC0k8yr1eqnLMtuLpdzBUEonwQzxu5GIhGhVqspiqK8AeA7Giai+7FYDJlMBrquGwAeHA07jhOLx+Pw+XzI5/OSKIqPvJhNzgKBwNK2bc45581mkxuG8e1Y+Ekqlbrk64zHYy5J0hLAmWfbuq6/LJfL55t1MBiEaZoLAGkvq0hE08FgwK/GsqwJgKdeZss0TTcajW5tEpHPs21VVV+USiX1+j5jTPCCBdd1C8Vicad+jPlhOBwWEonETsG2bRfA4lDHUFX1U71ed/ieGIYxA3DvEHubiOaj0WgH7PV6nDFm48p8b0VRlA+VSmWxz1qpVH4zxt4fsp4T0azf7++A3W5386fv7CUlSXpbKBR+XQen0yk3TXMWCAReHbKSLMuTTqezBQ6HQ55Op2eapl0cAiGKYjWbzc42kOu6vNVq8VAoNFcU5R1uGCiNMTZpt9t8tVrxRqPBk8nkVNO0H36///FB4/qtnwFwy7IuiWip63oHwLObbH/DGLsgoi8AngO45Qn8j/wBAgsYD1q8YF4AAAAASUVORK5CYII="/>
<filter filterUnits="userSpaceOnUse" height="250.665" id="_Effect2" width="250.594" x="806.471" y="527.222">
<feGaussianBlur in="SourceGraphic" stdDeviation="16.3028"/>
</filter>
</defs>
</svg>

Here is my python & BeautifulSoup program that creates this svg

from bs4 import BeautifulSoup
from re import compile

def handler_from_file(filename):
    html = open(filename, 'r')
    return BeautifulSoup(html, 'xml')

def group(svgs):
    """The first svg is a base img. Adds additional svgs as animation tags

    Args:
        svgs ([[string, int]]): [[svg filename, length of animation]]
    """
    base = handler_from_file(svgs[0][0])
    tags = base.find_all('path')
    soups = [handler_from_file(name) for [name, dur] in svgs]
    durs = [svg[1] for svg in svgs]

    for tag in tags:

        id = tag.get('id')
        d = tag.get('d')

        if not id:
            continue

        matches = [x for x in [s.find('path', id=id) for s in soups] if x is not None]
        if (len(matches) == 0):
            continue
        ds = [mch.get('d') for mch in matches]
        if (len(set(ds + [d])) == 1):
            continue

        # print(f"ds: {len(ds)}, durs: {len(durs)}, range: {range(1,len(ds))} ")
        for cnt in range(1,len(ds)):
            animId = f"{id}-{str(cnt)}"
            animate = base.new_tag('animate', id=animId)
            animate.attrs['to'] = ds[cnt]
            animate.attrs['attributeName'] = 'd'
            animate.attrs['dur'] = f"{durs[cnt]}ms"
            animate.attrs['fill'] = "freeze"
            
            if cnt == 1:    #If this is the first animation layer
                animate.attrs['begin'] = f"0s;{id}-{str(len(ds) - 1)}.end" #start the animation at 0s, and the end of the last animation(loops)
            else:
                animate.attrs['begin'] = f"{id}-{str(cnt - 1)}.end"     #start the animation at the end of the previous animation
            tag.append(animate)
            
    return base

def write(output, svg):
    with open(output, "w") as file:
        file.write(str(svg))


svgs = [
        ["./seal-1.svg", 1000],
        ["./seal-2.svg", 1000],
        ["./seal-3.svg", 1000],
]
output="seal-anim.svg"

animation = group(svgs)
write(output, animation)

With a larger example though, such as this one of someone playing trombone, which has many more layers, the animation is almost working, but the original image is staying there in the background

Here are all the files on a github repo for the second svg animation, along with the script to produce it



from Using Beautiful soup to create a looping svg animation from multiple svg frames

No comments:

Post a Comment