Python and Vector Layers

Hi

I’m trying to work SVG layers.
Already read this topic:

But it didn’t provided me answers…

What I want is to be able to import SVG file content into a vector layer

But VectorLayer class doesn’t provides anything useful.
Or I’m missing something… ??

What I’ve tried:

import os
from krita import Krita
from PyQt5.Qt import *

svgContent= """\
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
   width="100mm"
   height="100mm"
   viewBox="0 0 100 100">
  <path
     style="color:#000000"
     d="m 85.356686,14.643317 c -19.524422,-19.5244203 -51.188952,-19.5244203 -70.713372,0 -19.5244192,19.52442 -19.5244186,51.18895 0,70.71336 19.524421,19.524433 51.18895,19.524433 70.713372,0 19.524414,-19.52441 19.524424,-51.18894 0,-70.71336 z m -0.434405,0.4344 c 19.289639,19.28964 19.289419,50.5547 -2.21e-4,69.84434 -0.0548,0.0548 -0.110913,0.10737 -0.165906,0.16186 l -69.840301,-69.8403 c 0.05449,-0.055 0.107062,-0.1111 0.161862,-0.1659 19.289637,-19.2896403 50.554933,-19.2896403 69.844566,0 z"
     id="path12"/>
</svg>
"""

def saveSvg(fileName):
    with open(fileName, "wb") as f:
        f.write(svgContent.encode("utf-8"))


def importSvg(filename, doc):
    # - open svg file as new document
    # - first node is vector layer: clone layer 
    # - affect it to new document
    Krita.instance().setBatchmode(True)
    svgDoc=Krita.instance().openDocument(filename)
    Krita.instance().setBatchmode(False)
    
    newLayer=svgDoc.rootNode().childNodes()[0].clone()

    doc.rootNode().addChildNode(newLayer, None)
    # without waitForDone(), crash here (segmentation fault)
    doc.waitForDone()
    
    svgDoc.close()

    # doc is properly saved, kra file can be opened content match expected result
    doc.saveAs(os.path.expanduser("~/test_svg.kra"))

    # crash here (segmentation fault)
    doc.refreshProjection()

fileName = os.path.abspath(os.path.expanduser("~/test.svg"))
saveSvg(fileName)

doc = Krita.instance().createDocument(1000, 1000, "Test", "RGBA", "U8", "", 100.0)
Krita.instance().activeWindow().addView(doc)

importSvg(fileName, doc)

According to documentation on Node.clone() method:

The node is not associated with any image.

So I guess that cloning a node from a document, and add it to another one should be possible.

Execution works properly:

  • SVG file is opened
  • Vector layer is cloned
  • Clone is added to new document
  • New document can be saved (and content match expected results)

But finally, Krita crash after document is saved (crash without saving too, the save action is not the problem :slight_smile:

So, I’m wondering if there’s another method to do that without crash.
Anyone has an idea?

Also tried copy/paste layer method, but I have crash too…

Reading documentation, it seems that except to create a new empty vector layer, there’s no real possibility to manipulate content (add/remove shapes…) :thinking:

Tested on Linux Debian 10, Appimage (4.2.8, 4.2.9, 4.4.0, 4.4.1, 4.4.2b2, 5.0.0) without any success.

Grum999

1 Like

Yes, probably there is none… it would just need to be added.

Yep. There is two options:
Either you rasterize your vector layer and then past it using bytearray
Or you open it as seperate file

Hi

Can’t use any of these options, goal for me is to improve Composition Helper plugin, trying to add vector layer in document instead of paint layer :slight_smile:

I’ll continue to test, trying to understand why Krita crash after copying a vector layer node and hope to found a solution…

Grum999

I didn’t found why there’s a crash… but I finally found a solution.
Not the best, don’t really like it, but it does the job.

The tip is to copy svg content to clipboard, and paste it :slight_smile:

I don’t like it because the clipboard content is lost (even if it’s technically possible to get all clipboard content, save it in a cache, put svg content to clipboard, paste it, restore clipboard)

Following code allows to create svg content into a vector layer:

import os
from krita import Krita
from PyQt5.Qt import *

svgContent= """\
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
   width="100mm"
   height="100mm"
   viewBox="0 0 100 100">
  <path
     style="color:#000000"
     d="m 85.356686,14.643317 c -19.524422,-19.5244203 -51.188952,-19.5244203 -70.713372,0 -19.5244192,19.52442 -19.5244186,51.18895 0,70.71336 19.524421,19.524433 51.18895,19.524433 70.713372,0 19.524414,-19.52441 19.524424,-51.18894 0,-70.71336 z m -0.434405,0.4344 c 19.289639,19.28964 19.289419,50.5547 -2.21e-4,69.84434 -0.0548,0.0548 -0.110913,0.10737 -0.165906,0.16186 l -69.840301,-69.8403 c 0.05449,-0.055 0.107062,-0.1111 0.161862,-0.1659 19.289637,-19.2896403 50.554933,-19.2896403 69.844566,0 z"
     id="path12"/>
</svg>"""

def sleep(value):
    loop = QEventLoop()
    QTimer.singleShot(value, loop.quit)
    loop.exec()

def importSvg(svgContent, layer):
    if not layer is None:    
        doc.setActiveNode(vLayer)
        #doc.waitForDone()  # ==> waitForDone() doesn't work, need to apply a sleep :-(
        sleep(150)
        
    mimeContent=QMimeData()
    mimeContent.setData('image/svg', svgContent.encode())
    QGuiApplication.clipboard().setMimeData(mimeContent)
    Krita.instance().action('edit_paste').trigger()

doc = Krita.instance().createDocument(1000, 1000, "Test", "RGBA", "U8", "", 100.0)
Krita.instance().activeWindow().addView(doc)

vLayer=doc.createVectorLayer('test-v')
doc.rootNode().addChildNode(vLayer, None)

importSvg(svgContent, vLayer)

doc.refreshProjection()

Note

I had to made a call to a sleep() method
It seem setActiveNode() method is internally executed asyncronously but calling waitForDone() doesn’t change anything to result…

Unfortunately, applying a delay after layer selection is the only way “to be sure” that expected layer is active (but if delay is too short it doesn’t work :frowning: and if delay is too long it’s not fluid…)

Grum999