diff options
author | Nico Weber <thakis@chromium.org> | 2022-12-27 13:37:37 -0500 |
---|---|---|
committer | Brian Gianforcaro <b.gianfo@gmail.com> | 2022-12-27 15:58:39 -0800 |
commit | 782ab0a11f650e8b44341855d9cd93ac489e1900 (patch) | |
tree | 78456021648445152d30076f27d97208ee3800ce | |
parent | bb1f6f71f1c1efc675958390a52a9e52d9a9fd7b (diff) | |
download | serenity-782ab0a11f650e8b44341855d9cd93ac489e1900.zip |
LibGfx: Read profile creation time from ICCProfile header
-rw-r--r-- | Userland/Libraries/LibGfx/ICCProfile.cpp | 67 | ||||
-rw-r--r-- | Userland/Libraries/LibGfx/ICCProfile.h | 2 |
2 files changed, 63 insertions, 6 deletions
diff --git a/Userland/Libraries/LibGfx/ICCProfile.cpp b/Userland/Libraries/LibGfx/ICCProfile.cpp index 15621e4124..c1e0fa3ae3 100644 --- a/Userland/Libraries/LibGfx/ICCProfile.cpp +++ b/Userland/Libraries/LibGfx/ICCProfile.cpp @@ -6,6 +6,7 @@ #include <AK/Endian.h> #include <LibGfx/ICCProfile.h> +#include <time.h> // V2 spec: https://color.org/specification/ICC.1-2001-04.pdf // V4 spec: https://color.org/specification/ICC.1-2022-05.pdf @@ -14,6 +15,58 @@ namespace Gfx::ICC { namespace { +// ICC V4, 4.2 dateTimeNumber +// "All the dateTimeNumber values in a profile shall be in Coordinated Universal Time [...]." +struct DateTimeNumber { + BigEndian<u16> year; + BigEndian<u16> month; + BigEndian<u16> day; + BigEndian<u16> hour; + BigEndian<u16> minutes; + BigEndian<u16> seconds; +}; + +ErrorOr<time_t> parse_date_time_number(DateTimeNumber const& date_time) +{ + // ICC V4, 4.2 dateTimeNumber + + // "Number of the month (1 to 12)" + if (date_time.month < 1 || date_time.month > 12) + return Error::from_string_literal("ICC::Profile: dateTimeNumber month out of bounds"); + + // "Number of the day of the month (1 to 31)" + if (date_time.day < 1 || date_time.day > 31) + return Error::from_string_literal("ICC::Profile: dateTimeNumber day out of bounds"); + + // "Number of hours (0 to 23)" + if (date_time.hour > 23) + return Error::from_string_literal("ICC::Profile: dateTimeNumber hour out of bounds"); + + // "Number of minutes (0 to 59)" + if (date_time.minutes > 59) + return Error::from_string_literal("ICC::Profile: dateTimeNumber minutes out of bounds"); + + // "Number of seconds (0 to 59)" + // ICC profiles apparently can't be created during leap seconds (seconds would be 60 there, but the spec doesn't allow that). + if (date_time.seconds > 59) + return Error::from_string_literal("ICC::Profile: dateTimeNumber seconds out of bounds"); + + struct tm tm = {}; + tm.tm_year = date_time.year - 1900; + tm.tm_mon = date_time.month - 1; + tm.tm_mday = date_time.day; + tm.tm_hour = date_time.hour; + tm.tm_min = date_time.minutes; + tm.tm_sec = date_time.seconds; + // timegm() doesn't read tm.tm_isdst, tm.tm_wday, and tm.tm_yday, no need to fill them in. + + time_t timestamp = timegm(&tm); + if (timestamp == -1) + return Error::from_string_literal("ICC::Profile: dateTimeNumber not representable as timestamp"); + + return timestamp; +} + // ICC V4, 7.2 Profile header struct ICCHeader { BigEndian<u32> profile_size; @@ -27,12 +80,7 @@ struct ICCHeader { BigEndian<u32> data_color_space; BigEndian<u32> pcs; // "Profile Connection Space" - BigEndian<u16> year; - BigEndian<u16> month; - BigEndian<u16> day; - BigEndian<u16> hour; - BigEndian<u16> minutes; - BigEndian<u16> seconds; + DateTimeNumber profile_creation_time; BigEndian<u32> profile_file_signature; BigEndian<u32> primary_platform; @@ -78,6 +126,12 @@ ErrorOr<DeviceClass> parse_device_class(ICCHeader const& header) return Error::from_string_literal("ICC::Profile: Invalid device class"); } +ErrorOr<time_t> parse_creation_date_time(ICCHeader const& header) +{ + // iCC v4, 7.2.8 Date and time field + return parse_date_time_number(header.profile_creation_time); +} + ErrorOr<void> parse_file_signature(ICCHeader const& header) { // iCC v4, 7.2.9 Profile file signature field @@ -121,6 +175,7 @@ ErrorOr<NonnullRefPtr<Profile>> Profile::try_load_from_externally_owned_memory(R TRY(parse_file_signature(header)); profile->m_version = TRY(parse_version(header)); profile->m_device_class = TRY(parse_device_class(header)); + profile->m_creation_timestamp = TRY(parse_creation_date_time(header)); return profile; } diff --git a/Userland/Libraries/LibGfx/ICCProfile.h b/Userland/Libraries/LibGfx/ICCProfile.h index f28cee6267..2a918c205f 100644 --- a/Userland/Libraries/LibGfx/ICCProfile.h +++ b/Userland/Libraries/LibGfx/ICCProfile.h @@ -51,10 +51,12 @@ public: Version version() const { return m_version; } DeviceClass device_class() const { return m_device_class; } + time_t creation_timestamp() const { return m_creation_timestamp; } private: Version m_version; DeviceClass m_device_class; + time_t m_creation_timestamp; }; } |