Index: resources/iconsets/1_7_0/1_7_0.xml
===================================================================
--- resources/iconsets/1_7_0/1_7_0.xml	(Revision 26436)
+++ resources/iconsets/1_7_0/1_7_0.xml	(Arbeitskopie)
@@ -138,8 +138,10 @@
 		<icon id="u_group.png" file="16/object-group.svg" /><!-- Outliner -->
 		<icon id="u_undo16.png" file="16/action-undo.svg" /><!-- Barcode -->
 		<icon id="ungroup.png" file="16/object-ungroup.svg" />
+		<icon id="add" file="16/action-add.svg" />
 		<icon id="collapse" file="16/action-chevron-right.svg" />
 		<icon id="context-menu" file="16/action-menu.svg" />
+		<icon id="delete" file="16/action-delete.svg" />
 		<icon id="delete-selected" file="16/action-delete-selected.svg" />
 		<icon id="erase-shadow-off" file="16/action-shadow-erase-off.svg" />
 		<icon id="erase-shadow-on" file="16/action-shadow-erase-on.svg" />
@@ -146,15 +148,14 @@
 		<icon id="expand" file="16/action-chevron-down.svg" />
 		<icon id="inherit-opacity-off" file="16/action-inherit-opacity-off.svg" />
 		<icon id="inherit-opacity-on" file="16/action-inherit-opacity-on.svg" />
+		<icon id="remove" file="16/action-remove.svg" />
 		<icon id="reset" file="16/action-reload.svg" />
 		<icon id="reset-style-changes" file="16/action-reload.svg" /> <!-- 16/action-revert-changes.svg -->
 		<icon id="scale-height" file="16/action-scale-height.svg" />
 		<icon id="scale-width" file="16/action-scale-width.svg" />
+		<icon id="settings" file="16/properties-external-tools.svg" />
 		<icon id="swap" file="16/action-swap.svg" />
 		<icon id="toggle-object-coordination" file="16/object-xycoordination.svg" />
-		<icon id="context-menu" file="16/action-menu.svg" />
-		<icon id="scale-width" file="16/action-scale-width.svg" />
-		<icon id="scale-height" file="16/action-scale-height.svg" />
 		<icon id="whitespace-review-mode" file="16/action-whitespace-review.svg" />
 			
 		<!-- Alerts -->
Index: scribus/CMakeLists_Sources.txt
===================================================================
--- scribus/CMakeLists_Sources.txt	(Revision 26436)
+++ scribus/CMakeLists_Sources.txt	(Arbeitskopie)
@@ -114,6 +114,7 @@
 	nfttemplate.cpp
 	nodeeditcontext.cpp
 	notesstyles.cpp
+	opticalmarginlookup.cpp
 	pageitem.cpp
 	pageitem_arc.cpp
 	pageitem_group.cpp
@@ -381,6 +382,7 @@
 	ui/nodeeditpalette.cpp
 	ui/notesstyleseditor.cpp
 	ui/numformatcombo.cpp
+	ui/opticalmargineditor.cpp
 	ui/opticalmarginswidget.cpp
 	ui/outlinepalette.cpp
 	ui/outputpreview_pdf.cpp
Index: scribus/pageitem_textframe.cpp
===================================================================
--- scribus/pageitem_textframe.cpp	(Revision 26436)
+++ scribus/pageitem_textframe.cpp	(Arbeitskopie)
@@ -41,6 +41,7 @@
 #include "marks.h"
 #include "notesstyles.h"
 #include "numeration.h"
+#include "opticalmarginlookup.h"
 #include "pageitem.h"
 #include "pageitem_group.h"
 #include "pageitem_noteframe.h"
@@ -647,11 +648,22 @@
 		lineData.ascent = lineData.descent = 0;
 		if (glyphs.isEmpty())
 			return;
-		const CharStyle& cStyle(glyphs.at(0).style());
-		double scaleV = cStyle.scaleV() / 1000.0;
-		double offset = (cStyle.fontSize() / 10) * (cStyle.baselineOffset() / 1000.0);
-		lineData.ascent = cStyle.font().ascent(cStyle.fontSize() / 10.00) * scaleV + offset;
-		lineData.descent = cStyle.font().descent(cStyle.fontSize() / 10.00) * scaleV - offset;
+
+		// #17286 seek for largest glphy in line
+		for (int i = 0; i < glyphs.count(); i++)
+		{
+			const CharStyle& cStyle(glyphs.at(i).style());
+			double scaleV = cStyle.scaleV() / 1000.0;
+			double offset = (cStyle.fontSize() / 10) * (cStyle.baselineOffset() / 1000.0);
+			double tmpAscent = cStyle.font().ascent(cStyle.fontSize() / 10.00) * scaleV + offset;
+
+			if (lineData.ascent < tmpAscent)
+			{
+				lineData.ascent = tmpAscent;
+				lineData.descent = cStyle.font().descent(cStyle.fontSize() / 10.00) * scaleV;
+			}
+
+		}
 	}
 
 // yPos should not be changed when all line is already calculated - at new y position there can be overflow!!!
@@ -901,44 +913,34 @@
 
 
 	//defined but not used
