Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
branch: master
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

executable file 304 lines (252 sloc) 9.731 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
#!/usr/bin/env python

# Export a copy of the current image, scaled to a different size.
# It's like "Save a copy" only it remembers to re-scale each time
# back to whatever scale you used on the last export --
# like if you want to keep a thumbnail or a
# smaller web version current as you're editing the master.

# Copyright 2012 by Akkana Peck, http://www.shallowsky.com/software/
# You may use and distribute this plug-in under the terms of the GPL v2
# or, at your option, any later GPL version.

from gimpfu import *
import gtk
import os

# Image original size
orig_width = 0
orig_height = 0

# The three gtk.Entry widgets we'll monitor:
percent_e = None
width_e = None
height_e = None

# A way to show errors to the user:
warning_label = None

# A way to turn off notifications while we're calculating
busy = False

# Doesn't work to compare entry against percent_e, etc.
# Must pass that info in a separate arg, darnit.
def entry_changed(entry, which) :
    global percent_e, width_e, height_e, orig_width, orig_height, busy
    if busy :
        return

    # Can't use get_value() or get_value_as_int() because they
    # don't work before an update(), but update() will usually
    # overwrite what the user typed. So define a function:
    def get_num_value(spinbox) :
        s = spinbox.get_text()
        if not s :
            return 0

        try :
            p = int(s)
            return p
        except ValueError :
            try :
                p = float(s)
                return p
            except ValueError :
                return 0

    if which == 'p' :
        p = get_num_value(percent_e)
        if not p : return
        busy = True
        w = int(orig_width * p / 100.)
        width_e.set_text(str(w))
        h = int(orig_height * p / 100.)
        height_e.set_text(str(h))
        busy = False

    elif which == 'w' :
        w = get_num_value(width_e)
        if not w : return
        busy = True
        p = w * 100. / orig_width
        percent_e.set_text(str(p))
        h = int(orig_height * p / 100.)
        height_e.set_text(str(h))
        busy = False

    elif which == 'h' :
        h = get_num_value(height_e)
        if not h : return
        busy = True
        p = h * 100. / orig_height
        percent_e.set_text(str(p))
        w = int(orig_width * p / 100.)
        width_e.set_text(str(w))
        busy = False
    else :
        print "I'm confused -- not width, height or percentage"

