Как я могу экспортировать JavaFX node в образ SVG?

Другими словами, я пытаюсь что-то сделать с JavaFX, как Батик позволяет вам делать с Swing.

Я хочу уметь отображать появление произвольного node в моем интерфейсе JavaFX, как Node.snapshot() does, за исключением того, что мне нужно мое изображение в векторном формате, таком как SVG, а не растровое изображение. (И вставка растрового снимка моего node в изображение SVG недостаточно хороша, оно должно быть правильным, масштабируемым векторным изображением.)

Это долгосрочный проект, поэтому я даже готов пойти на реализацию моего собственного GraphicsContext или любого другого эквивалента находится в API сохраненного режима JavaFX.

Кто-нибудь знает, есть ли способ сделать это? Я надеюсь, что это возможно в JavaFX?

Ответ 1

Я начал писать конвертер JavaFx Node в SVG, который "расширяет" ShapeConverter от Gerrit Grunwald, который только преобразует геометрию формы:

https://github.com/stefaneidelloth/JavaFxNodeToSvg

... и остановил его после определенного количества разочарований.

Не стесняйтесь улучшать его, добавлять функции и исправлять ошибки.

Конвертер работает для простых Node примеров, но не подходит для расширенных примеров, таких как диаграмма. Мой неудачный конвертер может служить вам отправной точкой. Текущее состояние иллюстрируется следующими цифрами. На левой стороне показан исходный JavaFx Node, а на правой стороне показан результат svg.

Простой Node пример (работает): введите описание изображения здесь

Пример диаграммы (не работает): введите описание изображения здесь

Соответствующие файлы svg доступны здесь:

https://github.com/stefaneidelloth/JavaFxNodeToSvg/tree/master/output

Некоторые дополнительные примечания

В дополнение к проблемам позиционирования, которые проиллюстрированы приведенным выше примером диаграммы, необходимо рассмотреть некоторые дополнительные проблемы:

JavaFx предоставляет больше функций css, чем стандартные элементы SVG. Например, (прямоугольный) регион JavaFx может иметь индивидуальные стили линий для каждой из четырех пограничных линий. Такой регион нельзя просто преобразовать в SVG rect. Вместо этого четыре пограничные линии Региона должны быть составлены индивидуально. Кроме того, каждый конец такой пограничной линии может иметь два отдельных краевых радиуса: вертикальный радиус и горизонтальный радиус. Чтобы преобразовать "четыре" граничные линии в соответствующие строки SVG... может потребоваться дальнейшее разделение границы: нарисуйте две округлые части и прямую часть для каждой из четырех пограничных линий. Поэтому в некоторых случаях может быть 12 элементов пути SVG для рисования границы одного региона JavaFx. Кроме того, фоновое заполнение Региона может иметь разные радиусы, чем граница региона. Для рисования фона региона может потребоваться еще несколько элементов SVG. Поэтому даже преобразование "прямоугольной области" может стать довольно сложным.

Также обратите внимание, что узлы JavaFx могут быть анимированы. Например, непрозрачность линии равна 0 в начале и исчезает до другого значения через несколько миллисекунд.

FadeTransition ft = new FadeTransition(Duration.millis(250), borderline);
                    ft.setToValue(0);                       
                    ft.play();

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

Я был бы очень рад, если конвертация графиков JavaFx будет работать в один прекрасный день, поэтому я могу использовать построение JavaFx с экспортом SVG в одном из моих проектов.

Я решил остановить разработку конвертера на данный момент и исследовать построение графика и экспорта SVG с помощью библиотек JavaScript (d3). Если эта стратегия окажется еще хуже, я могу вернуться к JavaFxNodeToSvgConverter.

Edit Заговор с d3.js работает очень хорошо, и я решил не использовать JavaFx для создания/создания svg. https://github.com/stefaneidelloth/javafx-d3

Ответ 2

Существует простая форма JavaFX для SVG-конвертера строк, она будет преобразовывать только основные формы без применения css, а не произвольные узлы, но, возможно, это может быть все, что вам нужно.

Ответ 3

В JFX JIRA есть открытый запрос об ошибке в

https://javafx-jira.kenai.com/browse/RT-38559

(требуется регистрация, вы можете проголосовать за этот вопрос). В настоящее время он говорит

Рассмотрим для будущей версии.

И помечено для версии 9.

Ответ 4

Идея состоит в том, что если вы можете преобразовать древовидную структуру узлов JavaFX в ряд заказов Graphics2D, вы можете использовать Batik, у которого есть драйвер Graphics2D.

Дело в том, что преобразование структуры дерева JavaFX в заказы Graphics2D не так сложно, как вы думаете (даже если вы обрабатываете свойства CSS узлов).

Некоторые читатели предположили, что я должен включить некоторый код, а не только ссылку на библиотеку и фотографии ее работы. Это не так просто сделать, потому что даже если это не так сложно сделать, у него все еще есть 5000 строк кода.

Как выполнить преобразование:

  • у вас должен быть Graphics2D для преобразования в SVG (например, класс Batik SVGGraphics2D);
  • итерация через структуру JavaFX;
  • для каждого Node в структуре, сначала преобразуйте текущие преобразования javaFX в этот Node в AffineTransform и примените преобразование к Node (вы должны сделать это в стеке, чтобы убедиться, что оно вернется к начальный AffineTransform в конце Node);
  • и для каждого Node вам необходимо преобразовать в соответствующие графические заказы Graphics2D.

Обратите внимание, что вам не нужно поддерживать много типов Node, в основном:

  • Регионы (элементы управления - особый тип региона, который может иметь связанную графику)
  • Группы
  • Формы (Текст, Прямоугольник и т.д.)
  • ImageView для изображений

Вам также может понадобиться позаботиться о Node Clipping, чтобы применить связанное Clipping в Graphics2D.

Также вам придется позаботиться о свойствах CSS Node.

При всей своей ценности библиотека, которую я использовал (которая использует этот алгоритм), находится здесь: http://sourceforge.net/projects/jfxconverter/

Ответ 5

Идея состоит в том, что если вы можете преобразовать древовидную структуру узлов JavaFX в последовательность заказов Graphics2D, то вы можете использовать Batik, который имеет драйвер Graphics2D.

Дело в том, что преобразование древовидной структуры JavaFX в заказы Graphics2D не так сложно, как вы думаете (даже если вы обрабатываете свойства CSS узлов).

Вам следует создать SVGGraphics2D из нового пустого SVGDocument, например:

Document doc = SVGDOMImplementation.getDOMImplementation().createDocument(SVGDOMImplementation.SVG_NAMESPACE_URI, "svg", null);
SVGGraphics2D g2D = new SVGGraphics2D(doc);

Затем вы получаете корневой узел сцены, которую хотите преобразовать, и для этого узла вы получаете тип узла, который может быть Shape, Control, Region, ImageView, Group, SubScene, Shape3D

В зависимости от каждого узла, вы можете получить характеристики узла. Например, для фигуры, если это линия, вы можете нарисовать линию в SVGGraphics2D. Например:

g2D.drawLine((int) line.getStartX(), (int) line.getStartY(), (int) line.getEndX(), (int) line.getEndY());

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

Затем вы перебираете детей Node и делаете то же самое рекурсивно.

В конце вы сможете сохранить документ в SVG, поскольку Batik позволяет делать это изначально.