-	///// calculate how much the first char should stick out to the left
-	//double opticalLeftMargin(const StoryText& itemText)
-	//{
-	//	int b = line.firstChar;
-	//	while (b < line.lastChar && (itemText.flags(b) & ScLayout_SuppressSpace))
-	//		   ++b;
-	//
-	//	double chs = itemText.charStyle(b).fontSize() * (itemText.charStyle(b).scaleH() / 1000.0);
-	//	QChar chr = itemText.text(b);
-	//	double leftCorr = itemText.charStyle(b).font().realCharWidth(chr, chs / 10.0);
-	//	if (QString("'´`").indexOf(chr) >= 0
-	//		|| chr == QChar(0x2018) // quote 6
-	//		|| chr == QChar(0x2019) // quote 9
-	//		|| chr == QChar(0x201a) // lower quote 9
-	//		|| chr == QChar(0x201b) // upper reversed 9 6
-	//		|| chr == QChar(0x2039) // single guillemet <
-	//		|| chr == QChar(0x203a) // single guillemet >
-	//		)
-	//		leftCorr *= -0.7;
-	//	else if (QString("\"").indexOf(chr) >= 0
-	//			 || chr == QChar(0x00ab) // guillemet <<
-	//			 || chr == QChar(0x00bb) // guillemet >>
-	//			 || chr == QChar(0x201c) // quote 66
-	//			 || chr == QChar(0x201d) // quote 99
-	//			 || chr == QChar(0x201e) // lower quote 99
-	//			 || chr == QChar(0x201f) // upper reversed 99
-	//			 )
-	//		leftCorr *= -0.5;
-	//	else {
-	//		leftCorr = itemText.charStyle(b).font().charWidth(QChar(' '), chs / 10.0, chr);
-	//		leftCorr -= itemText.charStyle(b).font().charWidth(QChar(' '), chs / 10.0);
-	////				double leftCorr2 = itemText.charStyle(a).font().charWidth(QChar('K'), chs / 10.0, chr);
-	////				leftCorr2 -= itemText.charStyle(a).font().charWidth(QChar('K'), chs / 10.0);
-	////				leftCorr = qMin(leftCorr, leftCorr2);
-	//	}
-	//	return leftCorr;
-	//}
+	/// calculate how much the first char should stick out to the left
+	double opticalLeftMargin(const StoryText& itemText)
+	{
+		int b = 0;
+		while (b < lineData.lastCluster && (itemText.flags(b) & ScLayout_SuppressSpace))
+			   ++b;
 
+		if (b < lineData.lastCluster)
+		{
+			const CharStyle& style = glyphs[b].style();
+			const ParagraphStyle& pStyle = itemText.paragraphStyle(lineData.lastCluster);
+			const ScFace& font = style.font();
+			double chs = style.fontSize() * (style.scaleH() / 1000.0);
+			QChar chr = itemText.text(glyphs[b].firstChar());
+			double leftCorr = 0;
+
+			OpticalMarginSet set = doc->opticalMarginSets().value(pStyle.opticalMarginSetId());
+			OpticalMarginRule rule = OpticalMarginLookup::instance().offset(chr, set);
+			if ( rule.Unit == SC_PERCENT)
+				leftCorr = font.glyphBBox(font.char2CMap(chr.unicode()), chs / 10.0).width * rule.Left;
+			else
+				leftCorr = rule.Left;
+
+			return leftCorr;
+		}
+		return 0.0;
+	}
+
 	/// calculate how much the last char should stick out to the right
 	double opticalRightMargin(const StoryText& itemText)
 	{
@@ -951,46 +953,20 @@
 		if (b >= 0)
 		{
 			const CharStyle& style = glyphs[b].style();
+			const ParagraphStyle& pStyle = itemText.paragraphStyle(lineData.lastCluster);
 			const ScFace& font = style.font();
 			double chs = style.fontSize() * (style.scaleH() / 1000.0);
-			QChar chr = itemText.text(glyphs[b].lastChar());
-			double rightCorr;
-			if (glyphs[b].hasFlag(ScLayout_SoftHyphenVisible))
-				rightCorr = font.hyphenWidth(style, chs / 10.0);
+			bool softHypen = glyphs[b].hasFlag(ScLayout_SoftHyphenVisible);
+			QChar chr = softHypen ? QChar(style.hyphenChar()) : itemText.text(glyphs[b].lastChar());
+			double rightCorr = 0;
+
+			OpticalMarginSet set = doc->opticalMarginSets().value(pStyle.opticalMarginSetId());
+			OpticalMarginRule rule = OpticalMarginLookup::instance().offset(chr, set);
+			if ( rule.Unit == SC_PERCENT)
+				rightCorr = softHypen ? font.hyphenWidth(style, chs / 10.0) * rule.Right : font.glyphBBox(font.char2CMap(chr.unicode()), chs / 10.0).width * rule.Right;
 			else
-				rightCorr = font.glyphBBox(font.char2CMap(chr.unicode()), chs / 10.0).width;
-			if (glyphs[b].hasFlag(ScLayout_SoftHyphenVisible)
-				|| QString("-,.`´'~").indexOf(chr) >= 0
-				|| chr == QChar(0x2010)
-				|| chr == QChar(0x2018)
-				|| chr == QChar(0x2019)
-				|| chr == QChar(0x201a)
-				|| chr == QChar(0x201b)
-				|| chr == QChar(0x2039)
-				|| chr == QChar(0x203a)
-				|| chr == QChar(0x2032) // PRIME
-				)
-				rightCorr *= 0.7;
-			else if (QString(";:\"").indexOf(chr) >= 0
-					 || chr == QChar(0x00ab)
-					 || chr == QChar(0x00bb)
-					 || chr == QChar(0x201c)
-					 || chr == QChar(0x201d)
-					 || chr == QChar(0x201e)
-					 || chr == QChar(0x201f)
-					 || chr == QChar(0x2013) // EN DASH
-					 || chr == QChar(0x2033) // double prime
-					 )
-				rightCorr *= 0.5;
-			else {
-#if 0
-				// FIXME HOST: is the kerning with "." a really reliable way to check this?
-				rightCorr = chStyle.font().realCharWidth(chr, chs / 10.0);
-				rightCorr -= chStyle.font().charWidth(chr, chs / 10.0, QChar('.'));
-#else
-				rightCorr = 0;
-#endif
-			}
+				rightCorr = rule.Right;
+
 			return rightCorr;
 		}
 		return 0.0;
@@ -2438,6 +2414,11 @@
 					{
 						if (opticalMargins & ParagraphStyle::OM_RightHangingPunct)
 							current.lineData.width += current.opticalRightMargin(itemText);
+						if (opticalMargins & ParagraphStyle::OM_LeftHangingPunct)
+						{
+							current.lineData.width += current.opticalLeftMargin(itemText);
+							current.lineData.x -= current.opticalLeftMargin(itemText);
+						}
 
 						OFs = 0;
 						if (style.alignment() == ParagraphStyle::RightAligned)
@@ -2467,6 +2448,9 @@
 						{
 							if (opticalMargins & ParagraphStyle::OM_RightHangingPunct)
 								current.lineData.naturalWidth += current.opticalRightMargin(itemText);
+							if (opticalMargins & ParagraphStyle::OM_LeftHangingPunct)
+								current.lineData.naturalWidth += current.opticalLeftMargin(itemText);
+
 							double optiWidth = current.colRight - style.rightMargin() - style.lineSpacing() / 2.0 - current.lineData.x;
 							if (current.lineData.naturalWidth > optiWidth)
 								current.lineData.width = qMax(current.lineData.width - current.maxShrink, optiWidth);
@@ -2553,6 +2537,11 @@
 						// Justification
 						if (opticalMargins & ParagraphStyle::OM_RightHangingPunct)
 							current.lineData.width += current.opticalRightMargin(itemText);
+						if (opticalMargins & ParagraphStyle::OM_LeftHangingPunct)
+						{
+							current.lineData.width += current.opticalLeftMargin(itemText);
+							current.lineData.x -= current.opticalLeftMargin(itemText);
+						}
 						// #12565: Right alignment of hyphens
 						// The additional character width has already been taken into account
 						// above via the line break position, so it's not necessary to increase
@@ -2579,6 +2568,9 @@
 						{
 							if (opticalMargins & ParagraphStyle::OM_RightHangingPunct)
 								current.lineData.naturalWidth += current.opticalRightMargin(itemText);
+							if (opticalMargins & ParagraphStyle::OM_LeftHangingPunct)
+								current.lineData.naturalWidth += current.opticalLeftMargin(itemText);
+
 							current.indentLine(style, OFs);
 						}
 						current.xPos = current.lineData.x + current.lineData.width;
@@ -2841,6 +2833,11 @@
 
 			if (opticalMargins & ParagraphStyle::OM_RightHangingPunct)
 				current.lineData.width += current.opticalRightMargin(itemText);
+			if (opticalMargins & ParagraphStyle::OM_LeftHangingPunct)
+			{
+				current.lineData.width += current.opticalLeftMargin(itemText);
+				current.lineData.x -= current.opticalLeftMargin(itemText);
+			}
 
 			OFs = 0;
 			if (style.alignment() == ParagraphStyle::RightAligned)
@@ -2869,6 +2866,9 @@
 			{
 				if (opticalMargins & ParagraphStyle::OM_RightHangingPunct)
 					current.lineData.naturalWidth += current.opticalRightMargin(itemText);
+				if (opticalMargins & ParagraphStyle::OM_LeftHangingPunct)
+					current.lineData.naturalWidth -= current.opticalLeftMargin(itemText);
+
 				current.indentLine(style, OFs);
 			}
 			if (current.glyphs[0].hasFlag(ScLayout_DropCap))
Index: scribus/plugins/fileloader/scribus170format/scribus170format.cpp
===================================================================
--- scribus/plugins/fileloader/scribus170format/scribus170format.cpp	(Revision 26436)
+++ scribus/plugins/fileloader/scribus170format/scribus170format.cpp	(Arbeitskopie)
@@ -1974,6 +1974,11 @@
 			if (!success) break;
 			m_Doc->setUsesMarksAndNotes(true);
 		}
+		else if (tagName == QLatin1String("OpticalMarginSets"))
+		{
+			success = readOpticalMarginSets(m_Doc, reader);
+			if (!success) break;
+		}
 		else
 		{
 			reader.skipCurrentElement();
@@ -3166,6 +3171,10 @@
 	if (attrs.hasAttribute(OpticalMargins))
 		newStyle.setOpticalMargins(attrs.valueAsInt(OpticalMargins));
 
+	static const QString OpticalMarginSetId("OpticalMarginSetId");
+	if (attrs.hasAttribute(OpticalMarginSetId))
+		newStyle.setOpticalMarginSetId(attrs.valueAsString(OpticalMarginSetId));
+
 	static const QString HyphenConsecutiveLines("HyphenConsecutiveLines");
 	if (attrs.hasAttribute(HyphenConsecutiveLines))
 		newStyle.setHyphenConsecutiveLines(attrs.valueAsInt(HyphenConsecutiveLines));
@@ -3974,6 +3983,52 @@
 	return !reader.hasError();
 }
 
+bool Scribus170Format::readOpticalMarginSets(ScribusDoc *doc, ScXmlStreamReader &reader) const
+{
+	QString tagName(reader.nameAsString());
+	OpticalMarginSets sets;
+	OpticalMarginSet set;
+	QMap<QString, QString> names;
+	OpticalMarginRules rules;
+
+	while (!reader.atEnd() && !reader.hasError())
+	{
+		reader.readNext();
+		if (reader.isEndElement() && reader.name() == tagName)
+			break;
+
+		if (reader.isStartElement() && reader.name() == QLatin1String("Set"))
+		{
+			ScXmlStreamAttributes attrs = reader.scAttributes();
+			set.id = attrs.valueAsString("Id");
+			set.type = attrs.valueAsString("Type");
+			set.name = attrs.valueAsString("Name");
+		}
+
+		if (reader.isStartElement() && reader.name() == QLatin1String("Rule"))
+		{
+			ScXmlStreamAttributes attrs = reader.scAttributes();
+			OpticalMarginRule rule(unicodeToString(attrs.valueAsString("Characters")), attrs.valueAsDouble("Left"), attrs.valueAsDouble("Right"), attrs.valueAsInt("Unit"));
+			rules.append(rule);
+		}
+
+		if (reader.isEndElement() && reader.name() == QLatin1String("Rules"))
+		{
+			set.rules = OpticalMarginLookup::instance().splitRulesToChars(rules);
+			rules.clear();
+		}
+
+		if (reader.isEndElement() && reader.name() == QLatin1String("Set"))
+			sets.insert(set.id, set);
+
+	}
+
+	doc->typographicPrefs().opticalMarginSets = sets;
+	m_Doc->setOpticalMarginSets(sets);
+
+	return !reader.hasError();
+}
+
 bool Scribus170Format::readSections(ScribusDoc* doc, ScXmlStreamReader& reader) const
 {
 	QString tagName(reader.nameAsString());
@@ -5553,6 +5608,8 @@
 		pstyle.setMaxGlyphExtension(attrs.valueAsDouble("MaxGlyphExtend"));
 	if (attrs.hasAttribute("OpticalMargins"))
 		pstyle.setOpticalMargins(attrs.valueAsInt("OpticalMargins"));
+	if (attrs.hasAttribute("OpticalMarginSetId"))
+		pstyle.setOpticalMarginSetId(attrs.valueAsString("OpticalMarginSetId"));
 	if (attrs.hasAttribute("HyphenationMode"))
 		pstyle.setHyphenationMode(attrs.valueAsInt("HyphenationMode"));
 	if (attrs.hasAttribute("leftMargin"))
Index: scribus/plugins/fileloader/scribus170format/scribus170format.h
===================================================================
--- scribus/plugins/fileloader/scribus170format/scribus170format.h	(Revision 26436)
+++ scribus/plugins/fileloader/scribus170format/scribus170format.h	(Arbeitskopie)
@@ -162,6 +162,7 @@
 		bool readNotesStyles(ScribusDoc* doc, ScXmlStreamReader& reader) const;
 		bool readNotesFrames(ScXmlStreamReader &reader);
 		bool readMarks(ScribusDoc* doc, ScXmlStreamReader& reader);
+		bool readOpticalMarginSets(ScribusDoc* doc, ScXmlStreamReader& reader) const;
 
 		//lists of items and marks with names only, which need update to pointers
 		QMap<Mark*, int> markeredItemsMap;
@@ -213,6 +214,7 @@
 		void writeIndexes(ScXmlStreamWriter& docu);
 		void writeTOC(ScXmlStreamWriter& docu);
 		void writeMarks(ScXmlStreamWriter & docu);
+		void writeOpticalMarginSets(ScXmlStreamWriter & docu);
 		void writeNotesStyles(ScXmlStreamWriter & docu);
 		void writeNotesStyles(ScXmlStreamWriter & docu, const QStringList& styleSelection);
 		void writeNotesFrames(ScXmlStreamWriter & docu);
Index: scribus/plugins/fileloader/scribus170format/scribus170format_save.cpp
===================================================================
--- scribus/plugins/fileloader/scribus170format/scribus170format_save.cpp	(Revision 26436)
+++ scribus/plugins/fileloader/scribus170format/scribus170format_save.cpp	(Arbeitskopie)
@@ -45,8 +45,8 @@
 #include "ui/missing.h"
 #include "units.h"
 #include "util.h"
-
 #include "util_color.h"
+#include "util_text.h"
 
 QString Scribus170Format::saveElements(double xp, double yp, double wp, double hp, Selection* selection, QByteArray &prevData)
 {
@@ -540,6 +540,7 @@
 	writeTOC(docu);
 	writeMarks(docu);
 	writeNotesStyles(docu);
+	writeOpticalMarginSets(docu);
 	writeNotesFrames(docu);
 	writeNotes(docu);
 	writePageSets(docu);
@@ -882,6 +883,8 @@
 		docu.writeAttribute("NumerationHigher", static_cast<int>(style.numHigher()));
 	if ( ! style.isInhOpticalMargins())
 		docu.writeAttribute("OpticalMargins", style.opticalMargins());
+	if ( ! style.isInhOpticalMarginSetId())
+		docu.writeAttribute("OpticalMarginSetId", style.opticalMarginSetId());
 	if ( ! style.isInhHyphenConsecutiveLines())
 		docu.writeAttribute("HyphenConsecutiveLines", style.hyphenConsecutiveLines());
 	if ( ! style.isInhHyphenationMode())
@@ -1531,6 +1534,40 @@
 	docu.writeEndElement();
 }
 
+void Scribus170Format::writeOpticalMarginSets(ScXmlStreamWriter & docu)
+{
+	docu.writeStartElement("OpticalMarginSets");
+
+	OpticalMarginSets sets = m_Doc->typographicPrefs().opticalMarginSets;
+
+	for (const auto &set : sets)
+	{
+		docu.writeStartElement("Set");
+		docu.writeAttribute("Id", set.id);
+		docu.writeAttribute("Type", set.type);
+		docu.writeAttribute("Name", set.name);
+
+		docu.writeStartElement("Rules");
+
+		OpticalMarginRules rules = OpticalMarginLookup::instance().mergeCharsToRules(set.rules);
+
+		for (const auto &rule : rules)
+		{
+			docu.writeEmptyElement("Rule");
+			docu.writeAttribute("Left", rule.Left);
+			docu.writeAttribute("Right", rule.Right);
+			docu.writeAttribute("Unit", rule.Unit);
+			docu.writeAttribute("Characters", stringToUnicode(rule.Chars));
+		}
+
+		docu.writeEndElement(); // Rules
+		docu.writeEndElement(); // Set
+
+	}
+
+	docu.writeEndElement(); // OpticalMarginSets
+}
+
 void Scribus170Format::writeNotesStyles(ScXmlStreamWriter & docu)
 {
 	if (m_Doc->m_docNotesStylesList.isEmpty())
Index: scribus/prefsmanager.cpp
===================================================================
--- scribus/prefsmanager.cpp	(Revision 26436)
+++ scribus/prefsmanager.cpp	(Arbeitskopie)
@@ -60,6 +60,7 @@
 #include "util_color.h"
 #include "util_file.h"
 #include "util_ghostscript.h"
+#include "util_text.h"
 
 #include "ui/missing.h"
 #include "ui/modetoolbar.h"
@@ -224,6 +225,7 @@
 	appPrefs.typoPrefs.valueStrikeThruPos = -1;
 	appPrefs.typoPrefs.valueStrikeThruWidth = -1;
 	appPrefs.guidesPrefs.valueBaselineGrid = 14.4;
+	appPrefs.typoPrefs.opticalMarginSets = OpticalMarginLookup::instance().defaultSets();
 	appPrefs.guidesPrefs.offsetBaselineGrid = 0.0;
 	appPrefs.displayPrefs.showToolTips = true;
 	appPrefs.displayPrefs.showMouseCoordinates = true;
@@ -1920,6 +1922,33 @@
 	experimentalElem.setAttribute("NotesEnabled", appPrefs.experimentalFeaturePrefs.notesEnabled);
 	elem.appendChild(experimentalElem);
 
+	QDomElement opticalMarginSets = docu.createElement("OpticalMarginSets");
+	for (const auto &set : appPrefs.typoPrefs.opticalMarginSets)
+	{
+		QDomElement setItem = docu.createElement("Set");
+		setItem.setAttribute("Id", set.id);
+		setItem.setAttribute("Type", set.type);
+		setItem.setAttribute("Name", set.name);
+
+		QDomElement rulesItem = docu.createElement("Rules");
+		setItem.appendChild(rulesItem);
+
+		OpticalMarginRules rules = OpticalMarginLookup::instance().mergeCharsToRules(set.rules);
+
+		for (auto rule : rules)
+		{
+			QDomElement ruleItem = docu.createElement("Rule");
+			ruleItem.setAttribute("Left", rule.Left);
+			ruleItem.setAttribute("Right", rule.Right);
+			ruleItem.setAttribute("Unit", rule.Unit);
+			ruleItem.setAttribute("Characters", stringToUnicode(rule.Chars));
+			rulesItem.appendChild(ruleItem);
+		}
+
+		opticalMarginSets.appendChild(setItem);
+	}
+	elem.appendChild(opticalMarginSets);
+
 	// write file
 	bool result = false;
 	QFile f(filePath);
@@ -2768,6 +2797,66 @@
 			appPrefs.experimentalFeaturePrefs.notesEnabled = static_cast<bool>(dc.attribute("NotesEnabled", "0").toInt());
 		}
 
+		// optical margin groups
+		if (dc.tagName() == "OpticalMarginSets")
+		{
+			OpticalMarginSets sets;
+			OpticalMarginSet set;
+
+			QDomNodeList setNodes = dc.elementsByTagName("Set");
+
+			for(int i = 0; i < setNodes.count(); i++)
+			{
+				QDomNode setNode = setNodes.at(i);
+				if(setNode.isElement())
+				{
+					QDomElement setElement = setNode.toElement();
+					set.id = setElement.attribute("Id");
+					set.type = setElement.attribute("Type");
+					set.name = setElement.attribute("Name");
+
+					QDomNodeList childrenList = setElement.childNodes();
+
+					for(int i = 0; i < childrenList.count(); i++)
+					{
+						QDomNode child = childrenList.at(i);
+						if(child.isElement())
+						{
+							QDomElement childElement = child.toElement();
+
+							if (childElement.tagName() == "Rules")
+							{
+								OpticalMarginRules rules;
+
+								QDomNodeList ruleList = childElement.elementsByTagName("Rule");
+								for(int i = 0; i < ruleList.count(); i++)
+								{
+									QDomNode rule = ruleList.at(i);
+									if(rule.isElement())
+									{
+										QDomElement ruleElement = rule.toElement();
+										OpticalMarginRule rule(unicodeToString(ruleElement.attribute("Characters")),
+																 ruleElement.attribute("Left").toDouble(),
+																 ruleElement.attribute("Right").toDouble(),
+																 ruleElement.attribute("Unit").toInt()
+																 );
+
+										rules.append(rule);
+									}
+								}
+
+								set.rules = OpticalMarginLookup::instance().splitRulesToChars(rules);
+							}
+						}
+					}
+
+					sets.insert(set.id, set);
+				}
+			}
+
+			appPrefs.typoPrefs.opticalMarginSets = sets;
+		}
+
 		domNode = domNode.nextSibling();
 	}
 	// Some sanity checks
Index: scribus/prefsstructs.h
===================================================================
--- scribus/prefsstructs.h	(Revision 26436)
+++ scribus/prefsstructs.h	(Arbeitskopie)
@@ -20,6 +20,7 @@
 #include <QHash>
 
 #include "documentinformation.h"
+#include "opticalmarginlookup.h"
 #include "pagestructs.h"
 #include "pdfoptions.h"
 #include "scfonts.h"
@@ -80,6 +81,7 @@
 	int valueUnderlineWidth; //! Underline width
 	int valueStrikeThruPos; //! Strike-through displacement
 	int valueStrikeThruWidth; //! Strike-through line width
+	OpticalMarginSets opticalMarginSets; //! OpticalMargin Group List
 
 	inline bool operator==(const TypoPrefs &other) const
 	{
Index: scribus/scribusdoc.cpp
===================================================================
--- scribus/scribusdoc.cpp	(Revision 26436)
+++ scribus/scribusdoc.cpp	(Arbeitskopie)
@@ -9046,10 +9046,11 @@
 	itemSelection_ApplyCharStyle(newStyle, customSelection, "EFFECTS");
 }
 
-void ScribusDoc::itemSelection_SetOpticalMargins(int i, Selection* customSelection)
+void ScribusDoc::itemSelection_SetOpticalMargins(int i, const QString id, Selection* customSelection)
 {
 	ParagraphStyle newStyle;
-	newStyle.setOpticalMargins(i);
+	newStyle.setOpticalMarginSetId(id);
+	newStyle.setOpticalMargins(i);	
 	itemSelection_ApplyParagraphStyle(newStyle, customSelection);
 }
 
Index: scribus/scribusdoc.h
===================================================================
--- scribus/scribusdoc.h	(Revision 26436)
+++ scribus/scribusdoc.h	(Arbeitskopie)
@@ -49,6 +49,7 @@
 #include "nodeeditcontext.h"
 #include "notesstyles.h"
 #include "observable.h"
+#include "opticalmarginlookup.h"
 #include "pageitem.h"
 #include "pageitem_group.h"
 #include "pageitem_latexframe.h"
@@ -226,6 +227,9 @@
 	int marginPreset() const { return m_docPrefsData.docSetupPrefs.marginPreset; }
 	void setMarginPreset(int mp) { m_docPrefsData.docSetupPrefs.marginPreset = mp; }
 
+	OpticalMarginSets opticalMarginSets() { return m_docPrefsData.typoPrefs.opticalMarginSets; }
+	void setOpticalMarginSets(OpticalMarginSets sets) { m_docPrefsData.typoPrefs.opticalMarginSets = sets; }
+
 	TypoPrefs& typographicPrefs() { return m_docPrefsData.typoPrefs; }
 	GuidesPrefs& guidesPrefs() { return m_docPrefsData.guidesPrefs; }
 	ItemToolPrefs& itemToolPrefs() { return m_docPrefsData.itemToolPrefs; }
@@ -1156,7 +1160,7 @@
 	void itemSelection_SetUnderline(int pos, int wid, Selection* customSelection = nullptr);
 	void itemSelection_SetStrikethru(int pos, int wid, Selection* customSelection = nullptr);
 	void itemSelection_SetEffects(int s, Selection* customSelection = nullptr);
-	void itemSelection_SetOpticalMargins(int i, Selection* customSelection = nullptr);
+	void itemSelection_SetOpticalMargins(int i, const QString id, Selection* customSelection = nullptr);
 	void itemSelection_SetColorProfile(const QString& profileName, Selection* customSelection = nullptr);
 	void itemSelection_SetRenderIntent(int intentIndex, Selection* customSelection = nullptr);
 	void itemSelection_SetCompressionMethod(int cmIndex, Selection* customSelection = nullptr);
Index: scribus/styles/paragraphstyle.attrdefs.cxx
===================================================================
--- scribus/styles/paragraphstyle.attrdefs.cxx	(Revision 26436)
+++ scribus/styles/paragraphstyle.attrdefs.cxx	(Arbeitskopie)
@@ -57,3 +57,4 @@
 ATTRDEF(QString, backgroundColor, BackgroundColor, "None")
 ATTRDEF(double, backgroundShade, BackgroundShade, 100)
 ATTRDEF(int, hyphenConsecutiveLines, HyphenConsecutiveLines, 2)
+ATTRDEF(QString, opticalMarginSetId, OpticalMarginSetId, OpticalMarginLookup::instance().defaultSetId())
Index: scribus/styles/paragraphstyle.h
===================================================================
--- scribus/styles/paragraphstyle.h	(Revision 26436)
+++ scribus/styles/paragraphstyle.h	(Arbeitskopie)
@@ -18,6 +18,7 @@
 
 #include "style.h"
 #include "charstyle.h"
+#include "opticalmarginlookup.h"
 #include "styles/stylecontextproxy.h"
 
 
Index: scribus/ui/opticalmarginswidget.cpp
===================================================================
--- scribus/ui/opticalmarginswidget.cpp	(Revision 26436)
+++ scribus/ui/opticalmarginswidget.cpp	(Arbeitskopie)
@@ -22,6 +22,8 @@
 	parentButton = new QToolButton();
 	parentButton->hide(); // only used for style manager
 
+	setsCombo = new QComboBox();
+
 	QHBoxLayout* mainLayout = new QHBoxLayout();
 	mainLayout->setSpacing(4);
 	mainLayout->setContentsMargins(0, 0, 0, 0);
@@ -29,6 +31,7 @@
 	mainLayout->addWidget(leftButton);
 	mainLayout->addWidget(rightButton);
 	mainLayout->addSpacerItem(new QSpacerItem(4, 0, QSizePolicy::Fixed));
+	mainLayout->addWidget(setsCombo);
 	mainLayout->addWidget(parentButton);
 	mainLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding));
 
