diff --git a/src/libOpenImageIO/imagespec_test.cpp b/src/libOpenImageIO/imagespec_test.cpp
index a233758c65..986caf648d 100644
--- a/src/libOpenImageIO/imagespec_test.cpp
+++ b/src/libOpenImageIO/imagespec_test.cpp
@@ -47,6 +47,7 @@ const char* imagespec_xml_string = R"EOF(
0, 0
1
1
+30000/1001
)EOF";
@@ -358,6 +359,20 @@ test_imagespec_from_xml()
OIIO_CHECK_EQUAL(spec.format, TypeFloat);
OIIO_CHECK_EQUAL(spec.get_string_attribute("oiio:ColorSpace"),
"scene_linear");
+ OIIO_CHECK_EQUAL(spec.getattributetype("FramesPerSecond"), TypeRational);
+ OIIO_CHECK_EQUAL_THRESH(spec.get_float_attribute("FramesPerSecond", -1.0f),
+ 30000.0f / 1001.0f, 1.0e-5f);
+
+ std::string xml = spec.to_xml();
+ OIIO_CHECK_ASSERT(xml.find("name=\"FramesPerSecond\" type=\"rational\"")
+ != std::string::npos);
+ ImageSpec roundtripped;
+ roundtripped.from_xml(xml.c_str());
+ OIIO_CHECK_EQUAL(roundtripped.getattributetype("FramesPerSecond"),
+ TypeRational);
+ OIIO_CHECK_EQUAL_THRESH(roundtripped.get_float_attribute("FramesPerSecond",
+ -1.0f),
+ 30000.0f / 1001.0f, 1.0e-5f);
}
diff --git a/src/libutil/paramlist.cpp b/src/libutil/paramlist.cpp
index 28ee3ab365..0a32afc42b 100644
--- a/src/libutil/paramlist.cpp
+++ b/src/libutil/paramlist.cpp
@@ -130,7 +130,15 @@ parse_elements(string_view value, ParamValue& p)
ParamValue::ParamValue(string_view name, TypeDesc type, string_view value)
: ParamValue(name, type, 1, cspan())
{
- if (type.basetype == TypeDesc::INT) {
+ if (type == TypeRational || type == TypeURational) {
+ // Accept either "x,y" or "x/y" here
+ std::string rational_values(value);
+ std::replace(rational_values.begin(), rational_values.end(), '/', ',');
+ if (type.basetype == TypeDesc::INT)
+ parse_elements(rational_values, *this);
+ else if (type.basetype == TypeDesc::UINT)
+ parse_elements(rational_values, *this);
+ } else if (type.basetype == TypeDesc::INT) {
parse_elements(value, *this);
} else if (type.basetype == TypeDesc::UINT) {
parse_elements(value, *this);
diff --git a/src/libutil/typedesc.cpp b/src/libutil/typedesc.cpp
index 99dc09fed7..2896faa393 100644
--- a/src/libutil/typedesc.cpp
+++ b/src/libutil/typedesc.cpp
@@ -214,6 +214,15 @@ TypeDesc::c_str() const
result = Strutil::fmt::format("matrix{}", basetype_code[basetype]);
break;
}
+ } else if (vecsemantics == RATIONAL
+ && (*this == TypeRational || *this == TypeURational)) {
+ result = (basetype == TypeDesc::INT) ? "rational" : "urational";
+ } else if (vecsemantics == BOX) {
+ result = Strutil::fmt::format("box{}{}", int(aggregate),
+ basetype == FLOAT
+ ? ""
+ : basetype_code[basetype]);
+ alen = arraylen > 2 ? (arraylen / 2) : (arraylen < 0 ? -1 : 0);
} else {
// Special names for vector semantics
const char* vec = "";
@@ -237,14 +246,6 @@ TypeDesc::c_str() const
if (basetype != FLOAT)
result += basetype_code[basetype];
}
- // More unusual cases
- if (vecsemantics == BOX) {
- result = Strutil::fmt::format("box{}{}", int(aggregate),
- basetype == FLOAT
- ? ""
- : basetype_code[basetype]);
- alen = arraylen > 2 ? (arraylen / 2) : (arraylen < 0 ? -1 : 0);
- }
if (alen > 0)
result += Strutil::fmt::format("[{}]", alen);
else if (alen < 0)
@@ -323,9 +324,9 @@ TypeDesc::fromstring(string_view typestring)
t = OIIO::TypeFloat4;
else if (type == "timecode")
t = OIIO::TypeTimeCode;
- else if (type == "rational")
+ else if (type == "rational" || type == "rational2i")
t = OIIO::TypeRational;
- else if (type == "urational")
+ else if (type == "urational" || type == "rational2u")
t = OIIO::TypeURational;
else if (type == "box2i")
t = OIIO::TypeBox2i;
diff --git a/src/libutil/typedesc_test.cpp b/src/libutil/typedesc_test.cpp
index d8dfa7b324..328f4fc757 100644
--- a/src/libutil/typedesc_test.cpp
+++ b/src/libutil/typedesc_test.cpp
@@ -202,6 +202,8 @@ main(int /*argc*/, char* /*argv*/[])
TypeDesc(TypeDesc::INT, TypeDesc::VEC2,
TypeDesc::RATIONAL),
TypeRational, i2, "1/2");
+ OIIO_CHECK_EQUAL(TypeDesc("rational"), TypeRational);
+ OIIO_CHECK_EQUAL(TypeDesc("urational"), TypeURational);
uint32_t ui2[2] = { 1, 2 };
test_type("urational",
TypeDesc(TypeDesc::UINT32, TypeDesc::VEC2,
diff --git a/testsuite/python-typedesc/ref/out.txt b/testsuite/python-typedesc/ref/out.txt
index 9313bc8cda..9949850314 100644
--- a/testsuite/python-typedesc/ref/out.txt
+++ b/testsuite/python-typedesc/ref/out.txt
@@ -246,9 +246,9 @@ type 'TypeVector3i'
type 'TypeHalf'
c_str "half"
type 'TypeRational'
- c_str "rational2i"
+ c_str "rational"
type 'TypeURational'
- c_str "rational2ui"
+ c_str "urational"
type 'TypeUInt'
c_str "uint"