@@ -115,7 +115,17 @@ struct ICCHeader {
115
115
u8 reserved[28 ];
116
116
};
117
117
static_assert (sizeof (ICCHeader) == 128 );
118
+ }
119
+
120
+ // ICC V4, 7.3 Tag table, Table 24 - Tag table structure
121
+ struct Detail ::TagTableEntry {
122
+ BigEndian<TagSignature> tag_signature;
123
+ BigEndian<u32 > offset_to_beginning_of_tag_data_element;
124
+ BigEndian<u32 > size_of_tag_data_element;
125
+ };
126
+ static_assert (sizeof (Detail::TagTableEntry) == 12 );
118
127
128
+ namespace {
119
129
ErrorOr<u32 > parse_size (ICCHeader const & header, ReadonlyBytes icc_bytes)
120
130
{
121
131
// ICC v4, 7.2.2 Profile size field
@@ -539,14 +549,79 @@ ErrorOr<void> Profile::read_header(ReadonlyBytes bytes)
539
549
return {};
540
550
}
541
551
552
+ ErrorOr<NonnullRefPtr<TagData>> Profile::read_tag (ReadonlyBytes bytes, Detail::TagTableEntry const & entry)
553
+ {
554
+ if (entry.offset_to_beginning_of_tag_data_element + entry.size_of_tag_data_element > bytes.size ())
555
+ return Error::from_string_literal (" ICC::Profile: Tag data out of bounds" );
556
+
557
+ auto tag_bytes = bytes.slice (entry.offset_to_beginning_of_tag_data_element , entry.size_of_tag_data_element );
558
+
559
+ // ICC v4, 9 Tag definitions
560
+ // ICC v4, 9.1 General
561
+ // "All tags, including private tags, have as their first four bytes a tag signature to identify to profile readers
562
+ // what kind of data is contained within a tag."
563
+ if (tag_bytes.size () < sizeof (u32 ))
564
+ return Error::from_string_literal (" ICC::Profile: Not enough data for tag type" );
565
+ auto tag_type = *bit_cast<BigEndian<TagTypeSignature> const *>(tag_bytes.data ());
566
+
567
+ switch ((u32 )(TagTypeSignature)tag_type) {
568
+ default :
569
+ // FIXME: optionally ignore tags of unknown type
570
+ return adopt_ref (*new UnknownTagData (entry.offset_to_beginning_of_tag_data_element , entry.size_of_tag_data_element , tag_type));
571
+ }
572
+ }
573
+
574
+ ErrorOr<void > Profile::read_tag_table (ReadonlyBytes bytes)
575
+ {
576
+ // ICC v4, 7.3 Tag table
577
+ // ICC v4, 7.3.1 Overview
578
+ // "The tag table acts as a table of contents for the tags and an index into the tag data element in the profiles. It
579
+ // shall consist of a 4-byte entry that contains a count of the number of tags in the table followed by a series of 12-
580
+ // byte entries with one entry for each tag. The tag table therefore contains 4+12n bytes where n is the number of
581
+ // tags contained in the profile. The entries for the tags within the table are not required to be in any particular
582
+ // order nor are they required to match the sequence of tag data element within the profile.
583
+ // Each 12-byte tag entry following the tag count shall consist of a 4-byte tag signature, a 4-byte offset to define
584
+ // the beginning of the tag data element, and a 4-byte entry identifying the length of the tag data element in bytes.
585
+ // [...]
586
+ // The tag table shall define a contiguous sequence of unique tag elements, with no gaps between the last byte
587
+ // of any tag data element referenced from the tag table (inclusive of any necessary additional pad bytes required
588
+ // to reach a four-byte boundary) and the byte offset of the following tag element, or the end of the file.
589
+ // Duplicate tag signatures shall not be included in the tag table.
590
+ // Tag data elements shall not partially overlap, so there shall be no part of any tag data element that falls within
591
+ // the range defined for another tag in the tag table.
592
+ // The tag table may contain multiple tags signatures that all reference the same tag data element offset, allowing
593
+ // efficient reuse of tag data elements. In such cases, both the offset and size of the tag data elements in the tag
594
+ // table shall be the same."
595
+
596
+ ReadonlyBytes tag_table_bytes = bytes.slice (sizeof (ICCHeader));
597
+
598
+ if (tag_table_bytes.size () < sizeof (u32 ))
599
+ return Error::from_string_literal (" ICC::Profile: Not enough data for tag count" );
600
+ auto tag_count = *bit_cast<BigEndian<u32 > const *>(tag_table_bytes.data ());
601
+
602
+ tag_table_bytes = tag_table_bytes.slice (sizeof (u32 ));
603
+ if (tag_table_bytes.size () < tag_count * sizeof (Detail::TagTableEntry))
604
+ return Error::from_string_literal (" ICC::Profile: Not enough data for tag table entries" );
605
+ auto tag_table_entries = bit_cast<Detail::TagTableEntry const *>(tag_table_bytes.data ());
606
+
607
+ for (u32 i = 0 ; i < tag_count; ++i) {
608
+ // FIXME: optionally ignore tags with unknown signature
609
+ // FIXME: dedupe identical offset/sizes
610
+ auto tag_data = TRY (read_tag (bytes, tag_table_entries[i]));
611
+ // "Duplicate tag signatures shall not be included in the tag table."
612
+ if (TRY (m_tag_table.try_set (tag_table_entries[i].tag_signature , move (tag_data))) != AK::HashSetResult::InsertedNewEntry)
613
+ return Error::from_string_literal (" ICC::Profile: duplicate tag signature" );
614
+ }
615
+
616
+ return {};
617
+ }
618
+
542
619
ErrorOr<NonnullRefPtr<Profile>> Profile::try_load_from_externally_owned_memory (ReadonlyBytes bytes)
543
620
{
544
621
auto profile = adopt_ref (*new Profile ());
545
622
TRY (profile->read_header (bytes));
546
-
547
623
bytes = bytes.trim (profile->on_disk_size ());
548
- bytes = bytes.slice (sizeof (ICCHeader));
549
- // FIXME: Read tag table.
624
+ TRY (profile->read_tag_table (bytes));
550
625
551
626
return profile;
552
627
}
0 commit comments