@@ -40,6 +43,7 @@
 	connect(ScQApp, SIGNAL(iconSetChanged()), this, SLOT(iconSetChange()));
 	connect(rightButton, SIGNAL(clicked()), this, SIGNAL(opticalMarginChanged()));
 	connect(leftButton, SIGNAL(clicked()), this, SIGNAL(opticalMarginChanged()));
+	connect(setsCombo, SIGNAL(currentIndexChanged(int)), this, SIGNAL(opticalMarginChanged()));
 }
 
 int OpticalMarginsWidget::opticalMargin()
@@ -86,6 +90,27 @@
 	blockSignals(blocked);
 }
 
+void OpticalMarginsWidget::setOpticalMarginSets(OpticalMarginSets sets)
+{
+	m_sets = sets;
+	setOpticalMarginSetId(opticalMarginSetId());
+}
+
+void OpticalMarginsWidget::setOpticalMarginSetId(const QString id)
+{	
+	if (m_sets.size() <= 0)
+		return;
+
+	m_setId = m_sets.contains(id) ? id : m_sets.firstKey();
+
+	updateSetSelector();
+}
+
+const QString OpticalMarginsWidget::opticalMarginSetId()
+{
+	return setsCombo->currentData().toString();
+}
+
 void OpticalMarginsWidget::iconSetChange()
 {
 	IconManager &im = IconManager::instance();
@@ -103,9 +128,25 @@
 	QWidget::changeEvent(e);
 }
 
