LINUX.ORG.RU

Массовое изменение размера SVG-иконок

 


0

1

Дано: после дизайнера осталась куча иконок в виде svg. Размеры нестандартные типа 16 на 12 и тп. Мне нужно: изменить размер каждой картинки на каноничный (ширина=высоте). Алгоритм, который сразу придумал: вычисляем максимальную сторону, через transform/translate добавляем недостающие прозрачные пиксели. Если путь проще? Imagemagick может для SVG дорисовать прозрачные пиксели, чтобы ширина была равна высоте, а заодно вычистить всякий мусор в виде метаифнормации?

★★

В SVG нет пикселей. Можно банально изменить координатную систему (несколькими способами, включая изменение viewBox, добавление transform и банальное изменение всех координат примитивов). Но объекты могут выходить за границы старой области видимости, поэтому может также понадобиться добавление clip. Не знаю зачем вам говномажик - xml разбирается-собирается в две строчки на любом языке.

slovazap ★★★★★ ()
Последнее исправление: slovazap (всего исправлений: 1)

Готового решения точно нет. Можно xml парсером нахимичить.

То есть менять:

<svg width="16" height="12">
    <rect/>
</svg>

на

<svg width="16" height="16">
    <g transform="translate(2)">
        <rect/>
    </g>
</svg>

а заодно вычистить всякий мусор в виде метаифнормации

https://github.com/RazrFalcon/svgcleaner

RazrFalcon ★★★★★ ()
Последнее исправление: RazrFalcon (всего исправлений: 1)
Ответ на: комментарий от tz4678

Вы описали решение для растра, что некорректно.

по определению должно быть неправильным

Ну с вашими тараканами я спорить не буду.

RazrFalcon ★★★★★ ()
Ответ на: комментарий от anonymous

Да, твоё предложение - действительно фигня. Предлагать инструменты общего назначения для задачи, которая может быть решена удобным инструметом узкого назначения - нечто очень сомнительное.

dear_amomynous_3 ()
import argparse
import fnmatch
import sys
from pathlib import Path
from string import Template

import svgutils


def scale(src, width=None, height=None, dst=None):
    dst = dst or src

    origin = svgutils.transform.fromfile(src)
    width = width or origin.width
    height = height or origin.height

    move_x, move_y = 0, 0
    try:
        origin_width = float(str(origin.width).rstrip('px'))
        if width > origin_width:
            move_x = (width / 2) - (origin_width / 2)
    except (ValueError, TypeError) as exc:
        pass

    try:
        origin_height = float(str(origin.height).rstrip('px'))
        if height > origin_height:
            move_y = (height / 2) - (origin_height / 2)
    except (ValueError, TypeError):
        pass

    print(f"Converting {src} to {dst}: width: {origin_width} -> {width}, "
          f"height: {origin_height} -> {height}, move: {move_x}, {move_y}")
    svg = svgutils.compose.SVG(src)
    fig = svgutils.compose.Figure(width, height, svg).move(move_x, move_y)
    fig.save(dst)


def main(argv=None):
    if argv is None:
        argv = sys.argv

    parser = argparse.ArgumentParser(description='Resize SVG.')
    parser.add_argument('files', metavar="FILE", nargs='+',
                        help='files to scale')
    parser.add_argument('--width', default=None, type=float,
                        help='destination width of SVG')
    parser.add_argument('--height', default=None, type=float,
                        help='destination height of SVG')
    parser.add_argument('--pattern', default="*",
                        help='pattern for files to scale')
    parser.add_argument('--template', default=None,
                        help='output filename template')

    args = parser.parse_args(argv[1:])

    for src in args.files:
        src = Path(src)
        if not src.is_file():
            continue

        if not fnmatch.fnmatch(src.name, args.pattern):
            continue

        if not args.template:
            dst = src
        else:
            template = Template(args.tempalte)
            dst = Path(template.safe_substitute(
                full_name=str(src),
                drive=src.drive,
                root=src.root,
                anchor=src.anchor,
                parent=src.parent,
                name=src.name,
                suffix=src.suffix,
                stem=src.stem,
                as_posix=src.as_posix(),
                as_uri=src.as_uri(),
            ))

        try:
            scale(src, args.width, args.height, dst)

        except Exception as exc:  # noqa
            print(f"Error resizing SVG: src={src}, dst={dst}: {exc}")


if __name__ == "__main__":
    sys.exit(main())

unC0Rr ★★★★★ ()
Ответ на: комментарий от RazrFalcon

Я вижу, вы специалист по svg. Можно задать вопрос?

Мне нужно было сохранить svg в png, я воспользовался статьёй с хабра:

https://habr.com/ru/post/254973/

Всё работает, но есть один баг: при наличии в svg текста выравненного по левому краю, при сохранении, в png он съезжает со своего места левее, чем был в svg.

Как это можно исправить?

anonymous ()
Ответ на: комментарий от unC0Rr

Рабочее решение:

#!/usr/bin/env python
import os
import sys
import svgutils
from pathlib import Path
os.chdir(Path.cwd())


def main(argv):
  src = Path(argv[0]).resolve()
  dst = Path(argv[1]).resolve()
  original = svgutils.transform.fromfile(src)
  w = float(original.width)
  h = float(original.height)
  m = max(w, h)
  x, y = 0, 0
  if w > h:
    y = (m - h) / 2
  else:
    x = (m - w) / 2
  fig = svgutils.compose.Figure(m, m, original.root)
  fig.move(x, y)
  fig.save(dst)


