From 3c67bf3398d6f525a23fce9b7f05b5cc7e3f7e76 Mon Sep 17 00:00:00 2001 From: abakkk Date: Tue, 22 Sep 2020 23:21:48 +0200 Subject: [PATCH] colored SVG images Color (fill) SVG images by pressing the `Shift` key. --- area.js | 8 ++++---- elements.js | 9 +++++---- files.js | 49 ++++++++++++++++++++++++++++++++++++++++++------- menu.js | 2 -- 4 files changed, 51 insertions(+), 17 deletions(-) diff --git a/area.js b/area.js index f2165d1..7420c5c 100644 --- a/area.js +++ b/area.js @@ -603,7 +603,7 @@ var DrawingArea = new Lang.Class({ this._redisplay(); }, - _startDrawing: function(stageX, stageY, eraser) { + _startDrawing: function(stageX, stageY, shiftPressed) { let [success, startX, startY] = this.transform_stage_point(stageX, stageY); if (!success) @@ -617,7 +617,7 @@ var DrawingArea = new Lang.Class({ this.currentElement = new Elements.DrawingElement({ shape: this.currentTool, color: this.currentColor, - eraser: eraser, + eraser: shiftPressed, font: this.currentFont.copy(), // Translators: initial content of the text area text: pgettext("text-area-content", "Text"), @@ -628,7 +628,7 @@ var DrawingArea = new Lang.Class({ this.currentElement = new Elements.DrawingElement({ shape: this.currentTool, color: this.currentColor, - eraser: eraser, + colored: shiftPressed, image: this.currentImage, points: [] }); @@ -636,7 +636,7 @@ var DrawingArea = new Lang.Class({ this.currentElement = new Elements.DrawingElement({ shape: this.currentTool, color: this.currentColor, - eraser: eraser, + eraser: shiftPressed, fill: this.fill, fillRule: this.currentFillRule, line: { lineWidth: this.currentLineWidth, lineJoin: this.currentLineJoin, lineCap: this.currentLineCap }, diff --git a/elements.js b/elements.js index cd4ee90..85a4387 100644 --- a/elements.js +++ b/elements.js @@ -771,7 +771,7 @@ const ImageElement = new Lang.Class({ return { shape: this.shape, color: this.color, - eraser: this.eraser, + colored: this.colored, transformations: this.transformations, image: this.image, preserveAspectRatio: this.preserveAspectRatio, @@ -791,7 +791,7 @@ const ImageElement = new Lang.Class({ return; cr.save(); - this.image.setCairoSource(cr, x, y, width, height, this.preserveAspectRatio); + this.image.setCairoSource(cr, x, y, width, height, this.preserveAspectRatio, this.colored ? this.color.toJSON() : null); cr.rectangle(x, y, width, height); cr.fill(); cr.restore(); @@ -813,14 +813,15 @@ const ImageElement = new Lang.Class({ _drawSvg: function(transAttribute) { let points = this.points; let row = "\n "; - let attributes = this.eraser ? `class="eraser" ` : ''; + let attributes = ''; if (points.length == 2) { attributes += `fill="none"`; + let base64 = this.image.getBase64ForColor(this.colored ? this.color.toJSON() : null); row += ``; + `id="${this.image.displayName}" xlink:href="data:${this.image.contentType};base64,${base64}"/>`; } return row; diff --git a/files.js b/files.js index 924b006..4f4b6bb 100644 --- a/files.js +++ b/files.js @@ -83,6 +83,18 @@ Object.keys(ThemedIconNames).forEach(key => { }); }); +const replaceColor = function (contents, color) { + if (contents instanceof Uint8Array) + contents = ByteArray.toString(contents); + else + contents = contents.toString(); + + return contents.replace(/fill(?=="transparent"|="none"|:transparent|:none)/gi, 'filll') + .replace(/fill="[^"]+"/gi, `fill="${color}"`) + .replace(/fill:[^";]+/gi, `fill:${color};`) + .replace(/filll/gi, 'fill'); +}; + // Wrapper around image data. If not subclassed, it is used when loading in the area an image element for a drawing file (.json) // and it takes { displayName, contentType, base64, hash } as params. var Image = new Lang.Class({ @@ -122,6 +134,14 @@ var Image = new Lang.Class({ this._base64 = base64; }, + getBase64ForColor: function(color) { + if (!color || this.contentType != 'image/svg+xml') + return this.base64; + + let contents = GLib.base64_decode(this.base64); + return GLib.base64_encode(replaceColor(contents, color)); + }, + // hash is not used get hash() { if (!this._hash) @@ -133,7 +153,22 @@ var Image = new Lang.Class({ this._hash = hash; }, - get pixbuf() { + getBytesForColor: function(color) { + if (!color || this.contentType != 'image/svg+xml') + return this.bytes; + + let contents = this.bytes.get_data(); + return new GLib.Bytes(replaceColor(contents, color)); + }, + + getPixbuf: function(color) { + if (color) { + let stream = Gio.MemoryInputStream.new_from_bytes(this.getBytesForColor(color)); + let pixbuf = GdkPixbuf.Pixbuf.new_from_stream(stream, null); + stream.close(null); + return pixbuf; + } + if (!this._pixbuf) { let stream = Gio.MemoryInputStream.new_from_bytes(this.bytes); this._pixbuf = GdkPixbuf.Pixbuf.new_from_stream(stream, null); @@ -142,16 +177,16 @@ var Image = new Lang.Class({ return this._pixbuf; }, - getPixbufAtScale: function(width, height) { - let stream = Gio.MemoryInputStream.new_from_bytes(this.bytes); + getPixbufAtScale: function(width, height, color) { + let stream = Gio.MemoryInputStream.new_from_bytes(this.getBytesForColor(color)); let pixbuf = GdkPixbuf.Pixbuf.new_from_stream_at_scale(stream, width, height, true, null); stream.close(null); return pixbuf; }, - setCairoSource: function(cr, x, y, width, height, preserveAspectRatio) { - let pixbuf = preserveAspectRatio ? this.getPixbufAtScale(width, height) - : this.pixbuf.scale_simple(width, height, GdkPixbuf.InterpType.BILINEAR); + setCairoSource: function(cr, x, y, width, height, preserveAspectRatio, color) { + let pixbuf = preserveAspectRatio ? this.getPixbufAtScale(width, height, color) + : this.getPixbuf(color).scale_simple(width, height, GdkPixbuf.InterpType.BILINEAR); Gdk.cairo_set_source_pixbuf(cr, pixbuf, x, y); } }); @@ -552,7 +587,7 @@ var Jsons = { var getDateString = function() { let date = GLib.DateTime.new_now_local(); - return `${date.format("%F")} ${date.format("%X")}`; + return `${date.format("%F")} ${date.format("%T")}`; }; var saveSvg = function(content) { diff --git a/menu.js b/menu.js index 1185dc5..ebad93b 100644 --- a/menu.js +++ b/menu.js @@ -315,8 +315,6 @@ var DrawingMenu = new Lang.Class({ this.lineSection.actor.visible = !isText && !isImage; this.fontSection.actor.visible = isText; this.imageSection.actor.visible = isImage; - this.colorItem.setSensitive(!isImage); - this.paletteItem.setSensitive(!isImage); this.fillItem.setSensitive(!isText && !isImage); this.fillSection.setSensitive(!isText && !isImage);