Flip most of an svg image, but not the text within it Flip most of an svg image, but not the text within it node.js node.js

Flip most of an svg image, but not the text within it


Manipulating the SVG, all you need to do is

  • move the <text> elements outside the group with the transform attribute
  • recompute the y attribute with

    y -> 940 - y

    This supposes that the transform attribute is always transform="scale(1, -1) translate(0, -900)" and the font size 80px. A general formula would be

    y -> (negative y translation + font size / 2) - y

Here is a node script utilizing cheerio to change existing files:

const cheerio = require('cheerio');const $ = cheerio.load(xmlText, { xmlMode: true });function flipTextInPlace ($) {    $('text').each(() => {       const y = $(this).attr('y');       $(this).attr('y', 940 - y);    }).appendTo('svg');}


I hope this helps:

text:nth-child(1) {  transform: scale(1, -1);  transform-origin: 317px 812px;/*same as text coords*/  dominant-baseline: hanging;}
<svg version="1.1" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">  <g stroke="lightgray" stroke-dasharray="1,1" stroke-width="1" transform="scale(4, 4)">    <line x1="0" y1="0" x2="256" y2="256"></line>    <line x1="256" y1="0" x2="0" y2="256"></line>    <line x1="128" y1="0" x2="128" y2="256"></line>    <line x1="0" y1="128" x2="256" y2="128"></line>  </g>  <g transform="scale(1, -1) translate(0, -900)">      <style>        .stroke1 {fill: #BF0909;}        .stroke2 {fill: #BFBF09;}        .stroke3 {fill: #09BF09;}        .stroke4 {fill: #09BFBF;}        .stroke5 {fill: #0909BF;}        .stroke6 {fill: #BF09BF;}        .stroke7 {fill: #BFBFBF;}        .stroke8 {fill: #090909;}        text {            font-family: Helvetica;            font-size: 80px;            fill: #FFFFFF;            paint-order: stroke;            stroke: #000000;            stroke-width: 4px;            stroke-linecap: butt;            stroke-linejoin: miter;            font-weight: 800;        }      </style>      <path class="stroke1" d="M 272 567 Q 306 613 342 669 Q 370 718 395 743 Q 405 753 400 769 Q 396 782 365 808 Q 337 827 316 828 Q 297 827 305 802 Q 318 769 306 741 Q 267 647 207 560 Q 150 476 72 385 Q 60 375 58 367 Q 54 355 70 358 Q 82 359 109 384 Q 155 421 213 493 Q 226 509 241 527 L 272 567 Z" fill="#BF0909"></path>      <path class="stroke2" d="M 241 527 Q 262 506 258 375 Q 258 374 258 370 Q 254 253 221 135 Q 215 114 224 80 Q 236 44 248 32 Q 267 16 279 44 Q 294 86 294 134 Q 303 420 314 485 Q 321 515 295 543 Q 289 549 272 567 C 251 589 227 553 241 527 Z" fill="lightgray"></path>      <path class="stroke3" d="M 521 560 Q 561 621 602 708 Q 620 751 638 773 Q 645 786 639 799 Q 633 811 602 830 Q 572 846 554 843 Q 535 839 546 817 Q 561 795 552 757 Q 513 619 407 448 Q 398 436 397 430 Q 394 418 409 423 Q 439 432 503 532 L 521 560 Z" fill="lightgray"></path>      <path class="stroke4" d="M 503 532 Q 527 510 555 520 Q 795 608 782 549 Q 783 543 743 468 Q 736 458 741 453 Q 745 447 756 459 Q 852 532 894 549 Q 904 552 905 561 Q 906 574 876 592 Q 852 605 828 621 Q 800 637 783 630 Q 686 590 521 560 C 492 555 479 550 503 532 Z" fill="lightgray"></path>      <path class="stroke5" d="M 568 72 Q 531 81 494 91 Q 482 94 483 86 Q 484 79 494 71 Q 569 7 596 -33 Q 611 -49 626 -36 Q 659 -3 661 82 Q 655 149 655 345 Q 656 382 667 407 Q 676 426 659 439 Q 634 461 604 470 Q 585 477 577 469 Q 571 462 582 447 Q 619 384 603 127 Q 597 82 589 74 Q 582 67 568 72 Z" fill="lightgray"></path>      <path class="stroke6" d="M 444 320 Q 419 262 385 208 Q 364 180 381 144 Q 388 128 409 139 Q 460 181 468 264 Q 472 295 467 319 Q 463 328 456 328 Q 449 327 444 320 Z" fill="lightgray"></path>      <path class="stroke7" d="M 738 307 Q 789 249 847 168 Q 860 146 876 139 Q 885 138 893 146 Q 908 159 900 204 Q 891 264 743 338 Q 734 345 731 332 Q 728 319 738 307 Z" fill="lightgray"></path>        <g>      <text x="317" y="812">1</text>      <text x="273" y="558">2</text>      <text x="556" y="828">3</text>      <text x="513" y="532">4</text>      <text x="586" y="463">5</text>      <text x="455" y="316">6</text>      <text x="742" y="326">7</text>    </g>  </g> </svg>

