mirror of
https://github.com/processing/processing4.git
synced 2026-06-16 04:26:26 +02:00
Merge branch 'processing:main' into main-gradle
This commit is contained in:
@@ -67,16 +67,8 @@ jobs:
|
||||
architecture: ${{ matrix.arch }}
|
||||
- name: Setup Ant
|
||||
uses: cedx/setup-ant@v3
|
||||
- name: Install Certificates for Code Signing
|
||||
if: ${{ matrix.os_prefix == 'macos' }}
|
||||
uses: apple-actions/import-codesign-certs@v3
|
||||
with:
|
||||
p12-file-base64: ${{ secrets.CERTIFICATES_P12 }}
|
||||
p12-password: ${{ secrets.CERTIFICATES_P12_PASSWORD }}
|
||||
- name: Build Release
|
||||
run: ant -noinput -buildfile build/build.xml ${{ matrix.os_prefix }}-dist -Dversion="${{ github.sha }}"
|
||||
env:
|
||||
PROCESSING_APP_SIGNING: true
|
||||
- name: Add artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
|
||||
+2
-2
@@ -4,14 +4,14 @@
|
||||
<!-- Current version of Ant that works with Processing -->
|
||||
<!-- (Cannot use ant.version as the name of the property
|
||||
because that conflicts with the built-in variable) -->
|
||||
<property name="ant.version.num" value="1.10.12" />
|
||||
<property name="ant.version.num" value="1.10.15" />
|
||||
<!-- the .zip file to be downloaded -->
|
||||
<property name="ant.zip" value="apache-ant-${ant.version.num}-bin.zip" />
|
||||
|
||||
<!-- TODO implement a fallback URL that points to a location
|
||||
on download.processing.org so it's available forever. -->
|
||||
<property name="ant.url"
|
||||
value="https://archive.apache.org/dist/ant/binaries/${ant.zip}" />
|
||||
value="https://dlcdn.apache.org//ant/binaries/${ant.zip}" />
|
||||
|
||||
<fileset id="ant.files" dir="lib">
|
||||
<include name="ant.jar" />
|
||||
|
||||
@@ -25,6 +25,7 @@ menu.file.close = Close
|
||||
menu.file.save = Save
|
||||
menu.file.save_as = Save As...
|
||||
menu.file.export_application = Export Application...
|
||||
menu.file.export_pdez = Export as PDEZ...
|
||||
menu.file.page_setup = Page Setup
|
||||
menu.file.print = Print...
|
||||
menu.file.preferences = Preferences...
|
||||
|
||||
@@ -33,6 +33,9 @@ import java.awt.image.ColorModel;
|
||||
import java.awt.image.Raster;
|
||||
import java.awt.image.WritableRaster;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.awt.Color;
|
||||
|
||||
import processing.core.PApplet;
|
||||
import processing.core.PGraphics;
|
||||
import processing.core.PShapeSVG;
|
||||
@@ -96,16 +99,29 @@ public class PShapeJava2D extends PShapeSVG {
|
||||
*/
|
||||
|
||||
|
||||
static class LinearGradientPaint implements Paint {
|
||||
public static class LinearGradientPaint implements Paint {
|
||||
float x1, y1, x2, y2;
|
||||
float[] offset;
|
||||
int[] color;
|
||||
Color[] colors;
|
||||
int count;
|
||||
float opacity;
|
||||
AffineTransform xform;
|
||||
|
||||
public static enum CycleMethod {
|
||||
NO_CYCLE,
|
||||
REFLECT,
|
||||
REPEAT
|
||||
}
|
||||
|
||||
public static enum ColorSpaceType {
|
||||
SRGB,
|
||||
LINEAR_RGB
|
||||
}
|
||||
|
||||
public LinearGradientPaint(float x1, float y1, float x2, float y2,
|
||||
float[] offset, int[] color, int count,
|
||||
float opacity) {
|
||||
float opacity, AffineTransform xform) {
|
||||
this.x1 = x1;
|
||||
this.y1 = y1;
|
||||
this.x2 = x2;
|
||||
@@ -114,6 +130,13 @@ public class PShapeJava2D extends PShapeSVG {
|
||||
this.color = color;
|
||||
this.count = count;
|
||||
this.opacity = opacity;
|
||||
this.xform = xform;
|
||||
|
||||
//set an array of type Color
|
||||
this.colors = new Color[this.color.length];
|
||||
for (int i = 0; i < this.color.length; i++) {
|
||||
this.colors[i] = new Color(this.color[i], true);
|
||||
}
|
||||
}
|
||||
|
||||
public PaintContext createContext(ColorModel cm,
|
||||
@@ -125,6 +148,35 @@ public class PShapeJava2D extends PShapeSVG {
|
||||
(float) t2.getX(), (float) t2.getY());
|
||||
}
|
||||
|
||||
public Point2D getStartPoint() {
|
||||
return new Point2D.Float(this.x1, this.y1);
|
||||
}
|
||||
|
||||
public Point2D getEndPoint() {
|
||||
return new Point2D.Float(this.x2, this.y2);
|
||||
}
|
||||
|
||||
/* MultipleGradientPaint methods... */
|
||||
public AffineTransform getTransform() {
|
||||
return this.xform;
|
||||
}
|
||||
|
||||
public ColorSpaceType getColorSpace() {
|
||||
return ColorSpaceType.SRGB;
|
||||
}
|
||||
|
||||
public CycleMethod getCycleMethod() {
|
||||
return CycleMethod.NO_CYCLE;
|
||||
}
|
||||
|
||||
public Color[] getColors() {
|
||||
return Arrays.copyOf(this.colors, this.colors.length);
|
||||
}
|
||||
|
||||
public float[] getFractions() {
|
||||
return Arrays.copyOf(this.offset, this.offset.length);
|
||||
}
|
||||
|
||||
public int getTransparency() {
|
||||
return TRANSLUCENT; // why not.. rather than checking each color
|
||||
}
|
||||
@@ -221,16 +273,29 @@ public class PShapeJava2D extends PShapeSVG {
|
||||
}
|
||||
|
||||
|
||||
static class RadialGradientPaint implements Paint {
|
||||
public static class RadialGradientPaint implements Paint {
|
||||
float cx, cy, radius;
|
||||
float[] offset;
|
||||
int[] color;
|
||||
Color[] colors;
|
||||
int count;
|
||||
float opacity;
|
||||
AffineTransform xform;
|
||||
|
||||
public static enum CycleMethod {
|
||||
NO_CYCLE,
|
||||
REFLECT,
|
||||
REPEAT
|
||||
}
|
||||
|
||||
public static enum ColorSpaceType {
|
||||
SRGB,
|
||||
LINEAR_RGB
|
||||
}
|
||||
|
||||
public RadialGradientPaint(float cx, float cy, float radius,
|
||||
float[] offset, int[] color, int count,
|
||||
float opacity) {
|
||||
float opacity, AffineTransform xform) {
|
||||
this.cx = cx;
|
||||
this.cy = cy;
|
||||
this.radius = radius;
|
||||
@@ -238,6 +303,13 @@ public class PShapeJava2D extends PShapeSVG {
|
||||
this.color = color;
|
||||
this.count = count;
|
||||
this.opacity = opacity;
|
||||
this.xform = xform;
|
||||
|
||||
//set an array of type Color
|
||||
this.colors = new Color[this.color.length];
|
||||
for (int i = 0; i < this.color.length; i++) {
|
||||
this.colors[i] = new Color(this.color[i], true);
|
||||
}
|
||||
}
|
||||
|
||||
public PaintContext createContext(ColorModel cm,
|
||||
@@ -246,6 +318,41 @@ public class PShapeJava2D extends PShapeSVG {
|
||||
return new RadialGradientContext();
|
||||
}
|
||||
|
||||
public Point2D getCenterPoint() {
|
||||
return new Point2D.Double(this.cx, this.cy);
|
||||
}
|
||||
|
||||
//TODO: investigate how to change a focus point for 0% x of the gradient
|
||||
//for now default to center x/y
|
||||
public Point2D getFocusPoint() {
|
||||
return new Point2D.Double(this.cx, this.cy);
|
||||
}
|
||||
|
||||
public float getRadius() {
|
||||
return this.radius;
|
||||
}
|
||||
|
||||
/* MultipleGradientPaint methods... */
|
||||
public AffineTransform getTransform() {
|
||||
return this.xform;
|
||||
}
|
||||
|
||||
public ColorSpaceType getColorSpace() {
|
||||
return ColorSpaceType.SRGB;
|
||||
}
|
||||
|
||||
public CycleMethod getCycleMethod() {
|
||||
return CycleMethod.NO_CYCLE;
|
||||
}
|
||||
|
||||
public Color[] getColors() {
|
||||
return Arrays.copyOf(this.colors, this.colors.length);
|
||||
}
|
||||
|
||||
public float[] getFractions() {
|
||||
return Arrays.copyOf(this.offset, this.offset.length);
|
||||
}
|
||||
|
||||
public int getTransparency() {
|
||||
return TRANSLUCENT;
|
||||
}
|
||||
@@ -305,14 +412,14 @@ public class PShapeJava2D extends PShapeSVG {
|
||||
LinearGradient grad = (LinearGradient) gradient;
|
||||
return new LinearGradientPaint(grad.x1, grad.y1, grad.x2, grad.y2,
|
||||
grad.offset, grad.color, grad.count,
|
||||
opacity);
|
||||
opacity, grad.transform);
|
||||
|
||||
} else if (gradient instanceof RadialGradient) {
|
||||
// System.out.println("creating radial gradient");
|
||||
RadialGradient grad = (RadialGradient) gradient;
|
||||
return new RadialGradientPaint(grad.cx, grad.cy, grad.r,
|
||||
grad.offset, grad.color, grad.count,
|
||||
opacity);
|
||||
opacity, grad.transform);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -33,8 +33,7 @@ import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.*;
|
||||
|
||||
import processing.core.PApplet;
|
||||
import processing.core.PConstants;
|
||||
@@ -227,7 +226,7 @@ public class PSurfaceAWT extends PSurfaceNone {
|
||||
}
|
||||
|
||||
|
||||
synchronized protected void render() {
|
||||
protected void render() {
|
||||
if (canvas.isDisplayable() &&
|
||||
graphics.image != null) {
|
||||
if (canvas.getBufferStrategy() == null) {
|
||||
@@ -1021,6 +1020,9 @@ public class PSurfaceAWT extends PSurfaceNone {
|
||||
//sketch.postWindowMoved(x - currentInsets.left, y - currentInsets.top);
|
||||
sketch.postWindowMoved(x, y); // presumably user wants drawing area
|
||||
}
|
||||
}else{
|
||||
// Solves #862 - Grey/White bar on right side of sketches
|
||||
setFrameSize();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9663,7 +9663,7 @@ public class PApplet implements PConstants {
|
||||
|
||||
// WINDOW METHODS
|
||||
|
||||
Map<String, Integer> windowEventQueue = new ConcurrentHashMap<>();
|
||||
Map<String, WindowEventValuePairs> windowEventQueue = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
public void windowTitle(String title) {
|
||||
@@ -9683,8 +9683,7 @@ public class PApplet implements PConstants {
|
||||
* only the notification that the resize has happened.
|
||||
*/
|
||||
public void postWindowResized(int newWidth, int newHeight) {
|
||||
windowEventQueue.put("w", newWidth);
|
||||
windowEventQueue.put("h", newHeight);
|
||||
windowEventQueue.put("wh", new WindowEventValuePairs(newWidth, newHeight));
|
||||
}
|
||||
|
||||
|
||||
@@ -9724,8 +9723,7 @@ public class PApplet implements PConstants {
|
||||
frameMoved(newX, newY);
|
||||
}
|
||||
|
||||
windowEventQueue.put("x", newX);
|
||||
windowEventQueue.put("y", newY);
|
||||
windowEventQueue.put("xy", new WindowEventValuePairs(newX, newY));
|
||||
}
|
||||
|
||||
|
||||
@@ -9734,21 +9732,28 @@ public class PApplet implements PConstants {
|
||||
|
||||
|
||||
private void dequeueWindowEvents() {
|
||||
if (windowEventQueue.containsKey("x")) {
|
||||
windowX = windowEventQueue.remove("x");
|
||||
windowY = windowEventQueue.remove("y");
|
||||
if (windowEventQueue.containsKey("xy")) {
|
||||
WindowEventValuePairs xy = windowEventQueue.remove("xy");
|
||||
windowX = xy.num1;
|
||||
windowY = xy.num2;
|
||||
windowMoved();
|
||||
}
|
||||
if (windowEventQueue.containsKey("w")) {
|
||||
// these should already match width/height
|
||||
//windowResized(windowEventQueue.remove("w"),
|
||||
// windowEventQueue.remove("h"));
|
||||
windowEventQueue.remove("w");
|
||||
windowEventQueue.remove("h");
|
||||
if (windowEventQueue.containsKey("wh")) {
|
||||
WindowEventValuePairs wh = windowEventQueue.remove("wh");
|
||||
windowResized();
|
||||
}
|
||||
}
|
||||
|
||||
protected class WindowEventValuePairs {
|
||||
|
||||
public int num1;
|
||||
public int num2;
|
||||
|
||||
public WindowEventValuePairs(int num1, int num2) {
|
||||
this.num1 = num1;
|
||||
this.num2 = num2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scale the sketch as if it fits this specific width and height.
|
||||
|
||||
@@ -1397,7 +1397,8 @@ public class PShapeSVG extends PShape {
|
||||
}
|
||||
|
||||
|
||||
void setColor(String colorText, boolean isFill) {
|
||||
//making this public allows us to set gradient fills on a PShape
|
||||
public void setColor(String colorText, boolean isFill) {
|
||||
colorText = colorText.trim();
|
||||
int opacityMask = fillColor & 0xFF000000;
|
||||
boolean visible = true;
|
||||
@@ -1620,7 +1621,7 @@ public class PShapeSVG extends PShape {
|
||||
|
||||
|
||||
static public class Gradient extends PShapeSVG {
|
||||
AffineTransform transform;
|
||||
public AffineTransform transform;
|
||||
|
||||
public float[] offset;
|
||||
public int[] color;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
<property name="core.library.jar" location="../../../core/library/core.jar" />
|
||||
|
||||
<property name="batik.version" value="1.14" />
|
||||
<property name="batik.version" value="1.18" />
|
||||
<!-- the .zip file to be downloaded -->
|
||||
<property name="batik.zip" value="batik-bin-${batik.version}.zip" />
|
||||
<!-- the .jar file that we need from the download -->
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
<!-- URL for the version of Batik currently supported by this library. -->
|
||||
<property name="batik.url"
|
||||
value="https://archive.apache.org/dist/xmlgraphics/batik/binaries/${batik.zip}" />
|
||||
value="https://dlcdn.apache.org//xmlgraphics/batik/binaries/${batik.zip}" />
|
||||
|
||||
<!-- Storing a "local" copy in case the original link goes dead. When updating
|
||||
releases, please upload the new version to download.processing.org. -->
|
||||
|
||||
@@ -0,0 +1,209 @@
|
||||
package processing.svg;
|
||||
|
||||
import static org.apache.batik.util.SVGConstants.*;
|
||||
|
||||
import processing.awt.PShapeJava2D.LinearGradientPaint;
|
||||
import processing.awt.PShapeJava2D.RadialGradientPaint;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.MultipleGradientPaint;
|
||||
import java.awt.Paint;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Point2D;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.apache.batik.svggen.DefaultExtensionHandler;
|
||||
import org.apache.batik.svggen.SVGColor;
|
||||
import org.apache.batik.svggen.SVGGeneratorContext;
|
||||
import org.apache.batik.svggen.SVGPaintDescriptor;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* Extension of Batik's {@link DefaultExtensionHandler} which handles different kinds of Paint objects
|
||||
* based on the extenstion by Martin Steiger https://gist.github.com/msteiger/4509119
|
||||
* modified to work with Processing's SVG export library, by Benjamin Fox https://github.com/tracerstar
|
||||
*/
|
||||
public class GradientExtensionHandler extends DefaultExtensionHandler {
|
||||
|
||||
@Override
|
||||
public SVGPaintDescriptor handlePaint(Paint paint, SVGGeneratorContext genCtx) {
|
||||
|
||||
// Handle LinearGradientPaint
|
||||
if (paint instanceof LinearGradientPaint) {
|
||||
return getLgpDescriptor((LinearGradientPaint) paint, genCtx);
|
||||
}
|
||||
|
||||
// Handle RadialGradientPaint
|
||||
if (paint instanceof RadialGradientPaint) {
|
||||
return getRgpDescriptor((RadialGradientPaint) paint, genCtx);
|
||||
}
|
||||
|
||||
return super.handlePaint(paint, genCtx);
|
||||
}
|
||||
|
||||
private SVGPaintDescriptor getLgpDescriptor(LinearGradientPaint gradient, SVGGeneratorContext genCtx) {
|
||||
Element gradElem = genCtx.getDOMFactory().createElementNS(SVG_NAMESPACE_URI, SVG_LINEAR_GRADIENT_TAG);
|
||||
|
||||
// Create and set unique XML id
|
||||
String id = genCtx.getIDGenerator().generateID("gradient");
|
||||
gradElem.setAttribute(SVG_ID_ATTRIBUTE, id);
|
||||
|
||||
// Set x,y pairs
|
||||
Point2D startPt = gradient.getStartPoint();
|
||||
gradElem.setAttribute("x1", String.valueOf(startPt.getX()));
|
||||
gradElem.setAttribute("y1", String.valueOf(startPt.getY()));
|
||||
|
||||
Point2D endPt = gradient.getEndPoint();
|
||||
gradElem.setAttribute("x2", String.valueOf(endPt.getX()));
|
||||
gradElem.setAttribute("y2", String.valueOf(endPt.getY()));
|
||||
|
||||
//TODO: change this to be: addMgpAttributes after refactoring the paint methods
|
||||
addLgpAttributes(gradElem, genCtx, gradient);
|
||||
|
||||
return new SVGPaintDescriptor("url(#" + id + ")", SVG_OPAQUE_VALUE, gradElem);
|
||||
}
|
||||
|
||||
private SVGPaintDescriptor getRgpDescriptor(RadialGradientPaint gradient, SVGGeneratorContext genCtx) {
|
||||
Element gradElem = genCtx.getDOMFactory().createElementNS(SVG_NAMESPACE_URI, SVG_RADIAL_GRADIENT_TAG);
|
||||
|
||||
// Create and set unique XML id
|
||||
String id = genCtx.getIDGenerator().generateID("gradient");
|
||||
gradElem.setAttribute(SVG_ID_ATTRIBUTE, id);
|
||||
|
||||
// Set x,y pairs
|
||||
Point2D centerPt = gradient.getCenterPoint();
|
||||
gradElem.setAttribute("cx", String.valueOf(centerPt.getX()));
|
||||
gradElem.setAttribute("cy", String.valueOf(centerPt.getY()));
|
||||
|
||||
Point2D focusPt = gradient.getFocusPoint();
|
||||
gradElem.setAttribute("fx", String.valueOf(focusPt.getX()));
|
||||
gradElem.setAttribute("fy", String.valueOf(focusPt.getY()));
|
||||
|
||||
gradElem.setAttribute("r", String.valueOf(gradient.getRadius()));
|
||||
|
||||
//TODO: change this to be: addMgpAttributes after refactoring the paint methods
|
||||
addRgpAttributes(gradElem, genCtx, gradient);
|
||||
|
||||
return new SVGPaintDescriptor("url(#" + id + ")", SVG_OPAQUE_VALUE, gradElem);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Being lazy here to duplicate the methods so we don't have to refactor the two gradient paints
|
||||
to implement java.awt.MultipleGradientPaint
|
||||
|
||||
TODO: make the effort to refactor them to properly implement java.awt.MultipleGradientPaint
|
||||
*/
|
||||
private void addLgpAttributes(Element gradElem, SVGGeneratorContext genCtx, LinearGradientPaint gradient) {
|
||||
gradElem.setAttribute(SVG_GRADIENT_UNITS_ATTRIBUTE, SVG_USER_SPACE_ON_USE_VALUE);
|
||||
|
||||
// Set cycle method
|
||||
switch (gradient.getCycleMethod()) {
|
||||
case REFLECT:
|
||||
gradElem.setAttribute(SVG_SPREAD_METHOD_ATTRIBUTE, SVG_REFLECT_VALUE);
|
||||
break;
|
||||
case REPEAT:
|
||||
gradElem.setAttribute(SVG_SPREAD_METHOD_ATTRIBUTE, SVG_REPEAT_VALUE);
|
||||
break;
|
||||
case NO_CYCLE:
|
||||
default:
|
||||
gradElem.setAttribute(SVG_SPREAD_METHOD_ATTRIBUTE, SVG_PAD_VALUE); // this is the default
|
||||
break;
|
||||
}
|
||||
|
||||
// Set color space
|
||||
switch (gradient.getColorSpace()) {
|
||||
case LINEAR_RGB:
|
||||
gradElem.setAttribute(SVG_COLOR_INTERPOLATION_ATTRIBUTE, SVG_LINEAR_RGB_VALUE);
|
||||
break;
|
||||
case SRGB:
|
||||
default:
|
||||
gradElem.setAttribute(SVG_COLOR_INTERPOLATION_ATTRIBUTE, SVG_SRGB_VALUE);
|
||||
break;
|
||||
}
|
||||
|
||||
// Set transform matrix if not identity
|
||||
AffineTransform tf = gradient.getTransform();
|
||||
if (!Objects.isNull(tf) && !tf.isIdentity()) {
|
||||
String matrix = "matrix(" +
|
||||
tf.getScaleX() + " " + tf.getShearX() + " " + tf.getTranslateX() + " " +
|
||||
tf.getScaleY() + " " + tf.getShearY() + " " + tf.getTranslateY() + ")";
|
||||
gradElem.setAttribute(SVG_TRANSFORM_ATTRIBUTE, matrix);
|
||||
}
|
||||
|
||||
// Convert gradient stops
|
||||
Color[] colors = gradient.getColors();
|
||||
float[] fracs = gradient.getFractions();
|
||||
|
||||
for (int i = 0; i < colors.length; i++) {
|
||||
Element stop = genCtx.getDOMFactory().createElementNS(SVG_NAMESPACE_URI, SVG_STOP_TAG);
|
||||
SVGPaintDescriptor pd = SVGColor.toSVG(colors[i], genCtx);
|
||||
|
||||
stop.setAttribute(SVG_OFFSET_ATTRIBUTE, (int) (fracs[i] * 100.0f) + "%");
|
||||
stop.setAttribute(SVG_STOP_COLOR_ATTRIBUTE, pd.getPaintValue());
|
||||
|
||||
if (colors[i].getAlpha() != 255) {
|
||||
stop.setAttribute(SVG_STOP_OPACITY_ATTRIBUTE, pd.getOpacityValue());
|
||||
}
|
||||
|
||||
gradElem.appendChild(stop);
|
||||
}
|
||||
}
|
||||
|
||||
private void addRgpAttributes(Element gradElem, SVGGeneratorContext genCtx, RadialGradientPaint gradient) {
|
||||
gradElem.setAttribute(SVG_GRADIENT_UNITS_ATTRIBUTE, SVG_USER_SPACE_ON_USE_VALUE);
|
||||
|
||||
// Set cycle method
|
||||
switch (gradient.getCycleMethod()) {
|
||||
case REFLECT:
|
||||
gradElem.setAttribute(SVG_SPREAD_METHOD_ATTRIBUTE, SVG_REFLECT_VALUE);
|
||||
break;
|
||||
case REPEAT:
|
||||
gradElem.setAttribute(SVG_SPREAD_METHOD_ATTRIBUTE, SVG_REPEAT_VALUE);
|
||||
break;
|
||||
case NO_CYCLE:
|
||||
default:
|
||||
gradElem.setAttribute(SVG_SPREAD_METHOD_ATTRIBUTE, SVG_PAD_VALUE); // this is the default
|
||||
break;
|
||||
}
|
||||
|
||||
// Set color space
|
||||
switch (gradient.getColorSpace()) {
|
||||
case LINEAR_RGB:
|
||||
gradElem.setAttribute(SVG_COLOR_INTERPOLATION_ATTRIBUTE, SVG_LINEAR_RGB_VALUE);
|
||||
break;
|
||||
case SRGB:
|
||||
default:
|
||||
gradElem.setAttribute(SVG_COLOR_INTERPOLATION_ATTRIBUTE, SVG_SRGB_VALUE);
|
||||
break;
|
||||
}
|
||||
|
||||
// Set transform matrix if not identity
|
||||
AffineTransform tf = gradient.getTransform();
|
||||
if (!Objects.isNull(tf) && !tf.isIdentity()) {
|
||||
String matrix = "matrix(" +
|
||||
tf.getScaleX() + " " + tf.getShearX() + " " + tf.getTranslateX() + " " +
|
||||
tf.getScaleY() + " " + tf.getShearY() + " " + tf.getTranslateY() + ")";
|
||||
gradElem.setAttribute(SVG_TRANSFORM_ATTRIBUTE, matrix);
|
||||
}
|
||||
|
||||
// Convert gradient stops
|
||||
Color[] colors = gradient.getColors();
|
||||
float[] fracs = gradient.getFractions();
|
||||
|
||||
for (int i = 0; i < colors.length; i++) {
|
||||
Element stop = genCtx.getDOMFactory().createElementNS(SVG_NAMESPACE_URI, SVG_STOP_TAG);
|
||||
SVGPaintDescriptor pd = SVGColor.toSVG(colors[i], genCtx);
|
||||
|
||||
stop.setAttribute(SVG_OFFSET_ATTRIBUTE, (int) (fracs[i] * 100.0f) + "%");
|
||||
stop.setAttribute(SVG_STOP_COLOR_ATTRIBUTE, pd.getPaintValue());
|
||||
|
||||
if (colors[i].getAlpha() != 255) {
|
||||
stop.setAttribute(SVG_STOP_OPACITY_ATTRIBUTE, pd.getOpacityValue());
|
||||
}
|
||||
|
||||
gradElem.appendChild(stop);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,6 +87,10 @@ public class PGraphicsSVG extends PGraphicsJava2D {
|
||||
g2 = new SVGGraphics2D(document);
|
||||
((SVGGraphics2D) g2).setSVGCanvasSize(new Dimension(width, height));
|
||||
|
||||
//set the extension handler to allow linear and radial gradients to be exported as svg
|
||||
GradientExtensionHandler gradH = new GradientExtensionHandler();
|
||||
((SVGGraphics2D) g2).setExtensionHandler(gradH);
|
||||
|
||||
// Done with our work, let's check on defaults and the rest
|
||||
//super.beginDraw();
|
||||
// Can't call super.beginDraw() because it'll nuke our g2
|
||||
|
||||
@@ -28,9 +28,13 @@ import java.awt.event.*;
|
||||
import java.io.*;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.*;
|
||||
@@ -230,7 +234,17 @@ public class JavaEditor extends Editor {
|
||||
}
|
||||
});
|
||||
|
||||
return buildFileMenu(new JMenuItem[] { exportApplication });
|
||||
var exportPDEZ = new JMenuItem(Language.text("menu.file.export_pdez"));
|
||||
exportPDEZ.addActionListener(e -> {
|
||||
if (sketch.isUntitled() || sketch.isReadOnly()) {
|
||||
Messages.showMessage("Save First", "Please first save the sketch.");
|
||||
} else {
|
||||
handleExportPDEZ();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return buildFileMenu(new JMenuItem[] { exportApplication, exportPDEZ });
|
||||
}
|
||||
|
||||
|
||||
@@ -491,6 +505,52 @@ public class JavaEditor extends Editor {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for File → Export PDEZ
|
||||
*/
|
||||
public void handleExportPDEZ() {
|
||||
if (handleExportCheckModified()) {
|
||||
var sketch = getSketch();
|
||||
var folder = sketch.getFolder().toPath();
|
||||
var target = new File(folder + ".pdez").toPath();
|
||||
if (Files.exists(target)) {
|
||||
try {
|
||||
Platform.deleteFile(target.toFile());
|
||||
} catch (IOException e) {
|
||||
Messages.showError("Export Error", "Could not delete existing file: " + target, e);
|
||||
}
|
||||
}
|
||||
|
||||
try (var zs = new ZipOutputStream(Files.newOutputStream(target))) {
|
||||
Files.walk(folder)
|
||||
.filter(path -> !Files.isDirectory(path))
|
||||
.forEach(path -> {
|
||||
var zipEntry = new ZipEntry(folder.getParent().relativize(path).toString());
|
||||
try {
|
||||
zs.putNextEntry(zipEntry);
|
||||
Files.copy(path, zs);
|
||||
zs.closeEntry();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
if (Desktop.isDesktopSupported()) {
|
||||
var desktop = Desktop.getDesktop();
|
||||
if (desktop.isSupported(Desktop.Action.BROWSE_FILE_DIR)) {
|
||||
desktop.browseFileDirectory(target.toFile());
|
||||
} else {
|
||||
try {
|
||||
desktop.open(target.getParent().toFile());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if the sketch has been modified, and if so,
|
||||
|
||||
Reference in New Issue
Block a user