+void OpticalMarginsWidget::updateSetSelector()
+{
+	if (m_sets.size() <= 0)
+		return;
+
+	QSignalBlocker blockSets(setsCombo);
+	setsCombo->setVisible( m_sets.size() > 1 ? true : false );
+	setsCombo->clear();
+
+	for (auto [id, set] : m_sets.asKeyValueRange())
+		setsCombo->addItem(set.name, id);
+
+	int index = setsCombo->findData(m_setId);
+	setsCombo->setCurrentIndex(index);
+}
+
 void OpticalMarginsWidget::languageChange()
 {
-	setText(tr("Optical &Margins"));
+	setText( tr("Optical &Margins") );
 
 	leftButton->setToolTip( tr("Optical character offset on left side"));
 	rightButton->setToolTip( tr("Optical character offset on right side"));
Index: scribus/ui/opticalmarginswidget.h
===================================================================
--- scribus/ui/opticalmarginswidget.h	(Revision 26436)
+++ scribus/ui/opticalmarginswidget.h	(Arbeitskopie)
@@ -7,7 +7,9 @@
 #ifndef OPTICALMARGINSWIDGET_H
 #define OPTICALMARGINSWIDGET_H
 
+#include <QComboBox>
 #include <QToolButton>
+#include "opticalmarginlookup.h"
 #include "ui/widgets/form_widget.h"
 
 class ParagraphStyle;
@@ -25,13 +27,23 @@
 	QToolButton *leftButton = {nullptr};
 	QToolButton *rightButton = {nullptr};
 	QToolButton *parentButton = {nullptr};
+	QComboBox *setsCombo = {nullptr};
 
 	int opticalMargin();
 	void setOpticalMargin(int omt);
 
+	void setOpticalMarginSets(OpticalMarginSets sets);
+	OpticalMarginSets opticalMarginSets() { return m_sets; };
+
+	void setOpticalMarginSetId(const QString id);
+	const QString opticalMarginSetId();
+
 protected:
 
 	void changeEvent(QEvent *e) override;
+	void updateSetSelector();
+	OpticalMarginSets m_sets;
+	QString m_setId;
 
 public slots:
 
Index: scribus/ui/prefs_typography.cpp
===================================================================
--- scribus/ui/prefs_typography.cpp	(Revision 26436)
+++ scribus/ui/prefs_typography.cpp	(Arbeitskopie)
@@ -8,9 +8,13 @@
 #include "prefs_typography.h"
 #include "prefsstructs.h"
 #include "scribusdoc.h"
+#include "styles/styleset.h"
+#include "ui/smtextstyles.h"
+#include "scribus.h"
 
-Prefs_Typography::Prefs_Typography(QWidget* parent, ScribusDoc* /*doc*/)
-	: Prefs_Pane(parent)
+Prefs_Typography::Prefs_Typography(QWidget* parent, ScribusDoc* doc)
+	: Prefs_Pane(parent),
+	  m_Doc(doc)
 {
 	setupUi(this);
 	languageChange();
@@ -17,6 +21,8 @@
 
 	m_caption = tr("Typography");
 	m_icon = "16/draw-text.png";
+
+	connect(opticalMarginsEditor, &OpticalMarginEditor::setDeleted, this, &Prefs_Typography::updateParagraphStyles);
 }
 
 Prefs_Typography::~Prefs_Typography() = default;
@@ -41,6 +47,51 @@
 	automaticLineSpacingSpinBox->setToolTip( tr( "Percentage increase over the font size for the line spacing" ) );
 }
 