def python_export_scaled(img, drawable) :
    global percent_e, width_e, height_e, orig_width, orig_height, warning_label
    orig_filename = img.filename

    orig_width = img.width
    orig_height = img.height

    # Do we already have defaults?
    para = img.parasite_find('export-scaled')
    if para :
        [ init_filename, init_percent, init_width, init_height ] = \
            para.data.split('\n')
    else :
        init_filename = orig_filename
        init_percent = 100
        init_width = img.width
        init_height = img.height

    chooser = gtk.FileChooserDialog(title=None,
                                    action=gtk.FILE_CHOOSER_ACTION_SAVE,
                                    buttons=(gtk.STOCK_CANCEL,
                                             gtk.RESPONSE_CANCEL,
                                             gtk.STOCK_SAVE,
                                             gtk.RESPONSE_OK))

    if init_filename :
        chooser.set_current_name(os.path.basename(init_filename))
        chooser.set_current_folder(os.path.dirname(init_filename))

    vbox = gtk.VBox(spacing=8)

    hbox = gtk.HBox(spacing=8)

    l = gtk.Label("Export size:")
    l.set_alignment(1, 0)
    l.show()
    hbox.pack_start(l)

    l = gtk.Label("Percent")
    l.set_alignment(1, 0)
    hbox.pack_start(l)
    l.show()
    adj = gtk.Adjustment(float(init_percent), 1, 10000, 1, 10, 0)
    percent_e = gtk.SpinButton(adj) #, 0, 0)

    #percent_e = gtk.Entry()
    #percent_e.set_width_chars(5)
    #percent_e.set_text("100") # XXX Later, remember size from last time

    percent_e.connect("changed", entry_changed, 'p');
    percent_e.show()
    hbox.pack_start(percent_e)
    l = gtk.Label("%")
    l.set_alignment(0, 1)
    hbox.pack_start(l)
    l.show()

    l = gtk.Label("Width")
    l.set_alignment(1, 0)
    hbox.pack_start(l)
    l.show()
    adj = gtk.Adjustment(float(init_width), 1, 10000, 1, 10, 0)
    width_e = gtk.SpinButton(adj)
    #width_e = gtk.Entry()
    #width_e.set_width_chars(7)
    #width_e.set_text(str(img.width))
    # XXX Later, remember size from previous run
    width_e.connect("changed", entry_changed, 'w');
    width_e.show()
    hbox.pack_start(width_e)

    l = gtk.Label("Height")
    l.set_alignment(1, 0)
    hbox.pack_start(l)
    l.show()
    adj = gtk.Adjustment(float(init_height), 1, 10000, 1, 10, 0)
    height_e = gtk.SpinButton(adj)
    #height_e = gtk.Entry()
    #height_e.set_width_chars(7)
    #height_e.set_text(str(img.height))
    height_e.connect("changed", entry_changed, 'h');
    hbox.pack_start(height_e)
    height_e.show()

    hbox.show()
    vbox.pack_start(hbox)

    warning_label = gtk.Label("")
    warning_label.show()
    vbox.pack_start(warning_label)

    chooser.set_extra_widget(vbox)

    # Oh, cool, we could have shortcuts to image folders,
    # and maybe remove the stupid fstab shortcuts GTK adds for us.
        #chooser.add_shortcut_folder(folder)
        #chooser.remove_shortcut_folder(folder)

    # Loop to catch errors/warnings:
    while True :
        response = chooser.run()
        if response != gtk.RESPONSE_OK:
            chooser.destroy()
            return

        percent = float(percent_e.get_text())
        width = int(width_e.get_text())
        height = int(height_e.get_text())

        filename = chooser.get_filename()
        if filename == orig_filename :
            warning_label.set_text("Change the name or the directory -- don't overwrite original file!")
            continue

        # Whew, it's not the original filename, so now we can export.

        #print "export from", orig_filename, "to", filename, \
        # "at", width, "x", height

        # Is there any point to pushing and popping the context?
        #gimp.context_pop()

        newimg = pdb.gimp_image_duplicate(img)

        # If it's XCF, we don't need to flatten or process it further,
        # just scale it:
        base, ext = os.path.splitext(filename)
        ext = ext.lower()
        if ext == '.gz' or ext == '.bz2' :
            base, ext = os.path.splitext(base)
            ext = ext.lower()
        if ext != '.xcf' :
            newimg.flatten()
            # XXX This could probably be smarter about flattening. Oh, well.

        newimg.scale(width, height)

        # Find any image type settings parasites (e.g. jpeg settings)
        # that got set during save, so we'll be able to use them
        # next time.
        def copy_settings_parasites(fromimg, toimg) :
            for pname in fromimg.parasite_list() :
                if pname[-9:] == '-settings' or pname[-13:] == '-save-options' :
                    para = fromimg.parasite_find(pname)
                    if para :
                        toimg.attach_new_parasite(pname, para.flags, para.data)

        # Copy any settings parasites we may have saved from previous runs:
        copy_settings_parasites(img, newimg)

        # gimp-file-save insists on being passed a valid layer,
        # even if saving to a multilayer format such as XCF. Go figure.
        try :
            # I don't get it. What's the rule for whether gimp_file_save
            # will prompt for jpg parameters? Saving back to an existing
            # file doesn't seem to help.
            # run_mode=RUN_WITH_LAST_VALS is supposed to prevent prompting,
            # but actually seems to do nothing. Copying the -save-options
            # parasites is more effective.
            dpy = chooser.get_display()
            chooser.hide()
            dpy.sync()
            pdb.gimp_file_save(newimg, newimg.active_layer, filename, filename,
                               run_mode=RUN_WITH_LAST_VALS)
        except RuntimeError, e :
            #warning_label.set_text("Didn't save to " + filename
            # + ":" + str(e))
            markup = '<span foreground="red" size="larger" weight=\"bold">'
            markup_end = '</span>'
            warning_label.set_markup(markup + "Didn't save to " + filename
                                   + ":" + str(e) + markup_end)
            gimp.delete(newimg)
            chooser.show()
            continue

        chooser.destroy()

        #gimp.context_pop()

        copy_settings_parasites(newimg, img)

        gimp.delete(newimg)
        #gimp.Display(newimg)

        # Save parameters as a parasite, even if saving failed,
        # so we can default to them next time.
        # Save: percent, width, height
        # I have no idea what the flags are -- the doc doesn't say.
        para = img.attach_new_parasite('export-scaled', 0,
                                       '%s\n%d\n%d\n%d' % (filename, percent,
                                                           width, height))
        # img.parasite_attach(para)

        return

register(
    "python_fu_export_scaled",
    "Export a copy of the current image, scaled to a different size.",
    "Export a copy of the current image, scaled to a different size.",
    "Akkana Peck",
    "Akkana Peck",
    "2012",
    "Export scaled...",
    "*",
    [
        (PF_IMAGE, "image", "Input image", None),
        (PF_DRAWABLE, "drawable", "Input drawable", None),
    ],
    [],
    python_export_scaled,
    menu = "<Image>/File/Save/"
)

main()

Something went wrong with that request. Please try again.