summaryrefslogtreecommitdiffstats
path: root/src/util/units.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/util/units.cpp')
-rw-r--r--src/util/units.cpp384
1 files changed, 229 insertions, 155 deletions
diff --git a/src/util/units.cpp b/src/util/units.cpp
index d1275b082..7bc910fcc 100644
--- a/src/util/units.cpp
+++ b/src/util/units.cpp
@@ -1,12 +1,26 @@
+/*
+ * Inkscape Units
+ *
+ * Authors:
+ * Matthew Petroff <matthew@mpetroff.net>
+ *
+ * Copyright (C) 2013 Matthew Petroff
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <cmath>
#include <cerrno>
+#include <iomanip>
#include <glib.h>
+#include <glibmm/regex.h>
+#include <glibmm/fileutils.h>
+#include <glibmm/markup.h>
-#include "io/simple-sax.h"
#include "util/units.h"
#include "path-prefix.h"
#include "streq.h"
@@ -47,27 +61,31 @@ std::map<Glib::ustring, Inkscape::Util::UnitType> &getTypeMappings()
namespace Inkscape {
namespace Util {
-class UnitsSAXHandler : public Inkscape::IO::FlatSaxHandler
+class UnitParser : public Glib::Markup::Parser
{
public:
- UnitsSAXHandler(UnitTable *table);
- virtual ~UnitsSAXHandler() {}
+ typedef Glib::Markup::Parser::AttributeMap AttrMap;
+ typedef Glib::Markup::ParseContext Ctx;
+
+ UnitParser(UnitTable *table);
+ virtual ~UnitParser() {}
- virtual void _startElement(xmlChar const *name, xmlChar const **attrs);
- virtual void _endElement(xmlChar const *name);
+protected:
+ virtual void on_start_element(Ctx &ctx, Glib::ustring const &name, AttrMap const &attrs);
+ virtual void on_end_element(Ctx &ctx, Glib::ustring const &name);
+ virtual void on_text(Ctx &ctx, Glib::ustring const &text);
+public:
UnitTable *tbl;
bool primary;
bool skip;
Unit unit;
};
-UnitsSAXHandler::UnitsSAXHandler(UnitTable *table) :
- FlatSaxHandler(),
+UnitParser::UnitParser(UnitTable *table) :
tbl(table),
- primary(0),
- skip(0),
- unit()
+ primary(false),
+ skip(false)
{
}
@@ -103,7 +121,8 @@ void Unit::clear()
*this = Unit();
}
-int Unit::defaultDigits() const {
+int Unit::defaultDigits() const
+{
int factor_digits = int(log10(factor));
if (factor_digits < 0) {
g_warning("factor = %f, factor_digits = %d", factor, factor_digits);
@@ -113,30 +132,87 @@ int Unit::defaultDigits() const {
return factor_digits;
}
+bool Unit::compatibleWith(const Unit &u) const
+{
+ // Percentages
+ if (type == UNIT_TYPE_DIMENSIONLESS || u.type == UNIT_TYPE_DIMENSIONLESS) {
+ return true;
+ }
+
+ // Other units with same type
+ if (type == u.type) {
+ return true;
+ }
+
+ // Different, incompatible types
+ return false;
+}
+bool Unit::compatibleWith(const Glib::ustring u) const
+{
+ static UnitTable unit_table;
+ return compatibleWith(unit_table.getUnit(u));
+}
+
+bool operator== (const Unit &u1, const Unit &u2)
+{
+ return (u1.type == u2.type && u1.name.compare(u2.name) == 0);
+}
+
+bool operator!= (const Unit &u1, const Unit &u2)
+{
+ return !(u1 == u2);
+}
+
+int Unit::svgUnit() const
+{
+ if (!abbr.compare("px"))
+ return 1;
+ if (!abbr.compare("pt"))
+ return 2;
+ if (!abbr.compare("pc"))
+ return 3;
+ if (!abbr.compare("mm"))
+ return 4;
+ if (!abbr.compare("cm"))
+ return 5;
+ if (!abbr.compare("in"))
+ return 6;
+ if (!abbr.compare("ft"))
+ return 7;
+ if (!abbr.compare("em"))
+ return 8;
+ if (!abbr.compare("ex"))
+ return 9;
+ if (!abbr.compare("%"))
+ return 10;
+ return 0;
+}
+
UnitTable::UnitTable()
{
- // if we swich to the xml file, don't forget to force locale to 'C'
- // load("share/ui/units.xml"); // <-- Buggy
- gchar *filename = g_build_filename(INKSCAPE_UIDIR, "units.txt", NULL);
- loadText(filename);
+ gchar *filename = g_build_filename(INKSCAPE_UIDIR, "units.xml", NULL);
+ load(filename);
g_free(filename);
}
-UnitTable::~UnitTable() {
+UnitTable::~UnitTable()
+{
for (UnitMap::iterator iter = _unit_map.begin(); iter != _unit_map.end(); ++iter)
{
delete (*iter).second;
}
}
-void UnitTable::addUnit(Unit const &u, bool primary) {
+void UnitTable::addUnit(Unit const &u, bool primary)
+{
_unit_map[u.abbr] = new Unit(u);
if (primary) {
_primary_unit[u.type] = u.abbr;
}
}
-Unit UnitTable::getUnit(Glib::ustring const &unit_abbr) const {
+Unit UnitTable::getUnit(Glib::ustring const &unit_abbr) const
+{
UnitMap::const_iterator iter = _unit_map.find(unit_abbr);
if (iter != _unit_map.end()) {
return *((*iter).second);
@@ -145,7 +221,29 @@ Unit UnitTable::getUnit(Glib::ustring const &unit_abbr) const {
}
}
-bool UnitTable::deleteUnit(Unit const &u) {
+Quantity UnitTable::getQuantity(Glib::ustring const& q) const
+{
+ Glib::MatchInfo match_info;
+
+ // Extract value
+ double value = 0;
+ Glib::RefPtr<Glib::Regex> value_regex = Glib::Regex::create("[-+]*[\\d+]*\\.*[\\d+]*[eE]*[-+]*\\d+");
+ if (value_regex->match(q, match_info)) {
+ value = atof(match_info.fetch(0).c_str());
+ }
+
+ // Extract unit abbreviation
+ Glib::ustring abbr;
+ Glib::RefPtr<Glib::Regex> unit_regex = Glib::Regex::create("[A-z%]+");
+ if (unit_regex->match(q, match_info)) {
+ abbr = match_info.fetch(0);
+ }
+
+ return Quantity(value, abbr);
+}
+
+bool UnitTable::deleteUnit(Unit const &u)
+{
bool deleted = false;
// Cannot delete the primary unit type since it's
// used for conversions
@@ -183,172 +281,148 @@ Glib::ustring UnitTable::primary(UnitType type) const
return _primary_unit[type];
}
-bool UnitTable::loadText(Glib::ustring const &filename)
-{
- char buf[BUFSIZE] = {0};
-
- // Open file for reading
- FILE * f = fopen(filename.c_str(), "r");
- if (f == NULL) {
- g_warning("Could not open units file '%s': %s\n",
- filename.c_str(), strerror(errno));
- g_warning("* INKSCAPE_DATADIR is: '%s'\n", INKSCAPE_DATADIR);
- g_warning("* INKSCAPE_UIDIR is: '%s'\n", INKSCAPE_UIDIR);
- return false;
- }
-
- /** @todo fix this to use C++ means and explicit locale to avoid need to change. */
- // bypass current locale in order to make
- // sscanf read floats with '.' as a separator
- // set locale to 'C' and keep old locale
- char *old_locale = g_strdup(setlocale(LC_NUMERIC, NULL));
- setlocale (LC_NUMERIC, "C");
-
- while (fgets(buf, BUFSIZE, f) != NULL) {
- char name[BUFSIZE] = {0};
- char plural[BUFSIZE] = {0};
- char abbr[BUFSIZE] = {0};
- char type[BUFSIZE] = {0};
- double factor = 0.0;
- char primary[BUFSIZE] = {0};
-
- int nchars = 0;
- // locale is set to C, scanning %lf should work _everywhere_
- /** @todo address %15n, which causes a warning: */
- if (sscanf(buf, "%15s %15s %15s %15s %8lf %1s %15n",
- name, plural, abbr, type, &factor, primary, &nchars) != 6)
- {
- // Skip the line - doesn't appear to be valid
- continue;
- }
-
- g_assert(nchars < BUFSIZE);
-
- char *desc = buf;
- desc += nchars; // buf is now only the description
-
- // insert into _unit_map
- if (getTypeMappings().find(type) == getTypeMappings().end())
- {
- g_warning("Skipping unknown unit type '%s' for %s.\n", type, name);
- continue;
- }
- UnitType utype = getTypeMappings()[type];
-
- Unit u(utype, factor, name, plural, abbr, desc);
-
- // if primary is 'Y', list this unit as a primary
- addUnit(u, (primary[0]=='Y' || primary[0]=='y'));
- }
-
- // set back the saved locale
- setlocale (LC_NUMERIC, old_locale);
- g_free (old_locale);
-
- // close file
- if (fclose(f) != 0) {
- g_warning("Error closing units file '%s': %s\n", filename.c_str(), strerror(errno));
- return false;
- }
-
- return true;
-}
-
-bool UnitTable::load(Glib::ustring const &filename) {
- UnitsSAXHandler handler(this);
+bool UnitTable::load(std::string const &filename) {
+ UnitParser uparser(this);
+ Glib::Markup::ParseContext ctx(uparser);
- int result = handler.parseFile( filename.c_str() );
- if ( result != 0 ) {
- // perhaps
- g_warning("Problem loading units file '%s': %d\n", filename.c_str(), result);
+ try {
+ Glib::ustring unitfile = Glib::file_get_contents(filename);
+ ctx.parse(unitfile);
+ ctx.end_parse();
+ } catch (Glib::MarkupError const &e) {
+ g_warning("Problem loading units file '%s': %s\n", filename.c_str(), e.what().c_str());
return false;
}
-
return true;
}
-bool UnitTable::save(Glib::ustring const &filename) {
+bool UnitTable::save(std::string const &filename) {
- // open file for writing
- FILE *f = fopen(filename.c_str(), "w");
- if (f == NULL) {
- g_warning("Could not open units file '%s': %s\n", filename.c_str(), strerror(errno));
- return false;
- }
-
- // write out header
- // foreach item in _unit_map, sorted alphabetically by type and then unit name
- // sprintf a line
- // name
- // name_plural
- // abbr
- // type
- // factor
- // PRI - if listed in primary unit table, 'Y', else 'N'
- // description
- // write line to the file
-
- // close file
- if (fclose(f) != 0) {
- g_warning("Error closing units file '%s': %s\n", filename.c_str(), strerror(errno));
- return false;
- }
+ g_warning("UnitTable::save(): not implemented");
return true;
}
+Inkscape::Util::UnitTable unit_table;
-void UnitsSAXHandler::_startElement(xmlChar const *name, xmlChar const **attrs)
+void UnitParser::on_start_element(Ctx &ctx, Glib::ustring const &name, AttrMap const &attrs)
{
- if (streq("unit", (char const *)name)) {
+ if (name == "unit") {
// reset for next use
unit.clear();
primary = false;
skip = false;
- for ( int i = 0; attrs[i]; i += 2 ) {
- char const *const key = (char const *)attrs[i];
- if (streq("type", key)) {
- char const *type = (char const*)attrs[i+1];
- if (getTypeMappings().find(type) != getTypeMappings().end())
- {
- unit.type = getTypeMappings()[type];
- } else {
- g_warning("Skipping unknown unit type '%s' for %s.\n", type, name);
- skip = true;
- }
- } else if (streq("pri", key)) {
- primary = attrs[i+1][0] == 'y' || attrs[i+1][0] == 'Y';
+ AttrMap::const_iterator f;
+ if ((f = attrs.find("type")) != attrs.end()) {
+ Glib::ustring type = f->second;
+ if (getTypeMappings().find(type) != getTypeMappings().end()) {
+ unit.type = getTypeMappings()[type];
+ } else {
+ g_warning("Skipping unknown unit type '%s'.\n", type.c_str());
+ skip = true;
}
}
+ if ((f = attrs.find("pri")) != attrs.end()) {
+ primary = (f->second[0] == 'y' || f->second[0] == 'Y');
+ }
}
}
-void UnitsSAXHandler::_endElement(xmlChar const *xname)
+void UnitParser::on_text(Ctx &ctx, Glib::ustring const &text)
{
- char const *const name = (char const *) xname;
- if (streq("name", name)) {
- unit.name = data;
- } else if (streq("plural", name)) {
- unit.name_plural = data;
- } else if (streq("abbr", name)) {
- unit.abbr = data;
- } else if (streq("factor", name)) {
+ Glib::ustring element = ctx.get_element();
+ if (element == "name") {
+ unit.name = text;
+ } else if (element == "plural") {
+ unit.name_plural = text;
+ } else if (element == "abbr") {
+ unit.abbr = text;
+ } else if (element == "factor") {
// TODO make sure we use the right conversion
- unit.factor = atol(data.c_str());
- } else if (streq("description", name)) {
- unit.description = data;
- } else if (streq("unit", name)) {
- if (!skip) {
- tbl->addUnit(unit, primary);
- }
+ unit.factor = g_ascii_strtod(text.c_str(), NULL);
+ } else if (element == "description") {
+ unit.description = text;
+ }
+}
+
+void UnitParser::on_end_element(Ctx &ctx, Glib::ustring const &name)
+{
+ if (name == "unit" && !skip) {
+ tbl->addUnit(unit, primary);
}
}
+Quantity::Quantity(double q, const Unit &u)
+{
+ unit = new Unit(u);
+ quantity = q;
+}
+Quantity::Quantity(double q, const Glib::ustring u)
+{
+ unit = new Unit(unit_table.getUnit(u));
+ quantity = q;
+}
+
+bool Quantity::compatibleWith(const Unit &u) const
+{
+ return unit->compatibleWith(u);
+}
+bool Quantity::compatibleWith(const Glib::ustring u) const
+{
+ return compatibleWith(unit_table.getUnit(u));
+}
+
+double Quantity::value(const Unit &u) const
+{
+ return convert(quantity, *unit, u);
+}
+double Quantity::value(const Glib::ustring u) const
+{
+ return value(unit_table.getUnit(u));
+}
+
+Glib::ustring Quantity::string(const Unit &u) const {
+ return Glib::ustring::format(std::fixed, std::setprecision(2), value(u)) + " " + unit->abbr;
+}
+Glib::ustring Quantity::string(const Glib::ustring u) const {
+ return string(unit_table.getUnit(u));
+}
+Glib::ustring Quantity::string() const {
+ return string(*unit);
+}
+
+double Quantity::convert(const double from_dist, const Unit &from, const Unit &to)
+{
+ // Percentage
+ if (to.type == UNIT_TYPE_DIMENSIONLESS) {
+ return from_dist * to.factor;
+ }
+
+ // Incompatible units
+ if (from.type != to.type) {
+ return -1;
+ }
+
+ // Compatible units
+ return from_dist * from.factor / to.factor;
+}
+double Quantity::convert(const double from_dist, const Glib::ustring from, const Unit &to)
+{
+ return convert(from_dist, unit_table.getUnit(from), to);
+}
+double Quantity::convert(const double from_dist, const Unit &from, const Glib::ustring to)
+{
+ return convert(from_dist, from, unit_table.getUnit(to));
+}
+double Quantity::convert(const double from_dist, const Glib::ustring from, const Glib::ustring to)
+{
+ return convert(from_dist, unit_table.getUnit(from), unit_table.getUnit(to));
+}
+
} // namespace Util
} // namespace Inkscape
-
/*
Local Variables:
mode:c++