summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJabiertxof <jtx@jtx>2016-12-16 12:00:49 +0000
committerJabiertxof <jtx@jtx>2016-12-16 12:00:49 +0000
commit4446366fd075fb801420da520848c774b8ac4254 (patch)
treec2fc74dabefc43fd586aaf51405bebc698d7ba94
parentFixing nested mirrors (diff)
parentFix bug where conical gradient drawn in wrong arc. (diff)
downloadinkscape-4446366fd075fb801420da520848c774b8ac4254.tar.gz
inkscape-4446366fd075fb801420da520848c774b8ac4254.zip
Update to trunk
(bzr r15295.1.23)
-rw-r--r--share/extensions/dpiswitcher.py371
-rw-r--r--src/desktop-style.cpp11
-rw-r--r--src/file.cpp270
-rw-r--r--src/sp-item-group.cpp5
-rw-r--r--src/sp-mesh-array.cpp2
-rw-r--r--src/ui/tools/tweak-tool.cpp3
-rw-r--r--src/widgets/text-toolbar.cpp48
-rw-r--r--src/widgets/toolbox.cpp1
8 files changed, 559 insertions, 152 deletions
diff --git a/share/extensions/dpiswitcher.py b/share/extensions/dpiswitcher.py
index 46dad5ee5..317616db5 100644
--- a/share/extensions/dpiswitcher.py
+++ b/share/extensions/dpiswitcher.py
@@ -1,10 +1,11 @@
#!/usr/bin/env python
'''
-This extension scale or reduce a document to fit diferent SVG DPI -90/96-
+This extension scales a document to fit different SVG DPI -90/96-
Copyright (C) 2012 Jabiertxo Arraiza, jabier.arraiza@marker.es
+Copyright (C) 2016 su_v, <suv-sf@users.sf.net>
-Version 0.5 - DPI Switcher
+Version 0.6 - DPI Switcher
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,22 +20,40 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Changes since v0.5:
+ - transform all top-level containers and graphics elements
+ - support scientific notation in SVG lengths
+ - fix scaling with existing matrix() (use functions from simpletransform.py)
+ - support different units for document width, height attributes
+ - improve viewBox support (syntax, offset)
+ - support common cases of text-put-on-path in SVG root
+ - support common cases of <use> references in SVG root
+ - examples from http://tavmjong.free.fr/INKSCAPE/UNITS/ tested
+
+TODO:
+ - check grids/guides created with 0.91:
+ http://tavmjong.free.fr/INKSCAPE/UNITS/units_mm_nv_90dpi.svg
+ - check <symbol> instances
+ - check more <use> and text-on-path cases (reverse scaling needed?)
+ - scale perspective of 3dboxes
+
'''
# standard libraries
import sys
import re
import string
+import math
from lxml import etree
# local libraries
import inkex
+import simpletransform
+import simplestyle
# globals
-REFERENCED_CONTAINERS = [
- # These container elements - which may be referenced by other
- # elements - do not need to be scaled directly. The referencing
- # elements will be either insided scaled containers or scaled
- # directly as graphics elements in SVG root.
+SKIP_CONTAINERS = [
'defs',
'glyph',
'marker',
@@ -44,15 +63,11 @@ REFERENCED_CONTAINERS = [
'symbol',
]
CONTAINER_ELEMENTS = [
- # These element types have graphics elements and other container
- # elements as child elements. They need to be scaled if in SVG root.
'a',
'g',
'switch',
]
GRAPHICS_ELEMENTS = [
- # These element types cause graphics to be drawn. They need to be
- # scaled if in SVG root.
'circle',
'ellipse',
'image',
@@ -64,14 +79,118 @@ GRAPHICS_ELEMENTS = [
'text',
'use',
]
-# FIXME: instances and referenced elements
-# If for example a referenced element in SVG root is directly scaled,
-# and its instance (referencing element e.g. <use>) is inside a scaled
-# top-level container, the instance in the end will be rendered at an
-# incorrect scale relative to the viewport (page area) and the other
-# drawing content. Another unsupported case is both the referenced
-# element and the instance in SVG root: the clone will in the end be
-# rendered with the scale factor applied twice.
+
+def is_3dbox(element):
+ """Check whether element is an Inkscape 3dbox type."""
+ return element.get(inkex.addNS('type', 'sodipodi')) == 'inkscape:box3d'
+
+
+def is_use(element):
+ """Check whether element is of type <text>."""
+ return element.tag == inkex.addNS('use', 'svg')
+
+
+def is_text(element):
+ """Check whether element is of type <text>."""
+ return element.tag == inkex.addNS('text', 'svg')
+
+
+def is_text_on_path(element):
+ """Check whether text element is put on a path."""
+ if is_text(element):
+ text_path = element.find(inkex.addNS('textPath', 'svg'))
+ if text_path is not None and len(text_path):
+ return True
+ return False
+
+
+def is_sibling(element1, element2):
+ """Check whether element1 and element2 are siblings of same parent."""
+ return element2 in element1.getparent()
+
+
+def is_in_defs(doc, element):
+ """Check whether element is in defs."""
+ if element is not None:
+ defs = doc.find('defs', namespaces=inkex.NSS)
+ if defs is not None:
+ return linked_node in defs.iterdescendants()
+ return False
+
+
+def get_linked(doc, element):
+ """Return linked element or None."""
+ if element is not None:
+ href = element.get(inkex.addNS('href', 'xlink'), None)
+ if href is not None:
+ linked_id = href[href.find('#')+1:]
+ path = '//*[@id="%s"]' % linked_id
+ el_list = doc.xpath(path, namespaces=inkex.NSS)
+ if isinstance(el_list, list) and len(el_list):
+ return el_list[0]
+ else:
+ return None
+
+
+def check_3dbox(svg, element, scale_x, scale_y):
+ """Check transformation for 3dbox element."""
+ skip = False
+ if skip:
+ # 3dbox elements ignore preserved transforms
+ # FIXME: manually update geometry of 3dbox?
+ pass
+ return skip
+
+
+def check_text_on_path(svg, element, scale_x, scale_y):
+ """Check whether to skip scaling a text put on a path."""
+ skip = False
+ path = get_linked(svg, element.find(inkex.addNS('textPath', 'svg')))
+ if not is_in_defs(svg, path):
+ if is_sibling(element, path):
+ # skip common element scaling if both text and path are siblings
+ skip = True
+ # scale offset
+ if 'transform' in element.attrib:
+ mat = simpletransform.parseTransform(element.get('transform'))
+ mat[0][2] *= scale_x
+ mat[1][2] *= scale_y
+ element.set('transform', simpletransform.formatTransform(mat))
+ # scale font size
+ mat = simpletransform.parseTransform(
+ 'scale({},{})'.format(scale_x, scale_y))
+ det = abs(mat[0][0]*mat[1][1] - mat[0][1]*mat[1][0])
+ descrim = math.sqrt(abs(det))
+ prop = 'font-size'
+ # outer text
+ sdict = simplestyle.parseStyle(element.get('style'))
+ if prop in sdict:
+ sdict[prop] = float(sdict[prop]) * descrim
+ element.set('style', simplestyle.formatStyle(sdict))
+ # inner tspans
+ for child in element.iterdescendants():
+ if child.tag == inkex.addNS('tspan', 'svg'):
+ sdict = simplestyle.parseStyle(child.get('style'))
+ if prop in sdict:
+ sdict[prop] = float(sdict[prop]) * descrim
+ child.set('style', simplestyle.formatStyle(sdict))
+ return skip
+
+
+def check_use(svg, element, scale_x, scale_y):
+ """Check whether to skip scaling an instanciated element (<use>)."""
+ skip = False
+ path = get_linked(svg, element)
+ if not is_in_defs(svg, path):
+ if is_sibling(element, path):
+ skip = True
+ # scale offset
+ if 'transform' in element.attrib:
+ mat = simpletransform.parseTransform(element.get('transform'))
+ mat[0][2] *= scale_x
+ mat[1][2] *= scale_y
+ element.set('transform', simpletransform.formatTransform(mat))
+ return skip
class DPISwitcher(inkex.Effect):
@@ -89,106 +208,140 @@ class DPISwitcher(inkex.Effect):
self.units = "px"
self.unitExponent = 1.0
+ # dictionaries of unit to user unit conversion factors
+ __uuconvLegacy = {
+ 'in': 90.0,
+ 'pt': 1.25,
+ 'px': 1.0,
+ 'mm': 3.5433070866,
+ 'cm': 35.433070866,
+ 'm': 3543.3070866,
+ 'km': 3543307.0866,
+ 'pc': 15.0,
+ 'yd': 3240.0,
+ 'ft': 1080.0,
+ }
+ __uuconv = {
+ 'in': 96.0,
+ 'pt': 1.33333333333,
+ 'px': 1.0,
+ 'mm': 3.77952755913,
+ 'cm': 37.7952755913,
+ 'm': 3779.52755913,
+ 'km': 3779527.55913,
+ 'pc': 16.0,
+ 'yd': 3456.0,
+ 'ft': 1152.0,
+ }
+
+ def parse_length(self, length, percent=False):
+ """Parse SVG length."""
+ if self.options.switcher == "0": # dpi90to96
+ known_units = self.__uuconvLegacy.keys()
+ else: # dpi96to90
+ known_units = self.__uuconv.keys()
+ if percent:
+ unitmatch = re.compile('(%s)$' % '|'.join(known_units + ['%']))
+ else:
+ unitmatch = re.compile('(%s)$' % '|'.join(known_units))
+ param = re.compile(r'(([-+]?[0-9]+(\.[0-9]*)?|[-+]?\.[0-9]+)([eE][-+]?[0-9]+)?)')
+ p = param.match(length)
+ u = unitmatch.search(length)
+ val = 100 # fallback: assume default length of 100
+ unit = 'px' # fallback: assume 'px' unit
+ if p:
+ val = float(p.string[p.start():p.end()])
+ if u:
+ unit = u.string[u.start():u.end()]
+ return (val, unit)
+
+ def convert_length(self, val, unit):
+ """Convert length to self.units if unit differs."""
+ doc_unit = self.units or 'px'
+ if unit != doc_unit:
+ if self.options.switcher == "0": # dpi90to96
+ val_px = val * self.__uuconvLegacy[unit]
+ val = val_px / (self.__uuconvLegacy[doc_unit] / self.__uuconvLegacy['px'])
+ unit = doc_unit
+ else: # dpi96to90
+ val_px = val * self.__uuconv[unit]
+ val = val_px / (self.__uuconv[doc_unit] / self.__uuconv['px'])
+ unit = doc_unit
+ return (val, unit)
+
+ def check_attr_unit(self, element, attr, unit_list):
+ """Check unit of attribute value, match to units in *unit_list*."""
+ if attr in element.attrib:
+ unit = self.parse_length(element.get(attr), percent=True)[1]
+ return unit in unit_list
+
+ def scale_attr_val(self, element, attr, unit_list, factor):
+ """Scale attribute value if unit matches one in *unit_list*."""
+ if attr in element.attrib:
+ val, unit = self.parse_length(element.get(attr), percent=True)
+ if unit in unit_list:
+ element.set(attr, '{}{}'.format(val * factor, unit))
+
def scaleRoot(self, svg):
- widthNumber = re.sub("[a-zA-Z]", "", svg.get('width'))
- heightNumber = re.sub("[a-zA-Z]", "", svg.get('height'))
- widthDoc = str(float(widthNumber) * self.factor_a * self.unitExponent)
- heightDoc = str(float(heightNumber) * self.factor_a * self.unitExponent)
- if svg.get('viewBox'):
- widthNumber = svg.get('viewBox').split(" ")[2]
- heightNumber = svg.get('viewBox').split(" ")[3]
+ """Scale all top-level elements in SVG root."""
+
+ # update viewport
+ widthNumber = self.parse_length(svg.get('width'))[0]
+ heightNumber = self.convert_length(*self.parse_length(svg.get('height')))[0]
+ widthDoc = widthNumber * self.factor_a * self.unitExponent
+ heightDoc = heightNumber * self.factor_a * self.unitExponent
+
if svg.get('height'):
- svg.set('height', heightDoc)
+ svg.set('height', str(heightDoc))
if svg.get('width'):
- svg.set('width', widthDoc)
+ svg.set('width', str(widthDoc))
+
+ # update viewBox
if svg.get('viewBox'):
- svg.set('viewBox',"0 0 " + str(float(widthNumber) * self.factor_a) + " " + str(float(heightNumber) * self.factor_a))
+ viewboxstring = re.sub(' +|, +|,',' ', svg.get('viewBox'))
+ viewboxlist = [float(i) for i in viewboxstring.strip().split(' ', 4)]
+ svg.set('viewBox','{} {} {} {}'.format(*[(val * self.factor_a) for val in viewboxlist]))
+
+ # update guides, grids
if self.options.switcher == "1":
+ # FIXME: dpi96to90 only?
self.scaleGuides(svg)
self.scaleGrid(svg)
+
for element in svg: # iterate all top-level elements of SVGRoot
- box3DSide = element.get(inkex.addNS('box3dsidetype', 'inkscape'))
- if box3DSide:
- continue
- uri, tag = element.tag.split("}")
+
+ # init variables
+ tag = etree.QName(element).localname
width_scale = self.factor_a
height_scale = self.factor_a
- if tag in GRAPHICS_ELEMENTS or tag in CONTAINER_ELEMENTS:
- if element.get('width') is not None and \
- (re.sub("[0-9]*\.?[0-9]", "", element.get('width')) == "%" or \
- re.sub("[0-9]*\.?[0-9]", "", element.get('width')) == "px"):
- width_scale = 1.0;
- if element.get('height') is not None and \
- (re.sub("[0-9]*\.?[0-9]", "", element.get('height')) == "%" or \
- re.sub("[0-9]*\.?[0-9]", "", element.get('height')) == "px"):
- height_scale = 1.0;
- if element.get('x') is not None and \
- re.sub("[0-9]*\.?[0-9]", "", element.get('x')) == "%":
- xpos = str(float(element.get('x').replace('%','')) * self.factor_b) + '%'
- element.set('x', xpos)
- if element.get('y') is not None and \
- re.sub("[0-9]*\.?[0-9]", "", element.get('y')) == "%":
- ypos = str(float(element.get('y').replace('%','')) * self.factor_b) + '%'
- element.set('y', ypos)
- if element.get('transform'):
- if "matrix" in str(element.get('transform')) and width_scale != 1.0:
- result = re.sub(r".*?matrix( \(|\()(.*?)\)", self.matrixElement, str(element.get('transform')))
- element.set('transform', result)
- if "scale" in str(element.get('transform')) and width_scale != 1.0:
- result = re.sub(r".*?scale( \(|\()(.*?)\)", self.scaleElement, str(element.get('transform')))
- element.set('transform', result)
- if "translate" in str(element.get('transform')) and width_scale != 1.0:
- result = re.sub(r".*?translate( \(|\()(.*?)\)", self.translateElement, str(element.get('transform')))
- element.set('transform', result)
- if "skew" in str(element.get('transform')) and width_scale != 1.0:
- result = re.sub(r".*?skew( \(|\()(.*?)\)", self.skewElement, str(element.get('transform')))
- element.set('transform', result)
- if "scale" not in str(element.get('transform')) and "matrix" not in str(element.get('transform')):
- element.set('transform', str(element.get('transform')) + "scale(" + str( width_scale) + ", " + str(height_scale) + ")")
- else:
- element.set('transform', "scale(" + str(width_scale) + ", " + str(height_scale) + ")")
- #a dictionary of unit to user unit conversion factors
- __uuconv = {'in':96.0, 'pt':1.33333333333, 'px':1.0, 'mm':3.77952755913, 'cm':37.7952755913,
- 'm':3779.52755913, 'km':3779527.55913, 'pc':16.0, 'yd':3456.0 , 'ft':1152.0}
-
- __uuconvLegacy = {'in':90.0, 'pt':1.25, 'px':1, 'mm':3.5433070866, 'cm':35.433070866, 'm':3543.3070866,
- 'km':3543307.0866, 'pc':15.0, 'yd':3240 , 'ft':1080}
+ if tag in GRAPHICS_ELEMENTS or tag in CONTAINER_ELEMENTS:
- def scaleElement(self, m):
- scaleVal = m.group(2).replace(" ","")
- total = scaleVal.count(',')
- if total == 1:
- scaleVal = scaleVal.split(",")
- return "matrix(" + str(float(scaleVal[0]) * self.factor_a) + ",0,0," + str(float(scaleVal[1]) * self.factor_a) + ",0,0)"
- else:
- return "matrix(" + str(float(scaleVal) * self.factor_a) + ",0,0," + str(float(scaleVal) * self.factor_a) + ",0,0)"
+ # test for specific elements to skip from scaling
+ if is_3dbox(element):
+ if check_3dbox(svg, element, width_scale, height_scale):
+ continue
+ if is_text_on_path(element):
+ if check_text_on_path(svg, element, width_scale, height_scale):
+ continue
+ if is_use(element):
+ if check_use(svg, element, width_scale, height_scale):
+ continue
+ # relative units ('%') in presentation attributes
+ for attr in ['width', 'height']:
+ self.scale_attr_val(element, attr, ['%'], 1.0 / self.factor_a)
+ for attr in ['x', 'y']:
+ self.scale_attr_val(element, attr, ['%'], 1.0 / self.factor_a)
- def translateElement(self, m):
- translateVal = m.group(2).replace(" ","")
- total = translateVal.count(',')
- if total == 1:
- translateVal = translateVal.split(",")
- return "matrix(" + str(self.factor_a) + ",0,0," + str(self.factor_a) + "," + str(float(translateVal[0]) * self.factor_a) + "," + str(float(translateVal[1]) * self.factor_a) + ")"
- else:
- return "matrix(" + str(self.factor_a) + ",0,0," + str(self.factor_a) + "," + str(float(translateVal) * self.factor_a) + "," + str(float(translateVal) * self.factor_a) + ")"
-
- def skewElement(self, m):
- skeweVal = m.group(2).replace(" ","")
- total = skewVal.count(',')
- if total == 1:
- skeweVal = skewVal.split(",")
- return "skew(" + str(float(skewVal[0]) * self.factor_a) + "," + str(float(skewVal[1]) * self.factor_a) + ") matrix(" + str(self.factor_a) + ",0,0," + str(self.factor_a) + ",0,0)"
- else:
- return "skew(" + str(float(skewVal) * self.factor_a) + ") matrix(" + str(self.factor_a) + ",0,0," + str(self.factor_a) + ",0,0)"
+ # set preserved transforms on top-level elements
+ if width_scale != 1.0 and height_scale != 1.0:
+ mat = simpletransform.parseTransform(
+ 'scale({},{})'.format(width_scale, height_scale))
+ simpletransform.applyTransformToNode(mat, element)
- def matrixElement(self, m):
- matrixVal = m.group(2).replace(" ","")
- total = matrixVal.count(',')
- matrixVal = matrixVal.split(",")
- if total == 5:
- return "matrix(" + str(float(matrixVal[0]) * self.factor_a) + "," + matrixVal[1] + "," + matrixVal[2] + "," + str(float(matrixVal[3]) * self.factor_a) + "," + str(float(matrixVal[4]) * self.factor_a) + "," + str(float(matrixVal[5]) * self.factor_a) + ")"
+ def scaleElement(self, m):
+ pass # TODO: optionally scale graphics elements only?
def scaleGuides(self, svg):
xpathStr = '//sodipodi:guide'
@@ -255,7 +408,7 @@ class DPISwitcher(inkex.Effect):
self.factor_b = 90.0/96.0
namedview = svg.find(inkex.addNS('namedview', 'sodipodi'))
namedview.set(inkex.addNS('document-units', 'inkscape'), "px")
- self.units = re.sub("[0-9]*\.?[0-9]", "", svg.get('width'))
+ self.units = self.parse_length(svg.get('width'))[1]
if self.units and self.units <> "px" and self.units <> "" and self.units <> "%":
if self.options.switcher == "0":
self.unitExponent = 1.0/(self.factor_a/self.__uuconv[self.units])
@@ -264,5 +417,9 @@ class DPISwitcher(inkex.Effect):
self.scaleRoot(svg);
sys.stdout = saveout
-effect = DPISwitcher()
-effect.affect()
+
+if __name__ == '__main__':
+ effect = DPISwitcher()
+ effect.affect()
+
+# vim: expandtab shiftwidth=4 tabstop=8 softtabstop=4 fileencoding=utf-8 textwidth=99
diff --git a/src/desktop-style.cpp b/src/desktop-style.cpp
index dee36d681..4c07c76ea 100644
--- a/src/desktop-style.cpp
+++ b/src/desktop-style.cpp
@@ -209,7 +209,7 @@ sp_desktop_set_style(Inkscape::ObjectSet *set, SPDesktop *desktop, SPCSSAttr *cs
if (!change)
return;
-// 2. Emit signal... See desktop->connectStyleSet in text-tool, tweak-tool, and gradient-drag.
+// 2. Emit signal... See desktop->connectSetStyle in text-tool, tweak-tool, and gradient-drag.
bool intercepted = desktop->_set_style_signal.emit(css);
/** \todo
@@ -1045,6 +1045,7 @@ objects_query_fontnumbers (const std::vector<SPItem*> &objects, SPStyle *style_r
bool lineheight_normal = false;
bool lineheight_unit_proportional = false;
bool lineheight_unit_absolute = false;
+ bool lineheight_set = false; // Set true if any object has lineheight set.
double size_prev = 0;
double letterspacing_prev = 0;
@@ -1130,6 +1131,9 @@ objects_query_fontnumbers (const std::vector<SPItem*> &objects, SPStyle *style_r
lineheight_normal = false;
lineheight += lineheight_current * doc_scale;
}
+ if (style->line_height.set) {
+ lineheight_set = true;
+ }
if ((size_prev != 0 && style->font_size.computed != size_prev) ||
(letterspacing_prev != 0 && style->letter_spacing.computed != letterspacing_prev) ||
@@ -1205,6 +1209,9 @@ objects_query_fontnumbers (const std::vector<SPItem*> &objects, SPStyle *style_r
}
}
+ // Used by text toolbar unset 'line-height'
+ style_res->line_height.set = lineheight_set;
+
if (texts > 1) {
if (different || different_lineheight) {
return QUERY_STYLE_MULTIPLE_AVERAGED;
@@ -1907,7 +1914,7 @@ sp_desktop_query_style_from_list (const std::vector<SPItem*> &list, SPStyle *sty
int
sp_desktop_query_style(SPDesktop *desktop, SPStyle *style, int property)
{
- // Used by text tool and in gradient dragging
+ // Used by text tool and in gradient dragging. See connectQueryStyle.
int ret = desktop->_query_style_signal.emit(style, property);
if (ret != QUERY_STYLE_NOTHING)
diff --git a/src/file.cpp b/src/file.cpp
index 55089209a..8f283e1f8 100644
--- a/src/file.cpp
+++ b/src/file.cpp
@@ -66,6 +66,10 @@
// For updating old Inkscape SVG files
#include "display/canvas-grid.h"
#include "sp-guide.h"
+#include "selection-chemistry.h"
+#include "persp3d.h"
+#include "proj_pt.h"
+#include "ui/shape-editor.h"
#include <gtkmm.h>
@@ -353,11 +357,13 @@ bool sp_file_open(const Glib::ustring &uri,
bool need_fix_units = false;
bool need_fix_guides = false;
bool need_fix_grid_mm = false;
- bool is_extension = false;
+ bool need_fix_box3d = false;
+ bool did_scaling = false;
// Check if potentially need viewbox or unit fix
switch (root->width.unit) {
case SP_CSS_UNIT_PC:
+ case SP_CSS_UNIT_PT:
case SP_CSS_UNIT_MM:
case SP_CSS_UNIT_CM:
case SP_CSS_UNIT_IN:
@@ -366,13 +372,19 @@ bool sp_file_open(const Glib::ustring &uri,
case SP_CSS_UNIT_NONE:
case SP_CSS_UNIT_PX:
need_fix_units = true;
- default:
break;
+ case SP_CSS_UNIT_EM:
+ case SP_CSS_UNIT_EX:
+ case SP_CSS_UNIT_PERCENT:
// OK
+ break;
+ default:
+ std::cerr << "sp_file_open: Unhandled width unit!" << std::endl;
}
switch (root->height.unit) {
case SP_CSS_UNIT_PC:
+ case SP_CSS_UNIT_PT:
case SP_CSS_UNIT_MM:
case SP_CSS_UNIT_CM:
case SP_CSS_UNIT_IN:
@@ -381,14 +393,21 @@ bool sp_file_open(const Glib::ustring &uri,
case SP_CSS_UNIT_NONE:
case SP_CSS_UNIT_PX:
need_fix_units = true;
- default:
break;
+ case SP_CSS_UNIT_EM:
+ case SP_CSS_UNIT_EX:
+ case SP_CSS_UNIT_PERCENT:
// OK
+ break;
+ default:
+ std::cerr << "sp_file_open: Unhandled height unit!" << std::endl;
}
// std::cout << "Absolute SVG units in root? " << (need_fix_viewbox?"true":"false") << std::endl;
// std::cout << "User units in root? " << (need_fix_units ?"true":"false") << std::endl;
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+
if (!root->viewBox_set && need_fix_viewbox) {
Glib::ustring msg = _(
@@ -401,31 +420,37 @@ bool sp_file_open(const Glib::ustring &uri,
Gtk::Label info;
info.set_markup(msg.c_str());
info.show();
-
scaleDialog.get_content_area()->pack_start(info, false, false, 20);
+
Gtk::CheckButton backupButton( _("Create backup file (in same directory).") );
- backupButton.set_active();
+ bool backup = prefs->getBool("/options/dpifixbackup", true);
+ backupButton.set_active( backup );
backupButton.show();
-
scaleDialog.get_content_area()->pack_start(backupButton, false, false, 20);
- scaleDialog.add_button("Set 'viewBox'", 1);
- scaleDialog.add_button("Scale elements", 2);
- scaleDialog.add_button("Ignore", 3);
+
+ scaleDialog.add_button(_("Set 'viewBox'"), 1);
+ scaleDialog.add_button(_("Scale elements"), 2);
+ scaleDialog.add_button(_("Ignore"), 3);
+ scaleDialog.add_button("Scale test - group", 4);
+ scaleDialog.add_button("Scale test - children", 5);
gint response = scaleDialog.run();
- bool backup = backupButton.get_active();
+ backup = backupButton.get_active();
+ prefs->setBool("/options/dpifixbackup", backup);
+
+ if ( backup && response != 3) {
+ sp_file_save_backup( uri );
+ }
+
if (response == 1) {
- if (backup) {
- sp_file_save_backup( uri );
- }
+
doc->setViewBox(Geom::Rect::from_xywh(
0, 0,
doc->getWidth().value("px") * ratio,
doc->getHeight().value("px") * ratio));
+
} else if (response == 2 ) {
- if (backup) {
- sp_file_save_backup( uri );
- }
+
std::list<Inkscape::Extension::Effect *> effects;
Inkscape::Extension::db.get_effect_list(effects);
std::list<Inkscape::Extension::Effect *>::iterator it = effects.begin();
@@ -442,12 +467,65 @@ bool sp_file_open(const Glib::ustring &uri,
if (!did) {
std::cerr << "sp_file_open: Failed to find dpi90to96 extension." << std::endl;
}
- is_extension = true;
+ did_scaling = true;
+
+ } else if (response == 4) {
+
+ // Save preferences
+ bool onlysensitive = prefs->getBool("/options/kbselection/onlysensitive",true);
+ bool onlyvisible = prefs->getBool("/options/kbselection/onlyvisible", true);
+
+ prefs->setBool("/options/kbselection/onlysensitive", false);
+ prefs->setBool("/options/kbselection/onlyvisible", false);
+
+ Inkscape::Selection *selection = desktop->getSelection();
+ Inkscape::SelectionHelper::selectAllInAll( desktop );
+ selection->group();
+ SPItem * group = selection->singleItem();
+ if (group) {
+ group->setAttribute("transform","scale(1.06666667,1.06666667)");
+ } else {
+ std::cerr << "sp_file_open: Failed to get group!" << std::endl;
+ }
+ selection->clear();
+ selection->add( group );
+ selection->ungroup();
+ selection->clear();
+
+ prefs->setBool("/options/kbselection/onlysensitive", onlysensitive);
+ prefs->setBool("/options/kbselection/onlyvisible", onlyvisible );
+
+ did_scaling = true;
+
+ } else if (response == 5) {
+
+ // Save preferences
+ bool transform_stroke = prefs->getBool("/options/transform/stroke", true);
+ bool transform_rectcorners = prefs->getBool("/options/transform/rectcorners", true);
+ bool transform_pattern = prefs->getBool("/options/transform/pattern", true);
+ bool transform_gradient = prefs->getBool("/options/transform/gradient", true);
+
+ prefs->setBool("/options/transform/stroke", true);
+ prefs->setBool("/options/transform/rectcorners", true);
+ prefs->setBool("/options/transform/pattern", true);
+ prefs->setBool("/options/transform/gradient", true);
+
+ Inkscape::UI::ShapeEditor::blockSetItem(true);
+ doc->getRoot()->scaleChildItemsRec(Geom::Scale(1/ratio),Geom::Point(0, 0), false);
+ Inkscape::UI::ShapeEditor::blockSetItem(false);
+
+ // Restore preferences
+ prefs->setBool("/options/transform/stroke", transform_stroke);
+ prefs->setBool("/options/transform/rectcorners", transform_rectcorners);
+ prefs->setBool("/options/transform/pattern", transform_pattern);
+ prefs->setBool("/options/transform/gradient", transform_gradient);
}
+
+ need_fix_box3d = true;
need_fix_guides = true; // Always fix guides
}
- if (need_fix_units) {
+ else if (need_fix_units) {
Glib::ustring msg = (
"Old Inkscape files use 1in == 90px. CSS requires 1in == 96px.\n"
"Drawings meant to match a physical size (e.g. Letter or A4)\n"
@@ -462,19 +540,27 @@ bool sp_file_open(const Glib::ustring &uri,
scaleDialog.get_content_area()->pack_start(info, false, false, 20);
Gtk::CheckButton backupButton( _("Create backup file (in same directory).") );
- scaleDialog.get_content_area()->pack_start(backupButton, false, false, 20);
+ bool backup = prefs->getBool("/options/dpifixbackup", true);
+ backupButton.set_active( backup );
backupButton.show();
+ scaleDialog.get_content_area()->pack_start(backupButton, false, false, 20);
- scaleDialog.add_button("Set 'viewBox'", 1);
- scaleDialog.add_button("Scale elements", 2);
- scaleDialog.add_button("Ignore", 3);
+ scaleDialog.add_button(_("Set 'viewBox'"), 1);
+ scaleDialog.add_button(_("Scale elements"), 2);
+ scaleDialog.add_button(_("Ignore"), 3);
+ scaleDialog.add_button("Scale test - group", 4);
+ scaleDialog.add_button("Scale test - children", 5);
gint response = scaleDialog.run();
- bool backup = backupButton.get_active();
+ backup = backupButton.get_active();
+ prefs->setBool("/options/dpifixbackup", backup);
+
+ if ( backup && response != 3) {
+ sp_file_save_backup( uri );
+ }
+
if (response == 1) {
- if (backup) {
- sp_file_save_backup( uri );
- }
+
if (!root->viewBox_set) {
doc->setViewBox(Geom::Rect::from_xywh(
0, 0,
@@ -488,10 +574,10 @@ bool sp_file_open(const Glib::ustring &uri,
doc->setWidthAndHeight( width, height, false );
need_fix_guides = true; // Only fix guides if drawing scaled
+ need_fix_box3d = true;
+
} else if (response == 2) {
- if (backup) {
- sp_file_save_backup( uri );
- }
+
std::list<Inkscape::Extension::Effect *> effects;
Inkscape::Extension::db.get_effect_list(effects);
std::list<Inkscape::Extension::Effect *>::iterator it = effects.begin();
@@ -509,14 +595,94 @@ bool sp_file_open(const Glib::ustring &uri,
std::cerr << "sp_file_open: Failed to find dpi90to96 extension." << std::endl;
}
need_fix_guides = true; // Only fix guides if drawing scaled
- is_extension = true;
+ did_scaling = true;
+
+ } else if (response == 4) {
+
+ Inkscape::Util::Quantity width =
+ Inkscape::Util::Quantity(doc->getWidth().value("px")/ratio, "px" );
+ Inkscape::Util::Quantity height =
+ Inkscape::Util::Quantity(doc->getHeight().value("px")/ratio,"px" );
+ doc->setWidthAndHeight( width, height, false );
+
+ if (!root->viewBox_set) {
+
+ // Save preferences
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ bool onlysensitive = prefs->getBool("/options/kbselection/onlysensitive",true);
+ bool onlyvisible = prefs->getBool("/options/kbselection/onlyvisible", true);
+
+ prefs->setBool("/options/kbselection/onlysensitive", false);
+ prefs->setBool("/options/kbselection/onlyvisible", false);
+
+ Inkscape::Selection *selection = desktop->getSelection();
+ Inkscape::SelectionHelper::selectAllInAll( desktop );
+ selection->group();
+ SPItem * group = selection->singleItem();
+ if (group) {
+ group->setAttribute("transform","scale(1.06666667,1.06666667)");
+ } else {
+ std::cerr << "sp_file_open: Failed to get group!" << std::endl;
+ }
+ selection->clear();
+ selection->add( group );
+ selection->ungroup();
+ selection->clear();
+
+ prefs->setBool("/options/kbselection/onlysensitive", onlysensitive);
+ prefs->setBool("/options/kbselection/onlyvisible", onlyvisible );
+
+ did_scaling = true;
+ }
+
+ need_fix_box3d = true;
+ need_fix_guides = true; // Only fix guides if drawing scaled
+
+ } else if (response == 5) {
+
+ Inkscape::Util::Quantity width =
+ Inkscape::Util::Quantity(doc->getWidth().value("px")/ratio, "px" );
+ Inkscape::Util::Quantity height =
+ Inkscape::Util::Quantity(doc->getHeight().value("px")/ratio,"px" );
+ doc->setWidthAndHeight( width, height, false );
+
+ if (!root->viewBox_set) {
+
+ // Save preferences
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ bool transform_stroke = prefs->getBool("/options/transform/stroke", true);
+ bool transform_rectcorners = prefs->getBool("/options/transform/rectcorners", true);
+ bool transform_pattern = prefs->getBool("/options/transform/pattern", true);
+ bool transform_gradient = prefs->getBool("/options/transform/gradient", true);
+
+ prefs->setBool("/options/transform/stroke", true);
+ prefs->setBool("/options/transform/rectcorners", true);
+ prefs->setBool("/options/transform/pattern", true);
+ prefs->setBool("/options/transform/gradient", true);
+
+ Inkscape::UI::ShapeEditor::blockSetItem(true);
+ doc->getRoot()->scaleChildItemsRec(Geom::Scale(1/ratio),Geom::Point(0, 0), false);
+ Inkscape::UI::ShapeEditor::blockSetItem(false);
+
+ // Restore preferences
+ prefs->setBool("/options/transform/stroke", transform_stroke);
+ prefs->setBool("/options/transform/rectcorners", transform_rectcorners);
+ prefs->setBool("/options/transform/pattern", transform_pattern);
+ prefs->setBool("/options/transform/gradient", transform_gradient);
+
+ did_scaling = true;
+ }
+
+ need_fix_box3d = true;
+ need_fix_guides = true; // Only fix guides if drawing scaled
+
} else {
// Ignore
need_fix_grid_mm = true;
}
}
- // Fix guides and grids
+ // Fix guides and grids and perspective
for (SPObject *child = root->firstChild() ; child; child = child->getNext() ) {
SPNamedView *nv = dynamic_cast<SPNamedView *>(child);
if (nv) {
@@ -555,21 +721,53 @@ bool sp_file_open(const Glib::ustring &uri,
}
} else {
if (need_fix_guides) {
- // HACK: Scaling the document does not seem to cause
- // grids defined in document units to be updated.
- // This forces an update.
- if(is_extension){
+ if(did_scaling){
xy->Scale( Geom::Scale(ratio,ratio).inverse() );
} else {
+ // HACK: Scaling the document does not seem to cause
+ // grids defined in document units to be updated.
+ // This forces an update.
xy->Scale( Geom::Scale(1,1) );
}
}
}
}
}
+ } // If SPNamedView
+
+ SPDefs *defs = dynamic_cast<SPDefs *>(child);
+ if (defs && need_fix_box3d) {
+ for (SPObject *child = defs->firstChild() ; child; child = child->getNext() ) {
+ Persp3D* persp3d = dynamic_cast<Persp3D *>(child);
+ if (persp3d) {
+ std::vector<Glib::ustring> tokens;
+
+ const gchar* vp_x = persp3d->getAttribute("inkscape:vp_x");
+ const gchar* vp_y = persp3d->getAttribute("inkscape:vp_y");
+ const gchar* vp_z = persp3d->getAttribute("inkscape:vp_z");
+ const gchar* vp_o = persp3d->getAttribute("inkscape:persp3d-origin");
+ // std::cout << "Found Persp3d: "
+ // << " vp_x: " << vp_x
+ // << " vp_y: " << vp_y
+ // << " vp_z: " << vp_z << std::endl;
+ Proj::Pt2 pt_x (vp_x);
+ Proj::Pt2 pt_y (vp_y);
+ Proj::Pt2 pt_z (vp_z);
+ Proj::Pt2 pt_o (vp_o);
+ pt_x = pt_x * (1.0/ratio);
+ pt_y = pt_y * (1.0/ratio);
+ pt_z = pt_z * (1.0/ratio);
+ pt_o = pt_o * (1.0/ratio);
+ persp3d->setAttribute("inkscape:vp_x",pt_x.coord_string());
+ persp3d->setAttribute("inkscape:vp_y",pt_y.coord_string());
+ persp3d->setAttribute("inkscape:vp_z",pt_z.coord_string());
+ persp3d->setAttribute("inkscape:persp3d-origin",pt_o.coord_string());
+ }
+ }
}
- } // Look for SPNamedView loop
+ } // Look for SPNamedView and SPDefs loop
+ // desktop->getDocument()->ensureUpToDate(); // Does not update box3d!
DocumentUndo::done(desktop->getDocument(), SP_VERB_NONE, _("Update Document"));
} // If old Inkscape version
diff --git a/src/sp-item-group.cpp b/src/sp-item-group.cpp
index 96dcdbe30..7b2507b5e 100644
--- a/src/sp-item-group.cpp
+++ b/src/sp-item-group.cpp
@@ -584,12 +584,13 @@ sp_item_group_ungroup (SPGroup *group, std::vector<SPItem*> &children, bool do_d
SPText * text = dynamic_cast<SPText *>(citem);
if (text) {
//this causes a change in text-on-path appearance when there is a non-conformal transform, see bug #1594565
- double scale = (ctrans.expansionX() + ctrans.expansionY()) / 2.0;
SPTextPath * text_path = dynamic_cast<SPTextPath *>(text->firstChild());
if (!text_path) {
nrepr->setAttribute("transform", affinestr);
} else {
- sp_recursive_scale_text_size(nrepr, scale);
+ // The following breaks roundtripping group -> ungroup
+ // double scale = (ctrans.expansionX() + ctrans.expansionY()) / 2.0;
+ // sp_recursive_scale_text_size(nrepr, scale);
Geom::Affine ttrans = ctrans.inverse() * SP_ITEM(text)->transform * ctrans;
gchar *affinestr = sp_svg_transform_write(ttrans);
nrepr->setAttribute("transform", affinestr);
diff --git a/src/sp-mesh-array.cpp b/src/sp-mesh-array.cpp
index b522d577b..f192d0e44 100644
--- a/src/sp-mesh-array.cpp
+++ b/src/sp-mesh-array.cpp
@@ -1198,7 +1198,7 @@ void SPMeshNodeArray::create( SPMeshGradient *mg, SPItem *item, Geom::OptRect bb
ry = arc->ry.computed;
start = arc->start;
end = arc->end;
- if( end == start ) {
+ if( end <= start ) {
end += 2.0 * M_PI;
}
}
diff --git a/src/ui/tools/tweak-tool.cpp b/src/ui/tools/tweak-tool.cpp
index 56939cc30..ff5d623c2 100644
--- a/src/ui/tools/tweak-tool.cpp
+++ b/src/ui/tools/tweak-tool.cpp
@@ -1084,7 +1084,8 @@ sp_tweak_dilate (TweakTool *tc, Geom::Point event_p, Geom::Point p, Geom::Point
double move_force = get_move_force(tc);
double color_force = MIN(sqrt(path_force)/20.0, 1);
- auto items= selection->items();
+// auto items= selection->items();
+ std::vector<SPItem*> items(selection->items().begin(), selection->items().end());
for(auto i=items.begin();i!=items.end(); ++i){
SPItem *item = *i;
diff --git a/src/widgets/text-toolbar.cpp b/src/widgets/text-toolbar.cpp
index 847ebd8c1..784c467f1 100644
--- a/src/widgets/text-toolbar.cpp
+++ b/src/widgets/text-toolbar.cpp
@@ -239,7 +239,7 @@ static void sp_text_fontsize_value_changed( Ink_ComboBoxEntry_Action *act, GObje
if (dynamic_cast<SPText *>(*i) || dynamic_cast<SPFlowtext *>(*i)) {
SPItem *item = *i;
- // Scale by inverse of accumulated paraent transform
+ // Scale by inverse of accumulated parent transform
SPCSSAttr *css_set = sp_repr_css_attr_new();
sp_repr_css_merge(css_set, css);
Geom::Affine const local(item->i2doc_affine());
@@ -321,6 +321,29 @@ static void sp_text_outer_style_changed( InkToggleAction*act, GObject *tbl )
sp_text_toolbox_selection_changed( NULL, tbl );
}
+// Unset line height on selection's inner text objects (tspan, etc.).
+static void sp_text_lineheight_unset_changed( InkToggleAction*act, GObject *tbl )
+{
+ // quit if run by the _changed callbacks
+ if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
+ return;
+ }
+ g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
+
+ SPCSSAttr *css = sp_repr_css_attr_new();
+ sp_repr_css_unset_property(css, "line-height");
+
+ SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+ sp_desktop_set_style (desktop, css);
+
+ sp_repr_css_attr_unref(css);
+
+ DocumentUndo::done(desktop->getDocument(), SP_VERB_CONTEXT_TEXT,
+ _("Text: Unset line height."));
+
+ g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
+}
+
// Handles both Superscripts and Subscripts
static void sp_text_script_changed( InkToggleAction* act, GObject *tbl )
{
@@ -599,7 +622,7 @@ static void sp_text_lineheight_value_changed( GtkAdjustment *adj, GObject *tbl )
if (dynamic_cast<SPText *>(*i) || dynamic_cast<SPFlowtext *>(*i)) {
SPItem *item = *i;
- // Scale by inverse of accumulated paraent transform
+ // Scale by inverse of accumulated parent transform
SPCSSAttr *css_set = sp_repr_css_attr_new();
sp_repr_css_merge(css_set, css);
Geom::Affine const local(item->i2doc_affine());
@@ -1405,7 +1428,13 @@ static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/
}
// Save unit so we can do convertions between new/old units.
g_object_set_data( tbl, "lineheight_unit", GINT_TO_POINTER(line_height_unit));
-
+
+ // Enable and turn on only if selection includes an object with line height set.
+ InkToggleAction* lineHeightUnset =
+ INK_TOGGLE_ACTION( g_object_get_data( tbl, "TextLineHeightUnsetAction"));
+ gtk_action_set_sensitive(GTK_ACTION(lineHeightUnset), query.line_height.set );
+ gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(lineHeightUnset), query.line_height.set );
+
// Word spacing
double wordSpacing;
if (query.word_spacing.normal) wordSpacing = 0.0;
@@ -2100,6 +2129,19 @@ void sp_text_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObje
g_object_set( G_OBJECT(eact), "iconId", "text_rotation", NULL );
}
+ /* Text line height unset */
+ {
+ InkToggleAction* act = ink_toggle_action_new( "TextLineHeightUnsetAction", // Name
+ _("Unset line height"), // Label
+ _("If enabled, line height is set on part of selection. Click to unset."),
+ INKSCAPE_ICON("paint-unknown"),
+ secondarySize ); // Icon size
+ gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
+ g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_text_lineheight_unset_changed), holder );
+ gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/text/line_height_unset", false) );
+ g_object_set_data( holder, "TextLineHeightUnsetAction", act );
+ }
+
/* Text outer style */
{
InkToggleAction* act = ink_toggle_action_new( "TextOuterStyleAction", // Name
diff --git a/src/widgets/toolbox.cpp b/src/widgets/toolbox.cpp
index 1f6cb136c..a3db3c33d 100644
--- a/src/widgets/toolbox.cpp
+++ b/src/widgets/toolbox.cpp
@@ -514,6 +514,7 @@ static gchar const * ui_descr =
" <toolitem action='TextFontSizeAction' />"
" <toolitem action='TextLineHeightAction' />"
" <toolitem action='TextLineHeightUnitsAction' />"
+ " <toolitem action='TextLineHeightUnsetAction' />"
" <separator />"
" <toolitem action='TextAlignAction' />"
" <separator />"