Unfortunately this works on Chrome & Firefox but not on Safari. Didn't check Edge & IE.


You could use css transform on your text nodes

Edit

Inspired from @enxaneta answer, you could run this little script

const text = document.querySelectorAll("text");text.forEach(function(el){     el.style.transformOrigin = el.getAttribute('x')+'px '+el.getAttribute('y')+'px';  el.style.transform = "scale(1,-1)";     });
text{  dominant-baseline:central;}
<svg version="1.1" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">  <g stroke="lightgray" stroke-dasharray="1,1" stroke-width="1" transform="scale(4, 4)">    <line x1="0" y1="0" x2="256" y2="256"></line>    <line x1="256" y1="0" x2="0" y2="256"></line>    <line x1="128" y1="0" x2="128" y2="256"></line>    <line x1="0" y1="128" x2="256" y2="128"></line>  </g>  <g transform="scale(1, -1) translate(0, -900)">      <style>        .stroke1 {fill: #BF0909;}        .stroke2 {fill: #BFBF09;}        .stroke3 {fill: #09BF09;}        .stroke4 {fill: #09BFBF;}        .stroke5 {fill: #0909BF;}        .stroke6 {fill: #BF09BF;}        .stroke7 {fill: #BFBFBF;}        .stroke8 {fill: #090909;}        text {            font-family: Helvetica;            font-size: 80px;            fill: #FFFFFF;            paint-order: stroke;            stroke: #000000;            stroke-width: 4px;            stroke-linecap: butt;            stroke-linejoin: miter;            font-weight: 800;        }      </style>      <path class="stroke1" d="M 272 567 Q 306 613 342 669 Q 370 718 395 743 Q 405 753 400 769 Q 396 782 365 808 Q 337 827 316 828 Q 297 827 305 802 Q 318 769 306 741 Q 267 647 207 560 Q 150 476 72 385 Q 60 375 58 367 Q 54 355 70 358 Q 82 359 109 384 Q 155 421 213 493 Q 226 509 241 527 L 272 567 Z" fill="#BF0909"></path>      <path class="stroke2" d="M 241 527 Q 262 506 258 375 Q 258 374 258 370 Q 254 253 221 135 Q 215 114 224 80 Q 236 44 248 32 Q 267 16 279 44 Q 294 86 294 134 Q 303 420 314 485 Q 321 515 295 543 Q 289 549 272 567 C 251 589 227 553 241 527 Z" fill="lightgray"></path>      <path class="stroke3" d="M 521 560 Q 561 621 602 708 Q 620 751 638 773 Q 645 786 639 799 Q 633 811 602 830 Q 572 846 554 843 Q 535 839 546 817 Q 561 795 552 757 Q 513 619 407 448 Q 398 436 397 430 Q 394 418 409 423 Q 439 432 503 532 L 521 560 Z" fill="lightgray"></path>      <path class="stroke4" d="M 503 532 Q 527 510 555 520 Q 795 608 782 549 Q 783 543 743 468 Q 736 458 741 453 Q 745 447 756 459 Q 852 532 894 549 Q 904 552 905 561 Q 906 574 876 592 Q 852 605 828 621 Q 800 637 783 630 Q 686 590 521 560 C 492 555 479 550 503 532 Z" fill="lightgray"></path>      <path class="stroke5" d="M 568 72 Q 531 81 494 91 Q 482 94 483 86 Q 484 79 494 71 Q 569 7 596 -33 Q 611 -49 626 -36 Q 659 -3 661 82 Q 655 149 655 345 Q 656 382 667 407 Q 676 426 659 439 Q 634 461 604 470 Q 585 477 577 469 Q 571 462 582 447 Q 619 384 603 127 Q 597 82 589 74 Q 582 67 568 72 Z" fill="lightgray"></path>      <path class="stroke6" d="M 444 320 Q 419 262 385 208 Q 364 180 381 144 Q 388 128 409 139 Q 460 181 468 264 Q 472 295 467 319 Q 463 328 456 328 Q 449 327 444 320 Z" fill="lightgray"></path>      <path class="stroke7" d="M 738 307 Q 789 249 847 168 Q 860 146 876 139 Q 885 138 893 146 Q 908 159 900 204 Q 891 264 743 338 Q 734 345 731 332 Q 728 319 738 307 Z" fill="lightgray"></path>      <text x="317" y="812">1</text>      <text x="273" y="558">2</text>      <text x="556" y="828">3</text>      <text x="513" y="532">4</text>      <text x="586" y="463">5</text>      <text x="455" y="316">6</text>      <text x="742" y="326">7</text>  </g></svg>