if __name__ == '__main__':
  sys.exit(main(sys.argv[1:]))

Я переписал твою копипасту.

tz4678 ★★ ()
Ответ на: комментарий от unC0Rr
<?xml version='1.0' encoding='ASCII' standalone='yes'?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="28.649px" viewBox="0 0 28.649px 28.649px" height="28.649px">
  <g transform="translate(6.58, 0) scale(1) ">
    <svg width="15.489" height="28.649" viewBox="0 0 15.489 28.649">
    <defs>
        <style>
            .cls-1{fill:#d8d8d8}
        </style>
    </defs>
    <g id="facebook-logo" transform="translate(-22.077)">
        <path id="Path_67" d="M36.983.006L33.268 0C29.094 0 26.4 2.767 26.4 7.051V10.3h-3.739a.584.584 0 0 0-.584.584V15.6a.584.584 0 0 0 .584.584H26.4v11.881a.584.584 0 0 0 .584.584h4.874a.584.584 0 0 0 .584-.584V16.18h4.368a.584.584 0 0 0 .584-.584v-4.71a.585.585 0 0 0-.584-.584h-4.371V7.546c0-1.325.316-2 2.041-2h2.5a.584.584 0 0 0 .584-.584V.59a.584.584 0 0 0-.581-.584z" class="cls-1" data-name="Path 67"/>
    </g>
</svg>
  </g>
</svg>

Грязный выхлоп получается, но всяко лучше чем вручную править с сотню иконок, которые недодизайнер не смог привести к нужному формату до его увольнения.

tz4678 ★★ ()
Ответ на: комментарий от tz4678

Пожалуйста. Да, практически такая же задача возникла неделю назад на работе, только привести не к квадратному виду, а к заданному размеру.

unC0Rr ★★★★★ ()
Ответ на: комментарий от RazrFalcon

А если тупо обернуть svg в svg?

<svg width="..." height="...">
    <g transform="...">
        тут включаем исходный svg без изменений, как есть
    </g>
</svg>

Насколько это корректно?

anonymous ()
Ответ на: комментарий от tz4678

Nested SVG мало кто поддерживает нормально.

viewBox=«0 0 28.649px 28.649px»

viewBox не поддерживает единицы измерения.

width=«28.649px» height=«28.649px»

Треш, нет?

RazrFalcon ★★★★★ ()
Ответ на: комментарий от anonymous

Всё зависит от того, чем конкретно вы рендерите. Полноценной поддержки текста нет ни в одном SVG-рендере.

Оценить степень поддержки текста в SVG можно тут: https://razrfalcon.github.io/resvg-test-suite/svg-support-table.html#e-text

в png он съезжает со своего места левее, чем был в svg

У текста «в svg» нет ширины. Соответственно его выравнивание полностью на плечах рендера. Если вы хотите хранить текст в SVG - используйте ровно один рендер, иначе всё обязательно поплывёт.

RazrFalcon ★★★★★ ()
Ответ на: комментарий от anonymous

Nested SVG плохо поддерживается. Детали можно посмотреть тут: https://razrfalcon.github.io/resvg-test-suite/svg-support-table.html#e-svg

Совсем без модификаций оставлять не стоит. Следует добавить overflow="visible" на вложенный svg, иначе будет создан лишний clipPath.

То есть

<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
    <g transform="translate(60 60)">
        <svg width="80" height="80" overflow="visible">
            <circle id="circle1" cx="40" cy="40" r="30" fill="green"/>
        </svg>
    </g>
</svg>

превратится в:

<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
    <circle id="circle1" cx="40" cy="40" r="30" fill="green" transform="matrix(1 0 0 1 60 60)"/>
</svg>

а не в:

<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
    <defs>
        <clipPath id="clipPath1">
            <path d="M 0 0 L 80 0 L 80 80 L 0 80 Z"/>
        </clipPath>
    </defs>
    <g clip-path="url(#clipPath1)" transform="matrix(1 0 0 1 60 60)">
        <circle id="circle1" cx="40" cy="40" r="30" fill="green"/>
    </g>
</svg>

RazrFalcon ★★★★★ ()
Ответ на: комментарий от RazrFalcon

Nested SVG плохо поддерживается. Детали можно посмотреть тут: https://razrfalcon.github.io/resvg-test-suite/svg-support-table.html#e-svg

Спасибо, это интересно.

будет создан лишний clipPath.

По мне, то, что вложенный svg будет обрезаться по своим размерам - это правильно. Зачем рисовать то, что не рисовалось изначально (обрезалось)?

anonymous ()
Ответ на: комментарий от anonymous

clipPath требует аллокации доп. слоя, что может существенно снизить скорость отрисовки на простых изображениях.

RazrFalcon ★★★★★ ()
Ответ на: комментарий от RazrFalcon

clipPath требует аллокации доп. слоя

Это не важно. Важна правильность. Автор рисунка, например, специально может рисовать круг, который не влезает и будет обрезан. Как раз это я имел в виду, говоря про правильность вложенных svg, то что вложенный svg меньшего размера не будет рисовать за пределами своих границ.

anonymous ()
Ответ на: комментарий от RazrFalcon

чем конкретно вы рендерите.

FF и chromium, svg нарисован в inscape, и скопирован в html.

У текста «в svg» нет ширины.

Да, есть точка, и выравнивание относительно неё.

Нашёл способ обойти баг: внуири <text> был <tspan>, если убрать <tspan>, и его атрибуты перенести внутрь <text>, то всё остаётся на своих местах.

anonymous ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.