Inserting Text on Canvas via Plugin

Hey all, I’ve been trying to figure out how to add a layer with a string of text to the canvas in my plugin. Basically I want to use the built-in text tool, but with my script.

I’ve searched all relevant terms I could think of on the Krita API page, but didn’t find anything that looked like it could add text.
LazyTextTool was the only other plugin I found that does something similar to what I’m trying to do, though much more complicated, and I couldn’t make heads or tails of the implementation there.

I’d really appreciate it if someone could point me in the right direction!

Hello @Citrus8684 and welcome to the forum :slight_smile:

The text tool is being massively updated by @wolthera at the moment.
This is described here: https://krita-artists.org/t/text-tool-thread

The latest post is this one: Text Tool Thread - #130 by wolthera

You may want to wait a while to figure out what’s happening with it.

SVG to rescue!

from krita import Krita
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QColor


def add_document_to_window():
    app = Krita.instance()
    new_doc = app.createDocument(800, 600, "Test", "RGBA", "U8", "", 120.0)
    app.activeWindow().addView(new_doc)
    return new_doc


def add_vector_layer_text(doc, layer_name, text, x=0.0, y=0.0, font_family="'DejaVu Sans', sans-serif", font_size=16, fill=Qt.black):
    new_layer = doc.createVectorLayer(layer_name)
    doc.rootNode().addChildNode(new_layer, None)
    new_layer.addShapesFromSvg(
            f'<svg>'
            f'<text font-family="{font_family}" font-size="{font_size}" fill="{QColor(fill).name()}" x="{x}" y="{y}">'
            f'{text}'
            f'</text>'
            f'</svg>')
    return new_layer
    

doc = add_document_to_window()
add_vector_layer_text(doc, 'my_layer', 'Art enables us to find ourselves and lose ourselves at the same time. --Thomas Merton', x=40.0, y=200.0)

/AkiR

2 Likes

@AhabGreybeard
Thanks for linking the thread. I heard about 5.3 getting a new text tool, but this really does look amazing! Can’t wait to use it!
And as for biding my time until the new tool is out, I believe figuring this out now would still be beneficial, even if I have to fix or rewrite the code later.

@AkiR
You’re a godsend, thank you so much. I’ve been trying to figure this out for the better part of a week now.
A small nitpick; font sizes don’t match up between text created with this function and text created with the text tool, and the function text also doesn’t scale with the canvas’ DPI. Weird thing is, when you copy the SVG Source, make a new text element with the text tool, and paste the source in there, it scales correctly.
My best guess here is that there are some properties in the SVG tag of the text inserted by the text tool that the function is missing. Unfortunately, I have no idea where in the source code to look to investigate this.

After some digging, I found this in the KoSvgTextShapeMarkupConverter.cpp file:

    /**
     * DIRTY-DIRTY-DIRTY HACK ALERT!!!
     *
     * All the internals of QTextDocument work with the primary screen's DPI.
     * That is, when we ask Qt about font metrics, it returns values scaled
     * to the pixels in the current screen, that is,
     * ptValue * qt_defaultDpi() / 72.0. We need to convert this value back
     * into points before writing as SVG. Therefore, all the metrics returned
     * by the document are scaled with `fixQtDpi()`.
     *
     * Official Qt's way to workaround this problem is to set logicalDpiX/Y()
     * values on the paint device's associated with the document. But it seems
     * like in our version of Qt (5.12.12, which is rather old) it doesn't work
     * properly.
     */

This is probably it? Plugging font-size * canvas-DPI / 72 into the font-size yields text that is consistent with the scale of the normal text tool. But it’s still weird to me that the text created by the text tool works with the “normal” values, while the text created by the function doesn’t.

I remember seeing something about font-size adjustment in the LazyTextTool code, so maybe that’ll give me a hint on what’s wrong.
But of course, if you have an idea, please tell me.

2 Likes

@AkiR
Figured it out.
Courtesy of the LazyTextTool plugin:

def ptsToPx(points, dpi):
    return (points/72)*dpi

def svgDocument(svgContent, docWidth=False, docHeight=False, docRes=False):
        svgWidth = str(ptsToPx(docWidth, docRes))
        svgHeight = str(ptsToPx(docHeight, docRes))
        return '''<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<!-- Created using Krita: https://krita.org -->
<svg xmlns="http://www.w3.org/2000/svg" 
    xmlns:xlink="http://www.w3.org/1999/xlink"
    xmlns:krita="http://krita.org/namespaces/svg/krita"
    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
    width="''' + svgWidth + '''pt"
    height="''' + svgHeight + '''pt"
    viewBox="0 0 ''' + svgWidth + ' ' + svgHeight + '''">
<defs/>''' + svgContent + "</svg>"

It was just a matter of setting the width, height and viewport of the <svg> tag correctly!
Thanks again for your help! I probably would have been stuck on this for ages if you didn’t push me in the right direction.

Oh and just a note if anyone wants to do something similar in the future, the text does remain editable with the standard text-tool (as of Krita version 5.2.2 at least).

2 Likes

This topic was automatically closed 4 days after the last reply. New replies are no longer allowed.