How to convert XCF to PNG using GIMP from the command-line?

22,824

Solution 1

There are a few ways to go through this - my preferred method is always developing a GIMP-Python plug-in. Script-fu uses GIMP's built-in scheme implementation, which is extremely poor in handling I/O (such as listing files in a directory) - a task which is absolutely trivial in Python.

SO, since you said you don't want to save new scripts (you could do it because you can add new plug-in and scripts directories on edit->preferences->folder options so that you don't need to write on ~/.gimp*/plugins/)

Anyway, you can open the python console under filters, and paste this snippet in the interactive prompt.

import gimpfu
def convert(filename):
    img = pdb.gimp_file_load(filename, filename)
    new_name = filename.rsplit(".",1)[0] + ".png"
    layer = pdb.gimp_image_merge_visible_layers(img, gimpfu.CLIP_TO_IMAGE)

    pdb.gimp_file_save(img, layer, new_name, new_name)
    pdb.gimp_image_delete(img)

This small function will open any image, passed as a file path, flatten it, and save it as a png with the default settings, in the same directory.

Following the function, you can simply type:

from glob import glob
for filename in glob("*.xcf"):
    convert(filename)

On the interactive prompt to convert all .xcf files on the current directory to .png

(If you don't have gimp-python installed, it may be a separate package on your linux distribution, just install it using your favorite tool - for those under windows, and gimp-2.6, the instructions on this page have have to be followed - it should become easier on gimp 2.8)

Another way, altogether, applies if your images are sequentially numbered in their filenames, such as myimage_001.xcf, mymage_002.xcf and so on. If they are so arranged you could install GIMP-GAP (gimp animation package) which allows one to apply any filters or actions in such an image sequence.

Solution 2

Before jsbueno posted his answer I had also tried asking on the #gimp IRC channel. I was directed to this thread on Gimptalk which contains the following code:

gimp -n -i -b - <<EOF
(let* ( (file's (cadr (file-glob "*.xcf" 1))) (filename "") (image 0) (layer 0) )
  (while (pair? file's) 
    (set! image (car (gimp-file-load RUN-NONINTERACTIVE (car file's) (car file's))))
    (set! layer (car (gimp-image-merge-visible-layers image CLIP-TO-IMAGE)))
    (set! filename (string-append (substring (car file's) 0 (- (string-length (car file's)) 4)) ".png"))
    (gimp-file-save RUN-NONINTERACTIVE image layer filename filename)
    (gimp-image-delete image)
    (set! file's (cdr file's))
    )
  (gimp-quit 0)
  )
EOF

This scriptfu globs for xcf files, and then for each file it loads the file, merges the visible layers, saves the result as a PNG, and "unloads" the image. Finally, it quits GIMP. The glob approach is used to avoid starting up GIMP for each image. It also side-steps the issue of getting parameters from the shell into gimp.

I'm posting this answer just in case someone needs a way to do this without the use of GIMP-Python (perhaps because it isn't installed).

Solution 3

Here is my solution utilizing GIMP, GIMP's python-fu and bash. Note that GIMP's python-fu can only be run under a gimp process, not under plain python.

#!/bin/bash
set -e

while getopts df: OPTION "$@"; do
    case $OPTION in
    d)
        set -x
        ;;
    f)
        XCFFILE=${OPTARG}
        ;;
    esac
done

if [[ -z "$XCFFILE" ]]; then
    echo "usage: `basename $0` [-d] -f <xcfFile>"
    exit 1
fi

# Start gimp with python-fu batch-interpreter
gimp -i --batch-interpreter=python-fu-eval -b - << EOF
import gimpfu

def convert(filename):
    img = pdb.gimp_file_load(filename, filename)
    new_name = filename.rsplit(".",1)[0] + ".png"
    layer = pdb.gimp_image_merge_visible_layers(img, 1)

    pdb.gimp_file_save(img, layer, new_name, new_name)
    pdb.gimp_image_delete(img)

convert('${XCFFILE}')

pdb.gimp_quit(1)
EOF

Solution 4

Use xcftools

It's possible that this tool wasn't available (or in any case widely known) at the time this question was originally asked, but the xcf2png terminal command from the xcftools package does exactly what you ask and is very good. I use it for my thesis scripts and would be my go-to choice for this question.

From the documentation:

This is a set of fast command-line tools for extracting information from the Gimp's native file format XCF.
The tools are designed to allow efficient use of layered XCF files as sources in a build system that use 'make' and similar tools to manage automatic processing of the graphics.
These tools work independently of the Gimp engine and do not require the Gimp to even be installed.

"xcf2png" converts XCF files to PNG format, flattening layers if necessary. Transparency information can be kept in the image, or a background color can be specified on the command line.


(PS. I spotted xcftools mentioned in the comments just after I decided to post an answer about it, but I'm posting anyway as that comment was easy to miss (I saw it because I specifically checked for it) and I think xcftools deserves a proper answer's slot, as it really is the most appropriate answer at this point in time.)

Solution 5

I know this is not strictly the correct answer but seeing as a I got directed here while searching.

I modified the example code here to create a workable plugin

http://registry.gimp.org/node/28124

There is a bit of code from another answer on this page

#!/usr/bin/env python

from gimpfu import *
import os



