View Issue Details

IDProjectCategoryView StatusLast Update
0017398ScribusScripterpublic2025-02-09 12:06
Reporterhugowett Assigned Tojghali  
PrioritynormalSeverityfeatureReproducibilityN/A
Status resolvedResolutionfixed 
Product Version1.6.4.svn 
Fixed in Version1.6.4.svn 
Summary0017398: Add script methods for getting and setting min word kerning/tracking on a frame
DescriptionThis patch adds setTextTracking(), setTextWordTracking() and setTextMinWordTracking() that can be used to set the kerning for a frame. They all accept the tracking as in percents.

Very useful/necessary functionality for scripting automatic handling of over and underflow with a combination of scaling and kerning.
Tagsautomation, scripter
PatchYes

Relationships

related to 0017334 closedcbradney Add API endpoint setTracking and getTracking 

Activities

hugowett

2025-01-31 07:48

reporter  

0001-scripting-add-functions-for-setting-frame-letter-and.patch (7,045 bytes)   
From 8b382ec982bce665aca530b1a2bc9b2b345517b1 Mon Sep 17 00:00:00 2001
From: Hugo Wetterberg <hugo@wetterberg.nu>
Date: Fri, 31 Jan 2025 08:35:07 +0100
Subject: [PATCH] scripting: add functions for setting frame letter and word
 kerning

---
 scribus/plugins/scriptplugin/cmdtext.cpp      | 108 ++++++++++++++++++
 scribus/plugins/scriptplugin/cmdtext.h        |  30 +++++
 scribus/plugins/scriptplugin/scriptplugin.cpp |   3 +
 3 files changed, 141 insertions(+)

diff --git scribus/plugins/scriptplugin/cmdtext.cpp scribus/plugins/scriptplugin/cmdtext.cpp
index 2a60a7099..2f45a3ed6 100644
--- scribus/plugins/scriptplugin/cmdtext.cpp
+++ scribus/plugins/scriptplugin/cmdtext.cpp
@@ -1291,6 +1291,114 @@ PyObject *scribus_settextscalingv(PyObject* /* self */, PyObject* args)
 	Py_RETURN_NONE;
 }
 