+void Prefs_Typography::updateParagraphStyles(QString removedID, QString newID)
+{
+	if (!m_Doc)
+		return;
+
+	StyleSet<ParagraphStyle> m_tmpStyles;
+	m_tmpStyles.redefine(m_Doc->paragraphStyles(), true);
+
+	// Update all paragraph styles
+	for (int i = 0; i < m_tmpStyles.count(); ++i)
+	{
+		ParagraphStyle &parStyle = m_tmpStyles[i];
+		if (parStyle.opticalMarginSetId() == removedID)
+			parStyle.setOpticalMarginSetId(newID);
+	}
+
+	m_Doc->redefineStyles(m_tmpStyles, false);
+	m_Doc->scMW()->requestUpdate(reqTextStylesUpdate);
+
+	// Check for local paragraph changes
+	for (auto item : *m_Doc->Items)
+	{
+		if (!item->isTextFrame())
+			continue;
+
+		int par = 0;
+		PageItem_TextFrame *tf = item->asTextFrame();
+
+		while (par < tf->itemText.length())
+		{
+			int nextPar = tf->itemText.nextParagraph(par);
+			ParagraphStyle ps = tf->itemText.paragraphStyle(nextPar);
+
+			if (ps.opticalMarginSetId() == removedID)
+			{
+				ps.setOpticalMarginSetId(newID);
+				tf->itemText.setStyle(nextPar, ps);
+			}
+			par = nextPar;
+		}
+
+	}
+
+}
+
 void Prefs_Typography::restoreDefaults(struct ApplicationPrefs *prefsData)
 {
 	subscriptDisplacementSpinBox->setValue(prefsData->typoPrefs.valueSubScript);
@@ -53,6 +104,7 @@
 	strikeoutLineWidthSpinBox->setValue(prefsData->typoPrefs.valueStrikeThruWidth / 10.0);
 	smallcapsScalingSpinBox->setValue(prefsData->typoPrefs.valueSmallCaps);
 	automaticLineSpacingSpinBox->setValue(prefsData->typoPrefs.autoLineSpacing);
+	opticalMarginsEditor->setOpticalMarginSets(prefsData->typoPrefs.opticalMarginSets);
 }
 
 void Prefs_Typography::saveGuiToPrefs(struct ApplicationPrefs *prefsData) const
@@ -67,5 +119,6 @@
 	prefsData->typoPrefs.valueStrikeThruWidth = strikeoutLineWidthSpinBox->value() * 10.0;
 	prefsData->typoPrefs.valueSmallCaps = smallcapsScalingSpinBox->value();
 	prefsData->typoPrefs.autoLineSpacing = automaticLineSpacingSpinBox->value();
+	prefsData->typoPrefs.opticalMarginSets = opticalMarginsEditor->opticalMarginSets();
 }
 
Index: scribus/ui/prefs_typography.h
===================================================================
--- scribus/ui/prefs_typography.h	(Revision 26436)
+++ scribus/ui/prefs_typography.h	(Arbeitskopie)
@@ -25,8 +25,15 @@
 		void restoreDefaults(struct ApplicationPrefs *prefsData) override;
 		void saveGuiToPrefs(struct ApplicationPrefs *prefsData) const override;
 
+	protected:
+		ScribusDoc* m_Doc {nullptr};
+
 	public slots:
 		void languageChange();