def batch_convert_xcf_to_png(img, layer, inputFolder, outputFolder):
    ''' Convert the xcf images in a directory to png.

    Parameters:
    img : image The current image (unused).
    layer : layer The layer of the image that is selected (unused).
    inputFolder : string The folder of the images that must be modified.
    outputFolder : string The folder in which save the modified images.
    '''
    # Iterate the folder
    for file in os.listdir(inputFolder):
        try:
            # Build the full file paths.
            inputPath = inputFolder + "\\" + file

            new_name = file.rsplit(".",1)[0] + ".png"
            outputPath = outputFolder + "\\" + new_name

            # Open the file if is a XCF image.
            image = None
            if(file.lower().endswith(('.xcf'))):
                image = pdb.gimp_xcf_load(1,inputPath, inputPath)


            # Verify if the file is an image.
            if(image != None):

                #layer = pdb.gimp_image_merge_visible_layers(image, gimpfu.CLIP_TO_IMAGE)
                layer = pdb.gimp_image_merge_visible_layers(image, 1)

                # Save the image.
                pdb.file_png_save(image, image.layers[0], outputPath, outputPath, 0, 9, 0, 0, 0, 0, 0)

        except Exception as err:
            gimp.message("Unexpected error: " + str(err))   




'''                
    img = pdb.gimp_file_load(filename, filename)
    new_name = filename.rsplit(".",1)[0] + ".png"
    layer = pdb.gimp_image_merge_visible_layers(img, gimpfu.CLIP_TO_IMAGE)

    pdb.gimp_file_save(img, layer, new_name, new_name)
    pdb.gimp_image_delete(img)
'''    


register(
        "batch_convert_xcf_to_png",
        "convert chris",
        "convert ",
        "Chris O'Halloran",
        "Chris O'Halloran",
        "2014",
        "<Image>/Filters/Test/Batch batch_convert_xcf_to_png",
        "*",
        [
            (PF_DIRNAME, "inputFolder", "Input directory", ""),
            (PF_DIRNAME, "outputFolder", "Output directory", ""),            
        ],
        [],
        batch_convert_xcf_to_png)



main()
Share:
22,824
Laurence Gonsalves
Author by

Laurence Gonsalves

I'm the founder of Niphtio. If you'd like to work with me, we're hiring! When it comes to computing, I'm a bit of a generalist: I'm interested in programming language design, compilers, computer graphics, machine learning, robotics, games, and more. I regularly use Python, Kotlin, Java and C++, and am also a fan of Scheme. I like functional programming and static typing, but am not really a fan of Haskell... go figure. My editor of choice is Vim. I grew up with a Commodore 64, then a 128 and eventually had an Amiga. These days I primarily use Linux.

Updated on March 22, 2020

Comments

  • Laurence Gonsalves
    Laurence Gonsalves about 4 years

    As part of my build process I need to convert a number of XCF (GIMP's native format) images into PNG format. I'm sure this should be possible using GIMP's batch mode, but I have forgotten all of the script-fu I used to know.

    My input images have multiple layers, so I need the batch mode equivalent of "merge visible layers" followed by "save as PNG". Also note that I can't install anything in ~/.gimp*/scripts/ — I need a self-contained command-line, or a way to install scripts in my source tree.

    Note that while this is similar to this question, I have the additional constraint that I need this to be done using GIMP. I tried the current version of ImageMagick and it mangles my test images.

  • Laurence Gonsalves
    Laurence Gonsalves about 13 years
    Interesting! I didn't know about Python-Fu. That's certainly a lot nicer than Script-Fu's lobotomized Scheme implementation (and very non-lispy API). I want this to be completely non-interactive, though, so what's the best way to invoke Python-Fu from the command-line? gimp-console --batch-interpreter python-fu-eval -b - <myscript seems to work -- is there a better/less-verbose way?
  • Laurence Gonsalves
    Laurence Gonsalves about 13 years
    I just realized that I meant merge visible layers, not flatten. (I want the alpha channel.) I updated the question and your answer to match.
  • jsbueno
    jsbueno about 13 years
    As for starting python commands from the command line, if you write a python script witch registers itself as a plug-in (check the examples tahtc ome along with GIMP), you can call it straight after the --batch-interpreter parameter, instead of going though the pyhon-fu-eval bit
  • Kacper Perschke
    Kacper Perschke over 10 years
    I had to change [[ … ]] to [ … ]. Except this small change works brilliantly.
  • Nicolas Mattia
    Nicolas Mattia about 9 years
    Looks nice, but if I understand correctly, gimp is started and closed for each file. Could that be avoided?
  • mxmlnkn
    mxmlnkn about 5 years
    Unfortunately, I get Warning: XCF version 8 not supported (trying anyway...). Seems like the last update was in 2009 and even the fork is quite old and does not work.
  • ChameleonScales
    ChameleonScales over 4 years
    Thank you, this works great. Any idea how I might go about setting the jpeg quality with that script? I'd also like it to find files in subfolders if that's possible.
  • jamacoe
    jamacoe about 4 years
    I have german filenames with international characters like ä, ö and ü. Glob won't return the correct filename but an empty line if I 'print filename' in the loop. What can I do?
  • MRule
    MRule over 2 years
    This answer is out of date and no longer works as of October 2021. It fails with Warning: XCF version 11 not supported (trying anyway...). The converted PNG files are wrong (empty with white background).