Scripting Help - Programatically editing Krita files

Is there a recommended way of programmatically editing Krita project files (.kra)?

For context, I’m trying to add support for exporting to Krita for a 3D rendering/modelling tool I’m working on, and thus am trying to convert a series of images into a Krita project file, with each image placed in a separate layer.

Most of the tutorials on plugins seem to be from the perspective of calling python code from inside a running Krita instance, while I need something closer to an API to call out to Krita.

Is there an easy way of doing this, or will I just have to manually construct the project files myself?

Thanks.

I don’t know the details of the project you are working on. But if you want to have access to the flattened result of the kra file then you can extract it. KRA is just a zip file which will have a png image called “mergedimage.png” which will get updated on each save

Hi,

Thanks for the response.

Yes I’ve had a look at the unzipped contents of some Krita project files, and could probably write an exporter that produced such zip files (although I’m not sure what all the files do (like the specifications for the xml files)), however was wondering if there was a way of just leveraging some krita APIs to do this.

Ideally, I’d be against writing my own exporter as it just introduces an additional maintenance cost that could also quickly become outdated as krita itself changes.

It’s not really clear what you’re trying to do, but Krita’s file format is not meant to be an exchange format, there is no formal specification, Krita itself defines how it gets read and written, and the code is basically linked with all parts of Krita.

So there’s no library for external use, let alone bindings to scripting languages.

For the purpose to exchange layered raster images between applications, the OpenRaster (*.ora) format was created. It’s an open specification supported by various applications, however to my knowledge, it never really got out of the draft state, and the link to the reference implementation of libora is dead :frowning:

AFAIK, there were plans to join efforts on the OpenRaster specification at this year’s Libre Graphics Meeting to bring new life to it, but I guess we all know what happened to most of this year’s international events…

Maybe it “never got out of the draft state” but there are already things supported in this format, which is raster layers in PNG format in a zip, basically (plus some matedata I think). Much easier to implement if someone doesn’t care about Colorize Masks etc. and just want a format with multiple layers. Then using Krita one can always save it as a .kra anyway.

Thanks for the response,

That makes sense, I can understand why the Krita file format is not intended to be an exchange format, and thus the corresponding lack of libraries to access it externally.

My wording may have been unclear - It may make more sense to frame my request as that I’m not trying to use Krita as an exchange format, I just want to be able to edit my Krita project files from outside Krita for the purposes of automation - as an example, I’d like to automatically take the output of my tool and insert it into a Krita project (even if Krita isn’t necessarily running).

Achieving this doesn’t need to require external libraries or have to incur additional components in Krita - for example, Blender has a headless scripting mode that allows running python scripts without having to run a GUI, which could then be possibly used for this purpose - Is there something similar for Krita, and would it be possible to edit project files in this way?

There’s the kritarunner application which runs scripts headless, but it needs maintenance; it’s not well tested and not easy to use.

1 Like

Perfect, thanks for the suggestion - this seems like a good place to start looking!

Have you heard about File layers? File Layers — Krita Manual 5.2.0 documentation it seems like it could help you even though it’s not what you’re asking about…

Hey all,

Just a quick update - Thanks @halla for the kritarunner suggestion - it was exactly what I needed.

@tiar, yes - I noticed that option while I was going through the documentation, and while it’s related, a script was also needed to automatically create the file layers.

In case someone else happens to have this problem, here is a simplified form of the script I used:

import sys
import os
import re
from pathlib import *
from krita import *

def eprint(string):
        print(string, file=sys.stderr)

def __main__(args):
    if len(args) != 3:
        eprint("Invalid number of arguments (expecting 3 got {})".format(len(args)))
        return
    filename, basename, image_dir = args
    instance = Krita.instance()
    document = instance.openDocument(filename)
    root = document.rootNode()

    layer_name = "{}-out".format(basename)

    # place all images inside specific layer
    out_layer = document.nodeByName(layer_name)

    if not out_layer:
        out_layer = document.createGroupLayer(layer_name)
        root.addChildNode(out_layer, None)
    if not out_layer:
        eprint("Could not create/find output layer {}".format(layer_name))
        return

    image_dir = Path(image_dir)
    if not image_dir.exists() or not image_dir.is_dir():
        eprint("image dir {} is not a directory or does not exist".format(image_dir))
        return

    image_files = [path.stem,path for path in [path for path in image_dir.iterdir() if path.is_file()]]

    children = {}
    for child in out_layer.childNodes():
        children[child.name()] = child

    for (name, path) in image_files:
        full_path = str(path.resolve())
        position = out_layer.position()
        opacity = out_layer.opacity()

        if name in children and children[name].path() != full_path:
            # file exists, but uses wrong path
            layer = document.createFileLayer(name, full_path, "None")
            child = children[name]
            # copy over properties from child
            ...

            # insert updated layer below old version, then delete
            out_layer.addChildNode(layer, child)
            out_layer.removeChildNode(child)

        elif not name in children:
            # file does not exist
            layer = document.createFileLayer(name, full_path, "None")
            layer.move(position.x(), position.y())
            layer.setOpacity(opacity)
            out_layer.addChildNode(layer, None)

    document.save()
    document.refreshProjection()
3 Likes