diff options
| author | su_v <suv-sf@users.sourceforge.net> | 2013-08-29 21:06:10 +0000 |
|---|---|---|
| committer | ~suv <suv-sf@users.sourceforge.net> | 2013-08-29 21:06:10 +0000 |
| commit | 4d331e73a76dce7d703716093923ca01b3cc5936 (patch) | |
| tree | b444657ba269b25f60684e66858a138b74fe240d /src/util/units.cpp | |
| parent | Fix compiler warnings (diff) | |
| parent | Updating outdated test. Fixes bug #1202271. (diff) | |
| download | inkscape-4d331e73a76dce7d703716093923ca01b3cc5936.tar.gz inkscape-4d331e73a76dce7d703716093923ca01b3cc5936.zip | |
merge from trunk (r12487)
(bzr r11668.1.75)
Diffstat (limited to 'src/util/units.cpp')
| -rw-r--r-- | src/util/units.cpp | 549 |
1 files changed, 317 insertions, 232 deletions
diff --git a/src/util/units.cpp b/src/util/units.cpp index f822d01de..7bc910fcc 100644 --- a/src/util/units.cpp +++ b/src/util/units.cpp @@ -1,343 +1,428 @@ +/* + * 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" +using Inkscape::Util::UNIT_TYPE_DIMENSIONLESS; +using Inkscape::Util::UNIT_TYPE_LINEAR; +using Inkscape::Util::UNIT_TYPE_RADIAL; +using Inkscape::Util::UNIT_TYPE_FONT_HEIGHT; + +namespace +{ + +/** + * A std::map that gives the data type value for the string version. + * + * Note that we'd normally not return a reference to an internal version, but + * for this constant case it allows us to check against getTypeMappings().end(). + */ +/** @todo consider hiding map behind hasFoo() and getFoo() type functions.*/ +std::map<Glib::ustring, Inkscape::Util::UnitType> &getTypeMappings() +{ + static bool init = false; + static std::map<Glib::ustring, Inkscape::Util::UnitType> typeMap; + if (!init) + { + init = true; + typeMap["DIMENSIONLESS"] = UNIT_TYPE_DIMENSIONLESS; + typeMap["LINEAR"] = UNIT_TYPE_LINEAR; + typeMap["RADIAL"] = UNIT_TYPE_RADIAL; + typeMap["FONT_HEIGHT"] = UNIT_TYPE_FONT_HEIGHT; + // Note that code was not yet handling LINEAR_SCALED, TIME, QTY and NONE + } + return typeMap; +} + +} // namespace + 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; - virtual void _startElement(xmlChar const *name, xmlChar const **attrs); - virtual void _endElement(xmlChar const *name); + UnitParser(UnitTable *table); + virtual ~UnitParser() {} +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) { } #define BUFSIZE (255) -/** - * Returns the suggested precision to use for displaying numbers - * of this unit. - */ -int Unit::defaultDigits() const { +Unit::Unit() : + type(UNIT_TYPE_DIMENSIONLESS), // should this or NONE be the default? + factor(1.0), + name(), + name_plural(), + abbr(), + description() +{ +} + +Unit::Unit(UnitType type, + double factor, + Glib::ustring const &name, + Glib::ustring const &name_plural, + Glib::ustring const &abbr, + Glib::ustring const &description) : + type(type), + factor(factor), + name(name), + name_plural(name_plural), + abbr(abbr), + description(description) +{ +} + +void Unit::clear() +{ + *this = Unit(); +} + +int Unit::defaultDigits() const +{ int factor_digits = int(log10(factor)); if (factor_digits < 0) { g_warning("factor = %f, factor_digits = %d", factor, factor_digits); g_warning("factor_digits < 0 - returning 0"); - return 0; - } else { - return factor_digits; + factor_digits = 0; } + return factor_digits; } -/** - * Initializes the unit tables and identifies the primary unit types. - * - * The primary unit's conversion factor is required to be 1.00 - */ -UnitTable::UnitTable() +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) { - // 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); + 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() +{ + gchar *filename = g_build_filename(INKSCAPE_UIDIR, "units.xml", NULL); + load(filename); g_free(filename); } -UnitTable::~UnitTable() { - UnitMap::iterator iter = _unit_map.begin(); - while (iter != _unit_map.end()) { - delete (*iter).second; - ++iter; +UnitTable::~UnitTable() +{ + for (UnitMap::iterator iter = _unit_map.begin(); iter != _unit_map.end(); ++iter) + { + delete (*iter).second; } } -/** Add a new unit to the table */ -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; + _primary_unit[u.type] = u.abbr; } } -/** Retrieve a given unit based on its string identifier */ -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); + return *((*iter).second); } else { - return Unit(); + return Unit(); } } -/** Remove a unit definition from the given unit type table */ -bool UnitTable::deleteUnit(Unit const &u) { - if (u.abbr == _primary_unit[u.type]) { - // Cannot delete the primary unit type since it's - // used for conversions - return false; +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()); } - UnitMap::iterator iter = _unit_map.find(u.abbr); - if (iter != _unit_map.end()) { - delete (*iter).second; - _unit_map.erase(iter); - return true; - } else { - return false; + + // 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 + if (u.abbr != _primary_unit[u.type]) { + UnitMap::iterator iter = _unit_map.find(u.abbr); + if (iter != _unit_map.end()) { + delete (*iter).second; + _unit_map.erase(iter); + deleted = true; + } + } + return deleted; } -/** Returns true if the given string 'name' is a valid unit in the table */ bool UnitTable::hasUnit(Glib::ustring const &unit) const { UnitMap::const_iterator iter = _unit_map.find(unit); return (iter != _unit_map.end()); } -/** Provides an iteratable list of items in the given unit table */ UnitTable::UnitMap UnitTable::units(UnitType type) const { UnitMap submap; - for (UnitMap::const_iterator iter = _unit_map.begin(); - iter != _unit_map.end(); ++iter) { - if (((*iter).second)->type == type) { - submap.insert(UnitMap::value_type((*iter).first, new Unit(*((*iter).second)))); - } + for (UnitMap::const_iterator iter = _unit_map.begin(); iter != _unit_map.end(); ++iter) { + if (((*iter).second)->type == type) { + submap.insert(UnitMap::value_type((*iter).first, new Unit(*((*iter).second)))); + } } return submap; } -/** Returns the default unit abbr for the given type */ Glib::ustring UnitTable::primary(UnitType type) const { return _primary_unit[type]; } -/** Loads units from a text file. - - loadText loads and merges the contents of the given file into the UnitTable, - possibly overwriting existing unit definitions. - - @param filename: file to be loaded*/ -bool UnitTable::loadText(Glib::ustring const &filename) -{ - char buf[BUFSIZE]; - - // 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; - } - - // 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; - old_locale = g_strdup (setlocale (LC_NUMERIC, NULL)); - setlocale (LC_NUMERIC, "C"); - - while (fgets(buf, BUFSIZE, f) != NULL) { - char name[BUFSIZE]; - char plural[BUFSIZE]; - char abbr[BUFSIZE]; - char type[BUFSIZE]; - double factor; - char primary[BUFSIZE]; - - int nchars = 0; - // locale is set to C, scanning %lf should work _everywhere_ - 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 - Unit u; - u.name = name; - u.name_plural = plural; - u.abbr = abbr; - u.description = desc; - u.factor = factor; - - if (streq(type, "DIMENSIONLESS")) { - u.type = UNIT_TYPE_DIMENSIONLESS; - } else if (streq(type, "LINEAR")) { - u.type = UNIT_TYPE_LINEAR; - } else if (streq(type, "RADIAL")) { - u.type = UNIT_TYPE_RADIAL; - } else if (streq(type, "FONT_HEIGHT")) { - u.type = UNIT_TYPE_FONT_HEIGHT; - } else { - g_warning("Skipping unknown unit type '%s' for %s.\n", - type, name); - continue; - } - - // if primary is 'Y', list this unit as a primary - addUnit(u, (primary[0]=='Y' || primary[0]=='y')); - +bool UnitTable::load(std::string const &filename) { + UnitParser uparser(this); + Glib::Markup::ParseContext ctx(uparser); + + 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; +} - // set back the saved locale - setlocale (LC_NUMERIC, old_locale); - g_free (old_locale); +bool UnitTable::save(std::string const &filename) { - // 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; } -bool UnitTable::load(Glib::ustring const &filename) { - UnitsSAXHandler handler(this); +Inkscape::Util::UnitTable unit_table; - int result = handler.parseFile( filename.c_str() ); - if ( result != 0 ) { - // perhaps - g_warning("Problem loading units file '%s': %d\n", - filename.c_str(), result); - return false; - } +void UnitParser::on_start_element(Ctx &ctx, Glib::ustring const &name, AttrMap const &attrs) +{ + if (name == "unit") { + // reset for next use + unit.clear(); + primary = false; + skip = false; - return true; + 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'); + } + } } -/** Saves the current UnitTable to the given file. */ -bool UnitTable::save(Glib::ustring 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; +void UnitParser::on_text(Ctx &ctx, Glib::ustring const &text) +{ + 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 = g_ascii_strtod(text.c_str(), NULL); + } else if (element == "description") { + unit.description = text; } +} - // 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; +void UnitParser::on_end_element(Ctx &ctx, Glib::ustring const &name) +{ + if (name == "unit" && !skip) { + tbl->addUnit(unit, primary); } +} - return true; +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)); +} -void UnitsSAXHandler::_startElement(xmlChar const *name, xmlChar const **attrs) +double Quantity::value(const Unit &u) const { - if (streq("unit", (char const *)name)) { - // reset for next use - unit.name.clear(); - unit.name_plural.clear(); - unit.abbr.clear(); - unit.description.clear(); - unit.type = UNIT_TYPE_DIMENSIONLESS; - unit.factor = 1.0; - primary = false; - skip = false; + return convert(quantity, *unit, u); +} +double Quantity::value(const Glib::ustring u) const +{ + return value(unit_table.getUnit(u)); +} - 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 (streq(type, "DIMENSIONLESS")) { - unit.type = UNIT_TYPE_DIMENSIONLESS; - } else if (streq(type, "LINEAR")) { - unit.type = UNIT_TYPE_LINEAR; - } else if (streq(type, "RADIAL")) { - unit.type = UNIT_TYPE_RADIAL; - } else if (streq(type, "FONT_HEIGHT")) { - unit.type = UNIT_TYPE_FONT_HEIGHT; - } 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'; - } - } - } +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); } -void UnitsSAXHandler::_endElement(xmlChar const *xname) +double Quantity::convert(const double from_dist, const Unit &from, const Unit &to) { - 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)) { - // 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); - } + // 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++ |