+
+	private slots:
+		void updateParagraphStyles(QString removedID, QString newID);
+
 };
 
 #endif // PREFS_TYPOGRAPHY_H
Index: scribus/ui/propertywidget_alignment.cpp
===================================================================
--- scribus/ui/propertywidget_alignment.cpp	(Revision 26436)
+++ scribus/ui/propertywidget_alignment.cpp	(Arbeitskopie)
@@ -44,7 +44,7 @@
 
 	m_haveItem = tmp;
 
-	showOpticalMargins(newCurrent.opticalMargins());
+	showOpticalMargins(newCurrent.opticalMargins(), newCurrent.opticalMarginSetId());
 }
 
 void PropertyWidget_Alignment::setMainWindow(ScribusMainWindow* mw)
@@ -150,7 +150,6 @@
 	connect(tabsButton    , SIGNAL(clicked())           , this, SLOT(handleTabs()), Qt::UniqueConnection);
 	connect(flopWidget->flopGroup, SIGNAL(idClicked(int)), this, SLOT(handleFirstLinePolicy(int)));
 	connect(opticalMarginsWidget, SIGNAL(opticalMarginChanged()), this, SLOT(handleOpticalMargins()) );
-//	connect(opticalMarginsWidget->resetButton, SIGNAL(clicked()), this, SLOT(resetOpticalMargins()) );
 	connect(textAlignment , SIGNAL(State(int))   , this, SLOT(handleAlignment(int)));
 	connect(textVerticalAlignment, SIGNAL(State(int)), this, SLOT(handleVAlignment(int)));
 	connect(textDirection , SIGNAL(State(int))   , this, SLOT(handleDirection(int)));
@@ -161,7 +160,6 @@
 	disconnect(tabsButton    , SIGNAL(clicked())           , this, SLOT(handleTabs()));
 	disconnect(flopWidget->flopGroup, SIGNAL(idClicked(int)), this, SLOT(handleFirstLinePolicy(int)));
 	disconnect(opticalMarginsWidget, SIGNAL(opticalMarginChanged()), this, SLOT(handleOpticalMargins()) );
-//	disconnect(opticalMarginsWidget->resetButton, SIGNAL(clicked()), this, SLOT(resetOpticalMargins()) );
 	disconnect(textAlignment , SIGNAL(State(int))   , this, SLOT(handleAlignment(int)));
 	disconnect(textVerticalAlignment, SIGNAL(State(int)), this, SLOT(handleVAlignment(int)));
 	disconnect(textDirection , SIGNAL(State(int))   , this, SLOT(handleDirection(int)));
@@ -254,7 +252,7 @@
 		flopWidget->flopBaselineGrid->setChecked(true);
 }
 
-void PropertyWidget_Alignment::showOpticalMargins(int e)
+void PropertyWidget_Alignment::showOpticalMargins(int e, const QString &setId)
 {
 	if (!m_ScMW || m_ScMW->scriptIsRunning())
 		return;
@@ -261,6 +259,8 @@
 	bool tmp = m_haveItem;
 	m_haveItem = false;
 	opticalMarginsWidget->setOpticalMargin(e);
+	opticalMarginsWidget->setOpticalMarginSets(m_doc->opticalMarginSets());
+	opticalMarginsWidget->setOpticalMarginSetId(setId);
 	m_haveItem = tmp;
 }
 
@@ -389,8 +389,6 @@
 	if (!m_doc || !m_item || !m_ScMW || m_ScMW->scriptIsRunning())
 		return;
 
-	int omt(opticalMarginsWidget->opticalMargin());
-
 	PageItem *item = m_item;
 	if (m_doc->appMode == modeEditTable)
 		item = m_item->asTable()->activeCell().textFrame();
@@ -398,25 +396,10 @@
 	{
 		Selection tempSelection(this, false);
 		tempSelection.addItem(item, true);
-		m_doc->itemSelection_SetOpticalMargins(omt, &tempSelection);
+		m_doc->itemSelection_SetOpticalMargins(opticalMarginsWidget->opticalMargin(), opticalMarginsWidget->opticalMarginSetId(), &tempSelection);
 	}
 }
 
-// void PropertyWidget_Alignment::resetOpticalMargins()
-// {
-// 	// if (!m_doc || !m_item || !m_ScMW || m_ScMW->scriptIsRunning())
-// 	// 	return;
-// 	// PageItem *item = m_item;
-// 	// if (m_doc->appMode == modeEditTable)
-// 	// 	item = m_item->asTable()->activeCell().textFrame();
-// 	// if (item != nullptr)
-// 	// {
-// 	// 	Selection tempSelection(this, false);
-// 	// 	tempSelection.addItem(item, true);
-// 	// 	m_doc->itemSelection_resetOpticalMargins(&tempSelection);
-// 	// }
-// }
-
 void PropertyWidget_Alignment::changeEvent(QEvent *e)
 {
 	if (e->type() == QEvent::LanguageChange)
Index: scribus/ui/propertywidget_alignment.h
===================================================================
--- scribus/ui/propertywidget_alignment.h	(Revision 26436)
+++ scribus/ui/propertywidget_alignment.h	(Arbeitskopie)
@@ -53,7 +53,7 @@
 	void toggleLabelVisibility(bool v);
 
 	void showFirstLinePolicy(FirstLineOffsetPolicy f);
-	void showOpticalMargins(int e);
+	void showOpticalMargins(int e, const QString &setId);
 	void showAlignment(int e);
 	void showDirection(int e);
 
@@ -62,8 +62,6 @@
 	void handleTabs();
 	void handleFirstLinePolicy(int);
 	void handleOpticalMargins();
-//	void resetOpticalMargins();
-
 	void handleAlignment(int a);
 	void handleVAlignment(int a);
 	void handleDirection(int d);
Index: scribus/ui/smopticalmarginswidget.cpp
===================================================================
--- scribus/ui/smopticalmarginswidget.cpp	(Revision 26436)
+++ scribus/ui/smopticalmarginswidget.cpp	(Arbeitskopie)
@@ -54,12 +54,50 @@
 	connect(parentButton, SIGNAL(pressed()), this, SLOT(pbPressed()));
 }
 
+void SMOpticalMarginsWidget::setOpticalMarginSetId(const QString o)
+{
+	disconnect(parentButton, SIGNAL(pressed()), this, SLOT(pbPressed()));
+	disconnect(this, SIGNAL(opticalMarginChanged()), this, SLOT(styleChanged()));
+
+	m_hasParent = false;
+	parentButton->hide();
+	m_pSetId = "";
+
+	OpticalMarginsWidget::setOpticalMarginSetId(o);
+
+	connect(this, SIGNAL(opticalMarginChanged()), this, SLOT(styleChanged()));
+	connect(parentButton, SIGNAL(pressed()), this, SLOT(pbPressed()));
+}
+
+void SMOpticalMarginsWidget::setOpticalMarginSetId(const QString o, const QString ps, bool isParentValue)
+{
+	disconnect(parentButton, SIGNAL(pressed()), this, SLOT(pbPressed()));
+	disconnect(this, SIGNAL(opticalMarginChanged()), this, SLOT(styleChanged()));
+
+	if (!ps.isEmpty())
+	{
+		m_hasParent = true;
+		parentButton->setVisible(!isParentValue);
+		m_pSetId = ps;
+	}
+	else
+		m_hasParent = false;
+
+	OpticalMarginsWidget::setOpticalMarginSetId(o);
+
+	connect(this, SIGNAL(opticalMarginChanged()), this, SLOT(styleChanged()));
+	connect(parentButton, SIGNAL(pressed()), this, SLOT(pbPressed()));
+}
+
 bool SMOpticalMarginsWidget::useParentValue()
 {
 	bool ret = m_useParentStyle;
 	m_useParentStyle = false;
 	if (ret)
+	{
 		setOpticalMargins(m_pStyle, m_pStyle, true);
+		setOpticalMarginSetId(m_pSetId, m_pSetId, true);
+	}
 
 	return ret;
 }
Index: scribus/ui/smopticalmarginswidget.h
===================================================================
--- scribus/ui/smopticalmarginswidget.h	(Revision 26436)
+++ scribus/ui/smopticalmarginswidget.h	(Arbeitskopie)
@@ -20,6 +20,9 @@
 	void setOpticalMargins(int o);
 	void setOpticalMargins(int o, int ps, bool isParentValue);
 
