spakin / SimpInkScr

Simple Inkscape Scripting
https://inkscape.org/~pakin/%E2%98%85simple-inkscape-scripting
GNU General Public License v3.0
320 stars 31 forks source link

Strange bounding box behavior with text and get_id() #63

Closed andrewzwicky closed 1 year ago

andrewzwicky commented 1 year ago
V_PAD = 2
H_PAD = 5
V_SPACE = 20
H_SPACE = 50

KEYWORDS = [
    "X",
    "LONGER TEXT",
    "FIRST",
]

for index, word in enumerate(KEYWORDS):
    text_obj = text(word, (H_SPACE, (V_SPACE * index) + V_SPACE))

    bbox = text_obj.bounding_box()

    x1, x2 = bbox.x
    y1, y2 = bbox.y

    r = rect((x1 - H_PAD, y1 - V_PAD), (x2 + H_PAD, y2 + V_PAD))

Produces: image


But if I add: text_obj._inkscape_obj.get_id()

V_PAD = 2
H_PAD = 5
V_SPACE = 20
H_SPACE = 50

KEYWORDS = [
    "X",
    "LONGER TEXT",
    "FIRST",
]

for index, word in enumerate(KEYWORDS):
    text_obj = text(word, (H_SPACE, (V_SPACE * index) + V_SPACE))

    # ! this line somehow changes what the bounding_box calculation is?
    text_obj._inkscape_obj.get_id()

    bbox = text_obj.bounding_box()

    x1, x2 = bbox.x
    y1, y2 = bbox.y

    r = rect((x1 - H_PAD, y1 - V_PAD), (x2 + H_PAD, y2 + V_PAD))

I get my intended result of: image

I found this while doing print debugging. Printing somehow caused the output to change, which I tracked to the object id being evaluated, which lead me to this solution.

spakin commented 1 year ago

Wow, that's a weird bug! It took a while to track down, but I believe I now know what's going on.

Inkscape extensions lack an elegant way to acquire the bounding box of a piece of text. The best approach I know is to spawn another copy of Inkscape with command-line options requesting the bounding box of a given object ID. However, in the case of freshly generated text, an ID has not yet been assigned so invalid values come back from Inkscape.

According to the documentation for get_id, the function will

Get the id for the element, will set a new unique id if not set.

Hence, when you invoke text_obj._inkscape_obj.get_id(), this has a side effect of assigning an ID to the text object, and that enables Inkscape to return a valid bounding box.

I'll modify Simple Inkscape Scripting always to invoke get_id before launching Inkscape to query an object's bounding box. That should take just a moment…