+PyObject *scribus_settexttracking(PyObject* /* self */, PyObject* args)
+{
+	PyESString name;
+	double tt;
+	if (!PyArg_ParseTuple(args, "d|es", &tt, "utf-8", name.ptr()))
+		return nullptr;
+	if (!checkHaveDocument())
+		return nullptr;
+
+	PageItem *item = GetUniqueItem(QString::fromUtf8(name.c_str()));
+	if (item == nullptr)
+		return nullptr;
+	if (!item->isTextFrame())
+	{
+		PyErr_SetString(WrongFrameTypeError, QObject::tr("Cannot set tracking on a non-text frame.","python error").toLocal8Bit().constData());
+		return nullptr;
+	}
+
+	ScribusDoc* doc = ScCore->primaryMainWindow()->doc;
+	int oldAppMode = ScCore->primaryMainWindow()->doc->appMode;
+
+	Selection tmpSelection(nullptr, false);
+	tmpSelection.addItem(item);
+	if (item->HasSel)
+		doc->appMode = modeEdit;
+	doc->itemSelection_SetTracking(qRound(tt*10), &tmpSelection);
+	doc->appMode = oldAppMode;
+
+	Py_RETURN_NONE;
+}
+
+PyObject *scribus_settextwordtracking(PyObject* /* self */, PyObject* args)
+{
+	PyESString name;
+	double wt;
+	if (!PyArg_ParseTuple(args, "d|es", &wt, "utf-8", name.ptr()))
+		return nullptr;
+	if (!checkHaveDocument())
+		return nullptr;
+	if (wt < 1)
+	{
+		PyErr_SetString(PyExc_ValueError, QObject::tr("Word tracking out of bounds, must be > 1","python error").toLocal8Bit().constData());
+		return nullptr;
+	}
+	PageItem *item = GetUniqueItem(QString::fromUtf8(name.c_str()));
+	if (item == nullptr)
+		return nullptr;
+	if (!item->isTextFrame())
+	{
+		PyErr_SetString(WrongFrameTypeError, QObject::tr("Cannot set word tracking on a non-text frame.","python error").toLocal8Bit().constData());
+		return nullptr;
+	}
+
+	ScribusDoc* doc = ScCore->primaryMainWindow()->doc;
+	int oldAppMode = ScCore->primaryMainWindow()->doc->appMode;
+
+	Selection tmpSelection(nullptr, false);
+	tmpSelection.addItem(item);
+	if (item->HasSel)
+		doc->appMode = modeEdit;
+
+	ParagraphStyle newStyle;
+	newStyle.charStyle().setWordTracking(wt / 100.0);
+	doc->itemSelection_ApplyParagraphStyle(newStyle, &tmpSelection);
+
+	doc->appMode = oldAppMode;
+
+	Py_RETURN_NONE;
+}
+
+PyObject *scribus_settextminwordtracking(PyObject* /* self */, PyObject* args)
+{
+	PyESString name;
+	double wt;
+	if (!PyArg_ParseTuple(args, "d|es", &wt, "utf-8", name.ptr()))
+		return nullptr;
+	if (!checkHaveDocument())
+		return nullptr;
+	if (wt < 1 || wt > 100)
+	{
+		PyErr_SetString(PyExc_ValueError, QObject::tr("Minimum word out of bounds, must be >= 1 and <= 100","python error").toLocal8Bit().constData());
+		return nullptr;
+	}
+	PageItem *item = GetUniqueItem(QString::fromUtf8(name.c_str()));
+	if (item == nullptr)
+		return nullptr;
+	if (!item->isTextFrame())
+	{
+		PyErr_SetString(WrongFrameTypeError, QObject::tr("Cannot set minimum word tracking on a non-text frame.","python error").toLocal8Bit().constData());
+		return nullptr;
+	}
+
+	ScribusDoc* doc = ScCore->primaryMainWindow()->doc;
+	int oldAppMode = ScCore->primaryMainWindow()->doc->appMode;
+
+	Selection tmpSelection(nullptr, false);
+	tmpSelection.addItem(item);
+	if (item->HasSel)
+		doc->appMode = modeEdit;
+
+	ParagraphStyle newStyle;
+	newStyle.setMinWordTracking(wt / 100.0);
+	doc->itemSelection_ApplyParagraphStyle(newStyle, &tmpSelection);
+
+	doc->appMode = oldAppMode;
+
+	Py_RETURN_NONE;
+}
 
 PyObject *scribus_settextshade(PyObject* /* self */, PyObject* args)
 {
diff --git scribus/plugins/scriptplugin/cmdtext.h scribus/plugins/scriptplugin/cmdtext.h
index 1cf4fd196..93d76a70d 100644
--- scribus/plugins/scriptplugin/cmdtext.h
+++ scribus/plugins/scriptplugin/cmdtext.h
@@ -579,6 +579,36 @@ If \"name\" is not given the currently selected item is used.\n\
 /*! Set text shde */
 PyObject *scribus_settextscalingh(PyObject * /*self*/, PyObject* args);
 
+/*! docstring */
+PyDoc_STRVAR(scribus_settexttracking__doc__,
+QT_TR_NOOP("setTextTracking(ratio, [\"name\"])\n\
+\n\
+Sets the text tracking of the object \"name\" to \"ratio\" in percent.\n\
+If \"name\" is not given the currently selected item is used.\n\
+"));
+/*! Set text tracking */
+PyObject *scribus_settexttracking(PyObject * /*self*/, PyObject* args);
+
+/*! docstring */
+PyDoc_STRVAR(scribus_settextwordtracking__doc__,
+QT_TR_NOOP("setTextWordTracking(ratio, [\"name\"])\n\
+\n\
+Sets the word tracking of the object \"name\" to \"ratio\" in percent.\n\
+If \"name\" is not given the currently selected item is used.\n\
+"));
+/*! Set text word tracking */
+PyObject *scribus_settextwordtracking(PyObject * /*self*/, PyObject* args);
+
+/*! docstring */
+PyDoc_STRVAR(scribus_settextminwordtracking__doc__,
+QT_TR_NOOP("setTextMinWordTracking(ratio, [\"name\"])\n\
+\n\
+Sets the minimum word tracking of the object \"name\" to \"ratio\" in percent.\n\
+If \"name\" is not given the currently selected item is used.\n\
+"));
+/*! Set text minimum word tracking */
+PyObject *scribus_settextminwordtracking(PyObject * /*self*/, PyObject* args);
+
 /*! docstring */
 PyDoc_STRVAR(scribus_settextshade__doc__,
 QT_TR_NOOP("setTextShade(shade, [\"name\"])\n\
diff --git scribus/plugins/scriptplugin/scriptplugin.cpp scribus/plugins/scriptplugin/scriptplugin.cpp
index 49c00b156..c473e6f8a 100644
--- scribus/plugins/scriptplugin/scriptplugin.cpp
+++ scribus/plugins/scriptplugin/scriptplugin.cpp
@@ -598,6 +598,9 @@ PyMethodDef scribus_methods[] = {
 	{ "setTextFlowMode", scribus_settextflowmode, METH_VARARGS, tr(scribus_settextflowmode__doc__)},
 	{ "setTextScalingH", scribus_settextscalingh, METH_VARARGS, tr(scribus_settextscalingh__doc__)},
 	{ "setTextScalingV", scribus_settextscalingv, METH_VARARGS, tr(scribus_settextscalingv__doc__)},
+	{ "setTextTracking", scribus_settexttracking, METH_VARARGS, tr(scribus_settexttracking__doc__)},
+	{ "setTextWordTracking", scribus_settextwordtracking, METH_VARARGS, tr(scribus_settextwordtracking__doc__)},
+	{ "setTextMinWordTracking", scribus_settextminwordtracking, METH_VARARGS, tr(scribus_settextwordtracking__doc__)},
 	{ "setTextShade", scribus_settextshade, METH_VARARGS, tr(scribus_settextshade__doc__)},
 	{ "setTextStroke", scribus_settextstroke, METH_VARARGS, tr(scribus_settextstroke__doc__)},
 	{ "setTextVerticalAlignment", scribus_settextverticalalignment, METH_VARARGS, tr(scribus_settextverticalalignment__doc__)},
-- 
2.48.1

jghali

2025-02-04 20:38

administrator   ~0052010

Last edited: 2025-02-04 20:41

The functionnalities provided by setTextTracking() and setTextWordTracking() are already available in trunk :
- setTextTracking() is available in trunk as setTracking()
- setTextWordTracking() is available in trunk as setWordTracking()
So for these two functions, best is to backport what is already existing in trunk. Trunk has also getters for tracking and word tracking. As for setTextMinWordTracking(), for consistency it should probably be renamed to setMinWordTracking().

hugowett

2025-02-05 15:01

reporter   ~0052011

Ok, gotcha, should I submit one patch for trunk and one for v1.6?

jghali

2025-02-05 21:33

administrator   ~0052015

One patch will be sufficient. I just backported the setTracking() and setWordTracking() functions from trunk to 1.6.4.svn. So now you just need to provide a patch for setMinWordTracking().

hugowett

2025-02-06 15:02

reporter   ~0052017

Here's a patch based on trunk that adds getMinWordTracking and setMinWordTracking. Also noticed that the normal word tracking was documented as being an integer, so I adjusted that to just being a "number", not sure if that's the correct terminology, but the value for setWordTracking can be either a float or int.
0001-scripting-add-functions-for-setting-and-getting-min-.patch (7,817 bytes)   
From 2ca43b02dd443e884e8c2acdf75cd011aac230ae Mon Sep 17 00:00:00 2001
From: Hugo Wetterberg <hugo@wetterberg.nu>
Date: Thu, 6 Feb 2025 15:56:43 +0100
Subject: [PATCH] scripting: add functions for setting and getting min word
 tracking

Also fixes documentation that stated that word tracking is an integer value.
---
 scribus/plugins/scriptplugin/cmdtext.cpp      | 69 ++++++++++++++++++-
 scribus/plugins/scriptplugin/cmdtext.h        | 28 +++++++-
 scribus/plugins/scriptplugin/scriptplugin.cpp |  2 +
 3 files changed, 96 insertions(+), 3 deletions(-)

diff --git scribus/plugins/scriptplugin/cmdtext.cpp scribus/plugins/scriptplugin/cmdtext.cpp
index 2ce25ecef..ec77307f2 100644
--- scribus/plugins/scriptplugin/cmdtext.cpp
+++ scribus/plugins/scriptplugin/cmdtext.cpp
@@ -207,6 +207,34 @@ PyObject *scribus_getwordtracking(PyObject* /* self */, PyObject* args)
 	return PyLong_FromLong(item->currentCharStyle().wordTracking()*100.0);
 }
 
+PyObject *scribus_getminwordtracking(PyObject* /* self */, PyObject* args)
+{
+	PyESString name;
+	if (!PyArg_ParseTuple(args, "|es", "utf-8", name.ptr()))
+		return nullptr;
+	if (!checkHaveDocument())
+		return nullptr;
+	const PageItem *item = GetUniqueItem(QString::fromUtf8(name.c_str()));
+	if (item == nullptr)
+		return nullptr;
+	if (!(item->isTextFrame()) && !(item->isPathText()))
+	{
+		PyErr_SetString(WrongFrameTypeError, QObject::tr("Cannot get word tracking of non-text frame.", "python error").toLocal8Bit().constData());
+		return nullptr;
+	}
+	if (item->HasSel)
+	{
+		for (int i = 0; i < item->itemText.length(); ++i)
+		{
+			if (item->itemText.selected(i))
+				return PyLong_FromLong(item->itemText.paragraphStyle(i).minWordTracking());
+		}
+		return nullptr;
+	}
+
+	return PyLong_FromLong(item->currentStyle().minWordTracking()*100.0);
+}
+
 PyObject *scribus_gettextlength(PyObject* /* self */, PyObject* args)
 {
 	PyESString name;
@@ -992,6 +1020,46 @@ PyObject *scribus_setwordtracking(PyObject* /* self */, PyObject* args)
 	Py_RETURN_NONE;
 }
 
+PyObject *scribus_setminwordtracking(PyObject* /* self */, PyObject* args)
+{
+	PyESString name;
+	double wt;
+	if (!PyArg_ParseTuple(args, "d|es", &wt, "utf-8", name.ptr()))
+		return nullptr;
+	if (!checkHaveDocument())
+		return nullptr;
+	if (wt < 1 || wt > 100)
+	{
+		PyErr_SetString(PyExc_ValueError, QObject::tr("Minimum word out of bounds, must be >= 1 and <= 100","python error").toLocal8Bit().constData());
+		return nullptr;
+	}
+	PageItem *item = GetUniqueItem(QString::fromUtf8(name.c_str()));
+	if (item == nullptr)
+		return nullptr;
+	if (!item->isTextFrame())
+	{
+		PyErr_SetString(WrongFrameTypeError, QObject::tr("Cannot set minimum word tracking on a non-text frame.","python error").toLocal8Bit().constData());
+		return nullptr;
+	}
+
+	ScribusDoc* doc = ScCore->primaryMainWindow()->doc;
+	int oldAppMode = ScCore->primaryMainWindow()->doc->appMode;
+
+	Selection tmpSelection(nullptr, false);
+	tmpSelection.addItem(item);
+	if (item->HasSel)
+		doc->appMode = modeEdit;
+
+	ParagraphStyle newStyle;
+	newStyle.setMinWordTracking(wt / 100.0);
+	doc->itemSelection_ApplyParagraphStyle(newStyle, &tmpSelection);
+
+	doc->appMode = oldAppMode;
+
+	Py_RETURN_NONE;
+}
+
+
 PyObject *scribus_setlinespacingmode(PyObject* /* self */, PyObject* args)
 {
 	PyESString name;
@@ -1405,7 +1473,6 @@ PyObject *scribus_settextscalingv(PyObject* /* self */, PyObject* args)
 	Py_RETURN_NONE;
 }
 
-
 PyObject *scribus_settextshade(PyObject* /* self */, PyObject* args)
 {
 	PyESString name;
diff --git scribus/plugins/scriptplugin/cmdtext.h scribus/plugins/scriptplugin/cmdtext.h
index 581226c09..9b7ce60f5 100644
--- scribus/plugins/scriptplugin/cmdtext.h
+++ scribus/plugins/scriptplugin/cmdtext.h
@@ -201,7 +201,7 @@ PyObject *scribus_gettracking(PyObject * /*self*/, PyObject* args);
 
 /*! docstring */
 PyDoc_STRVAR(scribus_getwordtracking__doc__,
-QT_TR_NOOP("getWordTracking([\"name\"]) -> integer\n\
+QT_TR_NOOP("getWordTracking([\"name\"]) -> float\n\
 \n\
 Gets the word tracking of text inside text frame \"name\".\n\
 If \"name\" is not given the currently selected item is used. \n\
@@ -210,6 +210,18 @@ If there is some text selected only the selected text tracking is returned.\n\
 /*! Get word tracking */
 PyObject *scribus_getwordtracking(PyObject * /*self*/, PyObject* args);
 
+/*! docstring */
+PyDoc_STRVAR(scribus_getminwordtracking__doc__,
+QT_TR_NOOP("getMinWordTracking([\"name\"]) -> float\n\
+\n\
+Gets the word tracking of text inside text frame \"name\".\n\
+If \"name\" is not given the currently selected item is used. \n\
+If there is some text selected only the selected text tracking is returned.\n\
+"));
+/*! Get minimum word tracking */
+PyObject *scribus_getminwordtracking(PyObject * /*self*/, PyObject* args);
+
+
 /*! docstring */
 PyDoc_STRVAR(scribus_getlinespacing__doc__,
 QT_TR_NOOP("getLineSpacing([\"name\"]) -> float\n\
@@ -634,12 +646,24 @@ QT_TR_NOOP("setWordTracking(kern, [\"name\"])\n\
 \n\
 Sets the word tracking of the text the object \"name\" to \"kern\". If\n\
 there is some text selected only the selected text is changed. \"kern\" must\n\
-be an integer. If \"name\" is not given the currently selected item is\n\
+be an number. If \"name\" is not given the currently selected item is\n\
 used.\n\
 "));
 /*! Set text word tracking */
 PyObject *scribus_setwordtracking(PyObject * /*self*/, PyObject* args);
 
+/*! docstring */
+PyDoc_STRVAR(scribus_setminwordtracking__doc__,
+QT_TR_NOOP("setMinWordTracking(kern, [\"name\"])\n\
+\n\
+Sets the minimum word tracking of the text the object \"name\" to \"kern\".\n\
+If there is some text selected only the selected text is changed. \"kern\"\n\
+must be an number. If \"name\" is not given the currently selected item is\n\
+used.\n\
+"));
+/*! Set text minimum word tracking */
+PyObject *scribus_setminwordtracking(PyObject * /*self*/, PyObject* args);
+
 /*! docstring */
 PyDoc_STRVAR(scribus_linktextframes__doc__,
 QT_TR_NOOP("linkTextFrames(\"fromname\", \"toname\")\n\
diff --git scribus/plugins/scriptplugin/scriptplugin.cpp scribus/plugins/scriptplugin/scriptplugin.cpp
index 5a5bdd175..222d7a628 100644
--- scribus/plugins/scriptplugin/scriptplugin.cpp
+++ scribus/plugins/scriptplugin/scriptplugin.cpp
@@ -436,6 +436,7 @@ PyMethodDef scribus_methods[] = {
 	{ "getUnit", (PyCFunction) scribus_getunit, METH_NOARGS, tr(scribus_getunit__doc__)},
 	{ "getVisualBoundingBox", scribus_getvisualboundingbox, METH_VARARGS, tr(scribus_getvisualboundingbox__doc__) },
 	{ "getWordTracking", scribus_getwordtracking, METH_VARARGS, tr(scribus_getwordtracking__doc__)},
+	{ "getMinWordTracking", scribus_getminwordtracking, METH_VARARGS, tr(scribus_getminwordtracking__doc__)},
 	{ "pointsToDocUnit", scribus_pointstodocunit, METH_VARARGS, tr(scribus_pointstodocunit__doc__)},
 	{ "docUnitToPoints", scribus_docunittopoints, METH_VARARGS, tr(scribus_docunittopoints__doc__)},
 	{ "stringValueToPoints", scribus_stringvaluetopoints, METH_VARARGS, tr(scribus_stringvaluetopoints__doc__)},
@@ -603,6 +604,7 @@ PyMethodDef scribus_methods[] = {
 	{ "setTextFlowMode", scribus_settextflowmode, METH_VARARGS, tr(scribus_settextflowmode__doc__)},
 	{ "setTextScalingH", scribus_settextscalingh, METH_VARARGS, tr(scribus_settextscalingh__doc__)},
 	{ "setTextScalingV", scribus_settextscalingv, METH_VARARGS, tr(scribus_settextscalingv__doc__)},
+	{ "setMinWordTracking", scribus_setminwordtracking, METH_VARARGS, tr(scribus_setminwordtracking__doc__)},
 	{ "setTextShade", scribus_settextshade, METH_VARARGS, tr(scribus_settextshade__doc__)},
 	{ "setTextStroke", scribus_settextstroke, METH_VARARGS, tr(scribus_settextstroke__doc__)},
 	{ "setTextVerticalAlignment", scribus_settextverticalalignment, METH_VARARGS, tr(scribus_settextverticalalignment__doc__)},
-- 
2.48.1

jghali

2025-02-09 11:59

administrator   ~0052033

Last edited: 2025-02-09 12:06

Thanks. I've applied your patch with some minor corrections.

I've noticed there is an inconsistency between the get*Tracking and set*Tracking methods in the way the tracking value is passed as an argument to the set*Tracking functions and returned by the get*Tracking functions. The set*Tracking function parse the input value as a double, however the get*Tracking functions return the value as int. So I'll change the get*Tracking function so they return a floating point number as well.

Issue History

Date Modified Username Field Change
2025-01-31 07:48 hugowett New Issue
2025-01-31 07:48 hugowett Tag Attached: automation
2025-01-31 07:48 hugowett Tag Attached: scripter
2025-01-31 07:48 hugowett File Added: 0001-scripting-add-functions-for-setting-frame-letter-and.patch
2025-02-04 20:38 jghali Note Added: 0052010
2025-02-04 20:38 jghali Relationship added related to 0017334
2025-02-04 20:41 jghali Note Edited: 0052010
2025-02-05 15:01 hugowett Note Added: 0052011
2025-02-05 21:33 jghali Note Added: 0052015
2025-02-06 15:02 hugowett Note Added: 0052017
2025-02-06 15:02 hugowett File Added: 0001-scripting-add-functions-for-setting-and-getting-min-.patch
2025-02-09 11:51 jghali Summary Add script methods for setting letter and word kerning/tracking on a frame => Add script method for getting and setting min word kerning/tracking on a frame
2025-02-09 11:58 jghali Summary Add script method for getting and setting min word kerning/tracking on a frame => Add script methods for getting and setting min word kerning/tracking on a frame
2025-02-09 11:59 jghali Assigned To => jghali
2025-02-09 11:59 jghali Status new => resolved
2025-02-09 11:59 jghali Resolution open => fixed
2025-02-09 11:59 jghali Fixed in Version => 1.6.4.svn
2025-02-09 11:59 jghali Note Added: 0052033
2025-02-09 12:06 jghali Note Edited: 0052033