+	void setOpticalMarginSetId(const QString o);
+	void setOpticalMarginSetId(const QString o, const QString ps, bool isParentValue);
+
 	bool useParentValue();
 
 private:
@@ -26,6 +29,7 @@
 	bool m_hasParent = false;
 	bool m_useParentStyle = false;
 	int m_pStyle = -1;
+	QString m_pSetId;
 
 private slots:
 	void styleChanged();
Index: scribus/ui/smpstylewidget.cpp
===================================================================
--- scribus/ui/smpstylewidget.cpp	(Revision 26436)
+++ scribus/ui/smpstylewidget.cpp	(Arbeitskopie)
@@ -224,6 +224,8 @@
 	//fillNumerationsCombo();
 	//fillNumRestartCombo();
 
+	optMarginWidget->setOpticalMarginSets(m_Doc->opticalMarginSets());
+
 	if (m_hasParent)
 	{
 		lineSpacingMode->setCurrentItem(pstyle->lineSpacingMode(), pstyle->isInhLineSpacingMode());
@@ -231,7 +233,8 @@
 		
 //		optMarginCombo->setCurrentItemByData( pstyle->opticalMargins(),  pstyle->isInhOpticalMargins() );
 //		optMarginCombo->setParentItem(optMarginCombo->getItemIndexForData( parent->opticalMargins()));
-		optMarginWidget->setOpticalMargins(pstyle->opticalMargins(), parent->opticalMargins(), pstyle->isInhOpticalMargins());
+		optMarginWidget->setOpticalMargins(pstyle->opticalMargins(), parent->opticalMargins(), pstyle->isInhOpticalMargins());		
+		optMarginWidget->setOpticalMarginSetId(pstyle->opticalMarginSetId(), parent->opticalMarginSetId(), pstyle->isInhOpticalMarginSetId());
 		
 		minSpaceSpin->setValue(pstyle->minWordTracking() * 100.0,  pstyle->isInhMinWordTracking());
 		minSpaceSpin->setParentValue(parent->minWordTracking());
@@ -350,7 +353,8 @@
 		spaceAbove->setValue(pstyle->gapBefore());
 		spaceBelow->setValue(pstyle->gapAfter());
 //		optMarginCombo->setCurrentItemByData( pstyle->opticalMargins() );
-		optMarginWidget->setOpticalMargins(pstyle->opticalMargins());
+		optMarginWidget->setOpticalMargins(pstyle->opticalMargins());
+		optMarginWidget->setOpticalMarginSetId(pstyle->opticalMarginSetId());
 		minSpaceSpin->setValue(pstyle->minWordTracking() * 100.0);
 		minGlyphExtSpin->setValue(pstyle->minGlyphExtension() * 100.0);
 		maxGlyphExtSpin->setValue(pstyle->maxGlyphExtension() * 100.0);
@@ -658,7 +662,9 @@
 //		}
 //	}
 //	optMarginCombo->setCurrentItemByData(o);
-	optMarginWidget->setOpticalMargins(pstyles[0]->opticalMargins());
+	optMarginWidget->setOpticalMargins(pstyles[0]->opticalMargins());
+	optMarginWidget->setOpticalMarginSets(m_Doc->opticalMarginSets());
+	optMarginWidget->setOpticalMarginSetId(pstyles[0]->opticalMarginSetId());
 }
 
 void SMPStyleWidget::showMinSpace(const QList< ParagraphStyle * > & pstyles)
Index: scribus/ui/smtextstyles.cpp
===================================================================
--- scribus/ui/smtextstyles.cpp	(Revision 26436)
+++ scribus/ui/smtextstyles.cpp	(Arbeitskopie)
@@ -508,14 +508,11 @@
 
 	connect(m_pwidget, SIGNAL(useParentParaEffects()), this, SLOT(slotParentParaEffects()));
 	connect(m_pwidget->peCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(slotPargraphEffects(int)));
-//	connect(m_pwidget->dropCapsBox, SIGNAL(toggled(bool)), this, SLOT(slotDropCap(bool)));
 	connect(m_pwidget->dropCapLines, SIGNAL(valueChanged(int)), this, SLOT(slotDropCapLines(int)));
 	connect(m_pwidget->parEffectOffset, SIGNAL(valueChanged(double)), this, SLOT(slotParEffectOffset()));
 	connect(m_pwidget->parEffectIndentBox, SIGNAL(toggled(bool)), this, SLOT(slotParEffectIndent(bool)));
 	connect(m_pwidget->parEffectCharStyleCombo, SIGNAL(activated(int)), this, SLOT(slotParEffectCharStyle(int)));
-//	connect(m_pwidget->bulletBox, SIGNAL(toggled(bool)), this, SLOT(slotBullet(bool)));
 	connect(m_pwidget->bulletStrEdit, SIGNAL(editTextChanged(QString)), this, SLOT(slotBulletStr(QString)));
-//	connect(m_pwidget->numBox, SIGNAL(toggled(bool)), this, SLOT(slotNumeration(bool)));
 	connect(m_pwidget->numComboBox, SIGNAL(textActivated(QString)), this, SLOT(slotNumName(QString)));
 	connect(m_pwidget->numLevelSpin, SIGNAL(valueChanged(int)), this, SLOT(slotNumLevel(int)));
 	connect(m_pwidget->numFormatCombo, SIGNAL(activated(int)), this, SLOT(slotNumFormat(int)));
@@ -553,10 +550,6 @@
 	connect(m_pwidget->cpage->effects_->UnderlineVal->LWidth, SIGNAL(valueChanged(double)), this, SLOT(slotEffectProperties()));
 	connect(m_pwidget->cpage->effects_->StrikeVal->LPos, SIGNAL(valueChanged(double)), this, SLOT(slotEffectProperties()));
 	connect(m_pwidget->cpage->effects_->StrikeVal->LWidth, SIGNAL(valueChanged(double)), this, SLOT(slotEffectProperties()));
-	// connect(m_pwidget->cpage->fillColor_, SIGNAL(currentTextChanged(QString)), this, SLOT(slotFillColor()));
-	// connect(m_pwidget->cpage->fillShade_, SIGNAL(clicked()), this, SLOT(slotFillShade()));
-	// connect(m_pwidget->cpage->strokeColor_, SIGNAL(currentTextChanged(QString)), this, SLOT(slotStrokeColor()));
-	// connect(m_pwidget->cpage->strokeShade_, SIGNAL(clicked()), this, SLOT(slotStrokeShade()));
 	connect(m_pwidget->cpage->language_, SIGNAL(activated(int)), this, SLOT(slotLanguage()));
 	connect(m_pwidget->cpage->fontSize_, SIGNAL(valueChanged(double)), this, SLOT(slotFontSize()));
 	connect(m_pwidget->cpage->fontHScale_, SIGNAL(valueChanged(double)), this, SLOT(slotScaleH()));
@@ -565,8 +558,6 @@
 	connect(m_pwidget->cpage->widthSpaceSpin, SIGNAL(valueChanged(double)), this, SLOT(slotWordTracking()));
 	connect(m_pwidget->cpage->baselineOffset_, SIGNAL(valueChanged(double)), this, SLOT(slotBaselineOffset()));
 	connect(m_pwidget->cpage->parentCombo, SIGNAL(currentTextChanged(QString)), this, SLOT(slotCharParentChanged(QString)));
-	// connect(m_pwidget->cpage->backColor_, SIGNAL(currentTextChanged(QString)), this, SLOT(slotBackColor()));
-	// connect(m_pwidget->cpage->backShade_, SIGNAL(clicked()), this, SLOT(slotBackShade()));
 
 	connect(m_pwidget->cpage->textColor->colorButton, SIGNAL(colorChanged()), this, SLOT(slotFillColor()));
 	connect(m_pwidget->cpage->textColor->parentButton, SIGNAL(clicked()), this, SLOT(slotFillColor()));
@@ -612,14 +603,11 @@
 
 	disconnect(m_pwidget, SIGNAL(useParentParaEffects()), this, SLOT(slotParentParaEffects()));
 	disconnect(m_pwidget->peCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(slotPargraphEffects(int)));
