Clone in a terminal:
CutBackgroundTool.vala
/*
    Copyright (C) 2012 Johan Mattsson

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

using Cairo;
using Math;

namespace Supplement {

class CutBackgroundTool : Tool {

	double x1 = 0;
	double y1 = 0;
	double x2 = 0;
	double y2 = 0;
	
	bool is_visible = false;
	bool is_set = false;
	bool is_done = false;
		
	public signal void new_image (GlyphBackgroundImage file);
	
	public CutBackgroundTool (string name) {
		base (name, _("Crop background image"));

		select_action.connect((self) => {
		});

		deselect_action.connect ((self) => {
		});
		
		press_action.connect((self, b, x, y) => {				
			if (!is_over_rectangle (x, y) || !is_visible) {
				x1 = x;
				y1 = y;
	
				x2 = x;
				y2 = y;
				
				is_visible = true;
				is_set = false;
				is_done = false;
			}
			
			if (is_set && is_visible && is_over_rectangle (x, y)) {
				do_cut ();
				is_set = false;
				is_visible = false;
				is_done = true;
				
				x1 = 0;
				y1 = 0;
				x2 = 0;
				y2 = 0;
			}
		});

		release_action.connect((self, b, x, y) => {
			if (!is_set && !is_done) {
				x2 = x;
				y2 = y;
				is_set = true;
			}
			
			is_done = false;
		});

		move_action.connect((self, x, y) => {
			if (is_visible && !is_set) {
				x2 = x;
				y2 = y;
				
				MainWindow.get_glyph_canvas ().redraw ();
			}
		});
		
		draw_action.connect ((self, cr, glyph) => {
			if (is_visible) {
				cr.save ();
				cr.set_line_width (2.0);
				cr.set_source_rgba (0, 0, 1, 0.3);
				cr.rectangle (fmin (x1, x2), fmin (y1, y2), fabs (x1 - x2), fabs (y1 - y2));
				cr.stroke ();
				cr.restore ();
			}
		});

		new_image.connect ((file) => {
			Glyph glyph = MainWindow.get_current_glyph ();
			TabBar tb = MainWindow.get_tab_bar ();
			
			glyph.store_undo_state ();
			
			glyph.set_background_image (file);
			tb.select_tab_name (glyph.get_name ());
			
			glyph.set_background_visible (true);
		});

	}

	bool is_over_rectangle (double x, double y) {
		return fmin (x1, x2) + 1 < x < fmax (x1, x2) - 1 && fmin (y1, y2) + 1 < y < fmax (y1, y2) - 1;
	}

	double get_width () {
		return fabs (x1 - x2);
	}

	double get_height () {
		return fabs (y1 - y2);
	}

	void do_cut () {
		double x, y;
		int h, w;
		double wc, hc;
		
		Glyph g = MainWindow.get_current_glyph ();
		GlyphBackgroundImage? b = g.get_background_image ();
		GlyphBackgroundImage bg = (!) b;
		
		ImageSurface img;
		
		Surface sr;
		Context cr;
		
		double tx, ty, vx, vy;	
		if (b == null) {
			return;
		}

		// Add margin
		Surface sg = new Surface.similar (bg.get_img (), bg.get_img ().get_content (), bg.size_margin, bg.size_margin);
		Context cg = new Context (sg);
		
		wc = bg.get_margin_width ();
		hc = bg.get_margin_height ();
		
		cg.set_source_rgba (1, 1, 1, 1);
		cg.rectangle (0, 0, bg.size_margin, bg.size_margin);
		cg.fill ();
		
		cg.set_source_surface (bg.get_img (), wc, hc);
		cg.paint ();

		// find start
		tx = bg.img_offset_x - g.view_offset_x;
		ty = bg.img_offset_y - g.view_offset_y;
				
		ty *= g.view_zoom;
		tx *= g.view_zoom;

		vx = g.path_coordinate_x (tx) - g.path_coordinate_x (fmin (x1, x2));			
		vy = g.path_coordinate_y (ty) - g.path_coordinate_y (fmin (y1, y2));
		
		x = (int) (vx / bg.img_scale_x);
		y = (int) (-vy / bg.img_scale_y);

		// do the cut
		img = bg.get_img ();

		w = (int) (get_width () / g.view_zoom);
		h = (int) (get_height () / g.view_zoom);

		sr = new Surface.similar (sg, img.get_content (), w, h);
		cr = new Context (sr);

		cr.set_source_rgba (1, 1, 1, 1);
		cr.rectangle (0, 0, w, h);
		cr.fill ();
			
		cr.scale (bg.img_scale_x, bg.img_scale_y);
		
		cr.set_source_surface (sg, x, y);
		cr.paint ();
		
		save_img (sr, g);
		
		cr.restore ();
	}

	void save_img (Surface sr, Glyph g) {
		GlyphBackgroundImage newbg;
		Font f = Supplement.get_current_font ();
		File img_dir;
		File img_file;
		File img_file_next;
		string fn;
		double wc, hc;
		
		img_dir =  f.get_backgrounds_folder ().get_child ("parts");

		if (!img_dir.query_exists ()) {
			DirUtils.create ((!) img_dir.get_path (), 0xFFFFFF);
		}
	
		img_file = img_dir.get_child (@"NEW_BACKGROUND.png");
		fn = (!) img_file.get_path ();
		
		sr.write_to_png (fn);
		
		newbg = new GlyphBackgroundImage (fn);
		
		fn = newbg.get_sha1 () + ".png";

		img_file_next = img_dir.get_child (fn);
		
		try {
			if (img_file_next.query_exists ()) {
				img_file_next.delete ();
			}

			img_file.set_display_name (fn);
		} catch (GLib.Error e) {
			warning (e.message);
			return;
		}
		
		newbg = new GlyphBackgroundImage ((!) f.get_backgrounds_folder ().get_child ("parts").get_child (fn).get_path ());
			
		// set position for the new background
		wc = newbg.get_margin_width ();
		hc = newbg.get_margin_height ();
		
		newbg.img_x = g.path_coordinate_x (fmin (x1, x2)) - wc - 0.5;
		newbg.img_y = g.path_coordinate_y (fmin (y1, y2)) + hc + 0.5;

		new_image (newbg);
	}
}

}

Contact

Email: johan.mattsson.m@gmail.com
Mastodon: typo.social/@birdfont
GitHub: johanmattssonm
LinkedIn: johan-mattsson-2ab324112