-//	disconnect(m_pwidget->dropCapsBox, SIGNAL(toggled(bool)), this, SLOT(slotDropCap(bool)));
 	disconnect(m_pwidget->dropCapLines, SIGNAL(valueChanged(int)), this, SLOT(slotDropCapLines(int)));
 	disconnect(m_pwidget->parEffectOffset, SIGNAL(valueChanged(double)), this, SLOT(slotParEffectOffset()));
 	disconnect(m_pwidget->parEffectIndentBox, SIGNAL(toggled(bool)), this, SLOT(slotParEffectIndent(bool)));
 	disconnect(m_pwidget->parEffectCharStyleCombo, SIGNAL(activated(int)), this, SLOT(slotParEffectCharStyle(int)));
-//	disconnect(m_pwidget->bulletBox, SIGNAL(toggled(bool)), this, SLOT(slotBullet(bool)));
 	disconnect(m_pwidget->bulletStrEdit, SIGNAL(editTextChanged(QString)), this, SLOT(slotBulletStr(QString)));
-//	disconnect(m_pwidget->numBox, SIGNAL(toggled(bool)), this, SLOT(slotNumeration(bool)));
 	disconnect(m_pwidget->numComboBox, SIGNAL(textActivated(QString)), this, SLOT(slotNumName(QString)));
 	disconnect(m_pwidget->numFormatCombo, SIGNAL(activated(int)), this, SLOT(slotNumFormat(int)));
 	disconnect(m_pwidget->numLevelSpin, SIGNAL(valueChanged(int)), this, SLOT(slotNumLevel(int)));
@@ -655,10 +643,6 @@
 	disconnect(m_pwidget->cpage->effects_->UnderlineVal->LWidth, SIGNAL(valueChanged(double)), this, SLOT(slotEffectProperties()));
 	disconnect(m_pwidget->cpage->effects_->StrikeVal->LPos, SIGNAL(valueChanged(double)), this, SLOT(slotEffectProperties()));
 	disconnect(m_pwidget->cpage->effects_->StrikeVal->LWidth, SIGNAL(valueChanged(double)), this, SLOT(slotEffectProperties()));
-	// disconnect(m_pwidget->cpage->fillColor_, SIGNAL(currentTextChanged(QString)), this, SLOT(slotFillColor()));
-	// disconnect(m_pwidget->cpage->fillShade_, SIGNAL(clicked()), this, SLOT(slotFillShade()));
-	// disconnect(m_pwidget->cpage->strokeColor_, SIGNAL(currentTextChanged(QString)), this, SLOT(slotStrokeColor()));
-	// disconnect(m_pwidget->cpage->strokeShade_, SIGNAL(clicked()), this, SLOT(slotStrokeShade()));
 	disconnect(m_pwidget->cpage->language_, SIGNAL(activated(int)), this, SLOT(slotLanguage()));
 	disconnect(m_pwidget->cpage->fontSize_, SIGNAL(valueChanged(double)), this, SLOT(slotFontSize()));
 	disconnect(m_pwidget->cpage->fontHScale_, SIGNAL(valueChanged(double)), this, SLOT(slotScaleH()));
@@ -667,8 +651,6 @@
 	disconnect(m_pwidget->cpage->widthSpaceSpin, SIGNAL(valueChanged(double)), this, SLOT(slotWordTracking()));
 	disconnect(m_pwidget->cpage->baselineOffset_, SIGNAL(valueChanged(double)), this, SLOT(slotBaselineOffset()));
 	disconnect(m_pwidget->cpage->parentCombo, SIGNAL(currentTextChanged(QString)), this, SLOT(slotCharParentChanged(QString)));
-	// disconnect(m_pwidget->cpage->backColor_, SIGNAL(currentTextChanged(QString)), this, SLOT(slotBackColor()));
-	// disconnect(m_pwidget->cpage->backShade_, SIGNAL(clicked()), this, SLOT(slotBackShade()));
 
 	disconnect(m_pwidget->cpage->textColor->colorButton, SIGNAL(colorChanged()), this, SLOT(slotFillColor()));
 	disconnect(m_pwidget->cpage->textColor->parentButton, SIGNAL(clicked()), this, SLOT(slotFillColor()));
@@ -796,23 +778,21 @@
 	ParagraphStyle::OpticalMarginType omt( static_cast<ParagraphStyle::OpticalMarginType>(m_pwidget->optMarginWidget->opticalMargin()));
 	if (m_pwidget->optMarginWidget->useParentValue())
 		for (int i = 0; i < m_selection.count(); ++i)
+		{
 			m_selection[i]->resetOpticalMargins();
+			m_selection[i]->resetOpticalMarginSetId();
+		}
 	else
 		for (int i = 0; i < m_selection.count(); ++i)
-			m_selection[i]->setOpticalMargins(omt);
+		{
+			m_selection[i]->setOpticalMarginSetId(m_pwidget->optMarginWidget->opticalMarginSetId());
+			m_selection[i]->setOpticalMargins(omt);			
+		}
 
 	slotSelectionDirty();
 
 }
 
-void SMParagraphStyle::slotParentOpticalMargin()
-{
-	for (int i = 0; i < m_selection.count(); ++i)
-		m_selection[i]->resetOpticalMargins();
-
-	slotSelectionDirty();
-}
-
 void SMParagraphStyle::slotMinSpace()
 {
 	if (m_pwidget->minSpaceSpin->useParentValue())
Index: scribus/ui/smtextstyles.h
===================================================================
--- scribus/ui/smtextstyles.h	(Revision 26436)
+++ scribus/ui/smtextstyles.h	(Arbeitskopie)
@@ -97,7 +97,6 @@
 	void slotDirection();
 	void slotOpticalMargin(int i);
 	void slotOpticalMarginSelector();
-	void slotParentOpticalMargin();
 	void slotMinSpace();
 	void slotMinGlyphExt();
 	void slotMaxGlyphExt();
Index: scribus/ui/stylemanager.cpp
===================================================================
--- scribus/ui/stylemanager.cpp	(Revision 26436)
+++ scribus/ui/stylemanager.cpp	(Arbeitskopie)
@@ -473,6 +473,9 @@
 		QStringList neededColors;
 		neededColors.clear();
 
+		QStringList neededOpticalMarginSets;
+		neededOpticalMarginSets.clear();
+
 		foreach (const QString& aStyle, dia2->paragraphStyles())
 		{
 			ParagraphStyle& sty(tmpParaStyles[tmpParaStyles.find(aStyle)]);
@@ -493,6 +496,9 @@
 				neededColors.append(sty.charStyle().strokeColor());
 			if ((!m_doc->PageColors.contains(sty.charStyle().fillColor())) && (!neededColors.contains(sty.charStyle().fillColor())))
 				neededColors.append(sty.charStyle().fillColor());
+
+			if ((!m_doc->opticalMarginSets().contains(sty.opticalMarginSetId())) && (!neededOpticalMarginSets.contains(sty.opticalMarginSetId())))
+				neededOpticalMarginSets.append(sty.opticalMarginSetId());
 		}
 
 		foreach (const QString& aStyle, dia2->characterStyles())
@@ -554,6 +560,11 @@
 				}
 			}
 		}
+
+		if (!neededOpticalMarginSets.isEmpty())
+		{
+			// TODO: Load missing optical margin sets from doc
+		}
 	}
 	delete dia2;
 // Start hack part 2
Index: scribus/util_text.cpp
===================================================================
--- scribus/util_text.cpp	(Revision 26436)
+++ scribus/util_text.cpp	(Arbeitskopie)
@@ -94,3 +94,29 @@
 	return QString(xml.c_str());
 }
 
+
+const QString stringToUnicode(const QString &text)
+{
+	QString out;
+
+	for (auto c : text)
+		out += QString("U+%1,").arg(c.unicode(), 4, 16, QChar('0')).toUpper();
+
+	return out.mid(0, out.length() -1);
+
+}
+
+const QString unicodeToString(const QString &text)
+{
+	QString out;
+	for (const auto &str : text.split(",", Qt::SkipEmptyParts))
+	{
+		QString tmp(str);
+		tmp.remove("U+");
+
+		bool ok;
+		out += QChar(tmp.toInt(&ok, 16));
+	}
+
+	return out;
+}
Index: scribus/util_text.h
===================================================================
--- scribus/util_text.h	(Revision 26436)
+++ scribus/util_text.h	(Arbeitskopie)
@@ -30,4 +30,7 @@
 //returns string with saxed story
 QString SCRIBUS_API saxedText(StoryText* story);
 
+const QString SCRIBUS_API stringToUnicode(const QString &text);
+const QString SCRIBUS_API unicodeToString(const QString &text);
+
 #endif
