| 
									
										
										
										
											2023-11-12 23:59:52 -05:00
										 |  |  | #!/usr/bin/env python3 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Copyright (c) 2023, Lucas Chollet <lucas.chollet@serenityos.org> | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # SPDX-License-Identifier: BSD-2-Clause | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import argparse | 
					
						
							|  |  |  | import re | 
					
						
							|  |  |  | from enum import Enum | 
					
						
							|  |  |  | from collections import namedtuple | 
					
						
							|  |  |  | from pathlib import Path | 
					
						
							| 
									
										
										
										
											2024-01-07 00:17:19 -05:00
										 |  |  | from typing import Any, List, Type | 
					
						
							| 
									
										
										
										
											2023-11-12 23:59:52 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-02 20:01:30 -05:00
										 |  |  | class EnumWithExportName(Enum): | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def export_name(cls) -> str: | 
					
						
							|  |  |  |         return cls.__name__ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class TIFFType(EnumWithExportName): | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def export_name(cls) -> str: | 
					
						
							|  |  |  |         return "Type" | 
					
						
							| 
									
										
										
										
											2023-11-12 23:59:52 -05:00
										 |  |  |     Byte = 1 | 
					
						
							|  |  |  |     ASCII = 2 | 
					
						
							|  |  |  |     UnsignedShort = 3 | 
					
						
							|  |  |  |     UnsignedLong = 4 | 
					
						
							|  |  |  |     UnsignedRational = 5 | 
					
						
							|  |  |  |     Undefined = 7 | 
					
						
							|  |  |  |     SignedLong = 9 | 
					
						
							|  |  |  |     SignedRational = 10 | 
					
						
							|  |  |  |     Float = 11 | 
					
						
							|  |  |  |     Double = 12 | 
					
						
							|  |  |  |     UTF8 = 129 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-02 20:01:30 -05:00
										 |  |  | class Predictor(EnumWithExportName): | 
					
						
							| 
									
										
										
										
											2023-11-12 23:59:52 -05:00
										 |  |  |     NoPrediction = 1 | 
					
						
							|  |  |  |     HorizontalDifferencing = 2 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-02 20:01:30 -05:00
										 |  |  | class Compression(EnumWithExportName): | 
					
						
							| 
									
										
										
										
											2023-11-12 23:59:52 -05:00
										 |  |  |     NoCompression = 1 | 
					
						
							|  |  |  |     CCITT = 2 | 
					
						
							|  |  |  |     Group3Fax = 3 | 
					
						
							|  |  |  |     Group4Fax = 4 | 
					
						
							|  |  |  |     LZW = 5 | 
					
						
							|  |  |  |     JPEG = 6 | 
					
						
							| 
									
										
										
										
											2023-12-23 00:09:26 -05:00
										 |  |  |     AdobeDeflate = 8 | 
					
						
							| 
									
										
										
										
											2023-11-12 23:59:52 -05:00
										 |  |  |     PackBits = 32773 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-30 23:22:54 -05:00
										 |  |  | class PhotometricInterpretation(EnumWithExportName): | 
					
						
							|  |  |  |     WhiteIsZero = 0 | 
					
						
							|  |  |  |     BlackIsZero = 1 | 
					
						
							|  |  |  |     RGB = 2 | 
					
						
							|  |  |  |     RGBPalette = 3 | 
					
						
							|  |  |  |     TransparencyMask = 4 | 
					
						
							|  |  |  |     CMYK = 5 | 
					
						
							|  |  |  |     YCbCr = 6 | 
					
						
							|  |  |  |     CIELab = 8 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-17 19:28:54 -05:00
										 |  |  | class Orientation(EnumWithExportName): | 
					
						
							|  |  |  |     Default = 1 | 
					
						
							|  |  |  |     FlipHorizontally = 2 | 
					
						
							|  |  |  |     Rotate180 = 3 | 
					
						
							|  |  |  |     FlipVertically = 4 | 
					
						
							|  |  |  |     Rotate90ClockwiseThenFlipHorizontally = 5 | 
					
						
							|  |  |  |     Rotate90Clockwise = 6 | 
					
						
							|  |  |  |     FlipHorizontallyThenRotate90Clockwise = 7 | 
					
						
							|  |  |  |     Rotate90CounterClockwise = 8 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class PlanarConfiguration(EnumWithExportName): | 
					
						
							|  |  |  |     Chunky = 1 | 
					
						
							|  |  |  |     Planar = 2 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ResolutionUnit(EnumWithExportName): | 
					
						
							|  |  |  |     NoAbsolute = 1 | 
					
						
							|  |  |  |     Inch = 2 | 
					
						
							|  |  |  |     Centimeter = 3 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class SampleFormat(EnumWithExportName): | 
					
						
							|  |  |  |     Unsigned = 1 | 
					
						
							|  |  |  |     Signed = 2 | 
					
						
							|  |  |  |     Float = 3 | 
					
						
							|  |  |  |     Undefined = 4 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-16 20:33:39 -05:00
										 |  |  | class ExtraSample(EnumWithExportName): | 
					
						
							|  |  |  |     Unspecified = 0 | 
					
						
							|  |  |  |     AssociatedAlpha = 1 | 
					
						
							|  |  |  |     UnassociatedAlpha = 2 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-23 13:08:57 -05:00
										 |  |  | tag_fields = ['id', 'types', 'counts', 'default', 'name', 'associated_enum', 'is_required'] | 
					
						
							| 
									
										
										
										
											2023-11-12 23:59:52 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | Tag = namedtuple( | 
					
						
							|  |  |  |     'Tag', | 
					
						
							|  |  |  |     field_names=tag_fields, | 
					
						
							|  |  |  |     defaults=(None,) * len(tag_fields) | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # FIXME: Some tag have only a few allowed values, we should ensure that | 
					
						
							|  |  |  | known_tags: List[Tag] = [ | 
					
						
							| 
									
										
										
										
											2023-12-23 13:08:57 -05:00
										 |  |  |     Tag('256', [TIFFType.UnsignedShort, TIFFType.UnsignedLong], [1], None, "ImageWidth", is_required=True), | 
					
						
							|  |  |  |     Tag('257', [TIFFType.UnsignedShort, TIFFType.UnsignedLong], [1], None, "ImageHeight", is_required=True), | 
					
						
							|  |  |  |     Tag('258', [TIFFType.UnsignedShort], [], None, "BitsPerSample", is_required=True), | 
					
						
							|  |  |  |     Tag('259', [TIFFType.UnsignedShort], [1], None, "Compression", Compression, is_required=True), | 
					
						
							|  |  |  |     Tag('262', [TIFFType.UnsignedShort], [1], None, "PhotometricInterpretation", | 
					
						
							|  |  |  |         PhotometricInterpretation, is_required=True), | 
					
						
							|  |  |  |     Tag('273', [TIFFType.UnsignedShort, TIFFType.UnsignedLong], [], None, "StripOffsets", is_required=True), | 
					
						
							| 
									
										
										
										
											2023-12-17 19:28:54 -05:00
										 |  |  |     Tag('274', [TIFFType.UnsignedShort], [1], Orientation.Default, "Orientation", Orientation), | 
					
						
							| 
									
										
										
										
											2023-12-23 13:08:57 -05:00
										 |  |  |     Tag('277', [TIFFType.UnsignedShort], [1], None, "SamplesPerPixel", is_required=True), | 
					
						
							|  |  |  |     Tag('278', [TIFFType.UnsignedShort, TIFFType.UnsignedLong], [1], None, "RowsPerStrip", is_required=True), | 
					
						
							|  |  |  |     Tag('279', [TIFFType.UnsignedShort, TIFFType.UnsignedLong], [], None, "StripByteCounts", is_required=True), | 
					
						
							| 
									
										
										
										
											2023-12-27 21:03:42 -05:00
										 |  |  |     Tag('282', [TIFFType.UnsignedRational], [1], None, "XResolution"), | 
					
						
							|  |  |  |     Tag('283', [TIFFType.UnsignedRational], [1], None, "YResolution"), | 
					
						
							|  |  |  |     Tag('284', [TIFFType.UnsignedShort], [1], PlanarConfiguration.Chunky, "PlanarConfiguration", PlanarConfiguration), | 
					
						
							| 
									
										
										
										
											2023-12-17 19:28:54 -05:00
										 |  |  |     Tag('285', [TIFFType.ASCII], [], None, "PageName"), | 
					
						
							| 
									
										
										
										
											2023-12-27 21:03:42 -05:00
										 |  |  |     Tag('296', [TIFFType.UnsignedShort], [1], ResolutionUnit.Inch, "ResolutionUnit", ResolutionUnit), | 
					
						
							| 
									
										
										
										
											2023-12-17 19:28:54 -05:00
										 |  |  |     Tag('339', [TIFFType.UnsignedShort], [], SampleFormat.Unsigned, "SampleFormat", SampleFormat), | 
					
						
							| 
									
										
										
										
											2023-11-12 23:59:52 -05:00
										 |  |  |     Tag('317', [TIFFType.UnsignedShort], [1], Predictor.NoPrediction, "Predictor", Predictor), | 
					
						
							| 
									
										
										
										
											2023-12-17 23:35:21 -05:00
										 |  |  |     Tag('320', [TIFFType.UnsignedShort], [], None, "ColorMap"), | 
					
						
							| 
									
										
										
										
											2023-12-16 20:33:39 -05:00
										 |  |  |     Tag('338', [TIFFType.UnsignedShort], [], None, "ExtraSamples", ExtraSample), | 
					
						
							| 
									
										
										
										
											2023-11-28 21:16:43 -05:00
										 |  |  |     Tag('34675', [TIFFType.Undefined], [], None, "ICCProfile"), | 
					
						
							| 
									
										
										
										
											2023-11-12 23:59:52 -05:00
										 |  |  | ] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | HANDLE_TAG_SIGNATURE_TEMPLATE = ("ErrorOr<void> {namespace}handle_tag(Metadata& metadata, u16 tag," | 
					
						
							|  |  |  |                                  " {namespace}Type type, u32 count, Vector<{namespace}Value>&& value)") | 
					
						
							|  |  |  | HANDLE_TAG_SIGNATURE = HANDLE_TAG_SIGNATURE_TEMPLATE.format(namespace="") | 
					
						
							|  |  |  | HANDLE_TAG_SIGNATURE_TIFF_NAMESPACE = HANDLE_TAG_SIGNATURE_TEMPLATE.format(namespace="TIFF::") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-09 23:08:52 -05:00
										 |  |  | ENSURE_BASELINE_TAG_PRESENCE = "ErrorOr<void> ensure_baseline_tags_are_present(Metadata const& metadata)" | 
					
						
							| 
									
										
										
										
											2024-01-11 23:41:37 -05:00
										 |  |  | TIFF_TYPE_FROM_U16 = "ErrorOr<Type> tiff_type_from_u16(u16 type)" | 
					
						
							| 
									
										
										
										
											2023-12-23 13:08:57 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-12 23:59:52 -05:00
										 |  |  | LICENSE = R"""/*
 | 
					
						
							|  |  |  |  * Copyright (c) 2023, Lucas Chollet <lucas.chollet@serenityos.org> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * SPDX-License-Identifier: BSD-2-Clause | 
					
						
							|  |  |  |  */"""
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-16 20:33:22 -05:00
										 |  |  | def export_enum_to_cpp(e: Type[EnumWithExportName]) -> str: | 
					
						
							| 
									
										
										
										
											2023-12-02 20:01:30 -05:00
										 |  |  |     output = f'enum class {e.export_name()} {{\n' | 
					
						
							| 
									
										
										
										
											2023-11-12 23:59:52 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     for entry in e: | 
					
						
							|  |  |  |         output += f'    {entry.name} = {entry.value},\n' | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-30 23:22:08 -05:00
										 |  |  |     output += "};\n" | 
					
						
							| 
									
										
										
										
											2023-11-12 23:59:52 -05:00
										 |  |  |     return output | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-02 20:18:52 -05:00
										 |  |  | def export_enum_to_string_converter(enums: List[Type[EnumWithExportName]]) -> str: | 
					
						
							|  |  |  |     stringifier_internals = [] | 
					
						
							|  |  |  |     for e in enums: | 
					
						
							|  |  |  |         single_stringifier = fR"""    if constexpr (IsSame<E, {e.export_name()}>) {{
 | 
					
						
							|  |  |  |         switch (value) {{ | 
					
						
							|  |  |  |             default: | 
					
						
							|  |  |  |                 return "Invalid value for {e.export_name()}"sv;"""
 | 
					
						
							|  |  |  |         for entry in e: | 
					
						
							|  |  |  |             single_stringifier += fR"""
 | 
					
						
							|  |  |  |             case {e.export_name()}::{entry.name}: | 
					
						
							|  |  |  |                 return "{entry.name}"sv;"""
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         single_stringifier += R"""
 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     }"""
 | 
					
						
							|  |  |  |         stringifier_internals.append(single_stringifier) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     stringifier_internals_str = '\n'.join(stringifier_internals) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     out = fR"""template<Enum E>
 | 
					
						
							|  |  |  | StringView name_for_enum_tag_value(E value) {{ | 
					
						
							|  |  |  | {stringifier_internals_str} | 
					
						
							|  |  |  |     VERIFY_NOT_REACHED(); | 
					
						
							|  |  |  | }}"""
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return out | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-30 23:22:08 -05:00
										 |  |  | def export_tag_related_enums(tags: List[Tag]) -> str: | 
					
						
							|  |  |  |     exported_enums = [] | 
					
						
							|  |  |  |     for tag in tags: | 
					
						
							|  |  |  |         if tag.associated_enum: | 
					
						
							|  |  |  |             exported_enums.append(export_enum_to_cpp(tag.associated_enum)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return '\n'.join(exported_enums) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-12 23:59:52 -05:00
										 |  |  | def promote_type(t: TIFFType) -> TIFFType: | 
					
						
							|  |  |  |     if t == TIFFType.UnsignedShort: | 
					
						
							|  |  |  |         return TIFFType.UnsignedLong | 
					
						
							|  |  |  |     return t | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-23 11:36:55 -05:00
										 |  |  | def tiff_type_to_cpp(t: TIFFType, with_promotion: bool = True) -> str: | 
					
						
							| 
									
										
										
										
											2023-11-12 23:59:52 -05:00
										 |  |  |     # To simplify the code generator and the Metadata class API, all u16 are promoted to u32 | 
					
						
							|  |  |  |     # Note that the Value<> type doesn't include u16 for this reason | 
					
						
							| 
									
										
										
										
											2023-12-23 11:36:55 -05:00
										 |  |  |     if with_promotion: | 
					
						
							| 
									
										
										
										
											2023-11-12 23:59:52 -05:00
										 |  |  |         t = promote_type(t) | 
					
						
							| 
									
										
										
										
											2023-12-02 17:59:43 -05:00
										 |  |  |     if t in [TIFFType.ASCII, TIFFType.UTF8]: | 
					
						
							|  |  |  |         return 'String' | 
					
						
							| 
									
										
										
										
											2023-11-28 21:13:23 -05:00
										 |  |  |     if t == TIFFType.Undefined: | 
					
						
							|  |  |  |         return 'ByteBuffer' | 
					
						
							| 
									
										
										
										
											2023-11-28 09:54:03 +09:00
										 |  |  |     if t == TIFFType.UnsignedShort: | 
					
						
							|  |  |  |         return 'u16' | 
					
						
							|  |  |  |     if t == TIFFType.UnsignedLong: | 
					
						
							|  |  |  |         return 'u32' | 
					
						
							| 
									
										
										
										
											2023-12-17 19:28:54 -05:00
										 |  |  |     if t == TIFFType.UnsignedRational: | 
					
						
							|  |  |  |         return 'TIFF::Rational<u32>' | 
					
						
							| 
									
										
										
										
											2023-11-28 09:54:03 +09:00
										 |  |  |     raise RuntimeError(f'Type "{t}" not recognized, please update tiff_type_to_read_only_cpp()') | 
					
						
							| 
									
										
										
										
											2023-11-12 23:59:52 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-28 21:13:23 -05:00
										 |  |  | def is_container(t: TIFFType) -> bool: | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |         Some TIFF types are defined on the unit scale but are intended to be used within a collection. | 
					
						
							|  |  |  |         An example of that are ASCII strings defined as N * byte. Let's intercept that and generate | 
					
						
							|  |  |  |         a nice API instead of Vector<u8>. | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2023-12-02 17:59:43 -05:00
										 |  |  |     return t in [TIFFType.ASCII, TIFFType.Byte, TIFFType.Undefined, TIFFType.UTF8] | 
					
						
							| 
									
										
										
										
											2023-11-28 21:13:23 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-12 23:59:52 -05:00
										 |  |  | def export_promoter() -> str: | 
					
						
							|  |  |  |     output = R"""template<typename T>
 | 
					
						
							|  |  |  | struct TypePromoter { | 
					
						
							|  |  |  |     using Type = T; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  |     specialization_template = R"""template<>
 | 
					
						
							|  |  |  | struct TypePromoter<{}> {{ | 
					
						
							|  |  |  |     using Type = {}; | 
					
						
							|  |  |  | }}; | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  |     for t in TIFFType: | 
					
						
							|  |  |  |         if promote_type(t) != t: | 
					
						
							| 
									
										
										
										
											2023-12-23 11:36:55 -05:00
										 |  |  |             output += specialization_template.format(tiff_type_to_cpp(t, with_promotion=False), tiff_type_to_cpp(t)) | 
					
						
							| 
									
										
										
										
											2023-11-12 23:59:52 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return output | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def retrieve_biggest_type(types: List[TIFFType]) -> TIFFType: | 
					
						
							|  |  |  |     return TIFFType(max([t.value for t in types])) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def pascal_case_to_snake_case(name: str) -> str: | 
					
						
							| 
									
										
										
										
											2023-11-28 21:16:08 -05:00
										 |  |  |     name = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name) | 
					
						
							|  |  |  |     return re.sub('([a-z0-9])([A-Z])', r'\1_\2', name).lower() | 
					
						
							| 
									
										
										
										
											2023-11-12 23:59:52 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-07 00:17:19 -05:00
										 |  |  | def default_value_to_cpp(value: Any) -> str: | 
					
						
							|  |  |  |     if isinstance(value, EnumWithExportName): | 
					
						
							|  |  |  |         return f'TIFF::{value.export_name()}::{value.name}' | 
					
						
							|  |  |  |     return str(value) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-12 23:59:52 -05:00
										 |  |  | def generate_getter(tag: Tag) -> str: | 
					
						
							| 
									
										
										
										
											2023-11-28 21:13:23 -05:00
										 |  |  |     biggest_type = retrieve_biggest_type(tag.types) | 
					
						
							|  |  |  |     variant_inner_type = tiff_type_to_cpp(biggest_type) | 
					
						
							| 
									
										
										
										
											2023-11-12 23:59:52 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     extracted_value_template = f"(*possible_value)[{{}}].get<{variant_inner_type}>()" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     tag_final_type = variant_inner_type | 
					
						
							|  |  |  |     if tag.associated_enum: | 
					
						
							|  |  |  |         tag_final_type = f"TIFF::{tag.associated_enum.__name__}" | 
					
						
							|  |  |  |         extracted_value_template = f"static_cast<{tag_final_type}>({extracted_value_template})" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-07 00:17:19 -05:00
										 |  |  |     single_count = len(tag.counts) == 1 and tag.counts[0] == 1 or is_container(biggest_type) | 
					
						
							|  |  |  |     if single_count: | 
					
						
							| 
									
										
										
										
											2023-11-12 23:59:52 -05:00
										 |  |  |         return_type = tag_final_type | 
					
						
							| 
									
										
										
										
											2023-11-28 21:13:23 -05:00
										 |  |  |         if is_container(biggest_type): | 
					
						
							|  |  |  |             return_type += ' const&' | 
					
						
							| 
									
										
										
										
											2023-11-12 23:59:52 -05:00
										 |  |  |         unpacked_if_needed = f"return {extracted_value_template.format(0)};" | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         if len(tag.counts) == 1: | 
					
						
							|  |  |  |             container_type = f'Array<{tag_final_type}, {tag.counts[0]}>' | 
					
						
							|  |  |  |             container_initialization = f'{container_type} tmp{{}};' | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             container_type = f'Vector<{tag_final_type}>' | 
					
						
							|  |  |  |             container_initialization = fR"""{container_type} tmp{{}};
 | 
					
						
							|  |  |  |         auto maybe_failure = tmp.try_resize(possible_value->size()); | 
					
						
							|  |  |  |         if (maybe_failure.is_error()) | 
					
						
							|  |  |  |             return OptionalNone {{}}; | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return_type = container_type | 
					
						
							|  |  |  |         unpacked_if_needed = fR"""
 | 
					
						
							|  |  |  |         {container_initialization} | 
					
						
							|  |  |  |         for (u32 i = 0; i < possible_value->size(); ++i) | 
					
						
							|  |  |  |             tmp[i] = {extracted_value_template.format('i')}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return tmp;"""
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     signature = fR"    Optional<{return_type}> {pascal_case_to_snake_case(tag.name)}() const" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-07 00:17:19 -05:00
										 |  |  |     if tag.default and single_count: | 
					
						
							|  |  |  |         return_if_empty = f'{default_value_to_cpp(tag.default)}' | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         return_if_empty = 'OptionalNone {}' | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-12 23:59:52 -05:00
										 |  |  |     body = fR"""
 | 
					
						
							|  |  |  |     {{ | 
					
						
							|  |  |  |         auto const& possible_value = m_data.get("{tag.name}"sv); | 
					
						
							|  |  |  |         if (!possible_value.has_value()) | 
					
						
							| 
									
										
										
										
											2024-01-07 00:17:19 -05:00
										 |  |  |             return {return_if_empty}; | 
					
						
							| 
									
										
										
										
											2023-11-12 23:59:52 -05:00
										 |  |  |         {unpacked_if_needed} | 
					
						
							|  |  |  |     }} | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return signature + body | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def generate_metadata_class(tags: List[Tag]) -> str: | 
					
						
							|  |  |  |     getters = '\n'.join([generate_getter(tag) for tag in tags]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     output = fR"""class Metadata {{
 | 
					
						
							|  |  |  | public: | 
					
						
							|  |  |  | {getters} | 
					
						
							|  |  |  | private: | 
					
						
							|  |  |  |     friend {HANDLE_TAG_SIGNATURE_TIFF_NAMESPACE}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     void add_entry(StringView key, Vector<TIFF::Value>&& value) {{ | 
					
						
							|  |  |  |         m_data.set(key, move(value)); | 
					
						
							|  |  |  |     }} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     HashMap<StringView, Vector<TIFF::Value>> m_data; | 
					
						
							|  |  |  | }}; | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return output | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def generate_metadata_file(tags: List[Tag]) -> str: | 
					
						
							|  |  |  |     output = fR"""{LICENSE}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #pragma once | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <AK/HashMap.h> | 
					
						
							|  |  |  | #include <AK/Variant.h> | 
					
						
							|  |  |  | #include <AK/Vector.h> | 
					
						
							|  |  |  | #include <LibGfx/Size.h> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace Gfx {{ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Metadata; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace TIFF {{ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-02 20:01:30 -05:00
										 |  |  | {export_enum_to_cpp(TIFFType)} | 
					
						
							| 
									
										
										
										
											2023-11-12 23:59:52 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | template<OneOf<u32, i32> x32> | 
					
						
							|  |  |  | struct Rational {{ | 
					
						
							|  |  |  |     using Type = x32; | 
					
						
							|  |  |  |     x32 numerator; | 
					
						
							|  |  |  |     x32 denominator; | 
					
						
							|  |  |  | }}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | {export_promoter()} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Note that u16 is not include on purpose | 
					
						
							| 
									
										
										
										
											2023-11-28 21:13:23 -05:00
										 |  |  | using Value = Variant<ByteBuffer, String, u32, Rational<u32>, i32, Rational<i32>>; | 
					
						
							| 
									
										
										
										
											2023-11-12 23:59:52 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-30 23:22:08 -05:00
										 |  |  | {export_tag_related_enums(known_tags)} | 
					
						
							| 
									
										
										
										
											2023-11-12 23:59:52 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-02 20:18:52 -05:00
										 |  |  | {export_enum_to_string_converter([tag.associated_enum for tag in known_tags if tag.associated_enum] + [TIFFType])} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-12 23:59:52 -05:00
										 |  |  | {HANDLE_TAG_SIGNATURE}; | 
					
						
							| 
									
										
										
										
											2023-12-23 13:08:57 -05:00
										 |  |  | {ENSURE_BASELINE_TAG_PRESENCE}; | 
					
						
							| 
									
										
										
										
											2024-01-11 23:41:37 -05:00
										 |  |  | {TIFF_TYPE_FROM_U16}; | 
					
						
							| 
									
										
										
										
											2023-11-12 23:59:52 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | }} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-02 19:51:29 -05:00
										 |  |  | {generate_metadata_class(tags)} | 
					
						
							| 
									
										
										
										
											2023-11-12 23:59:52 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-02 19:51:29 -05:00
										 |  |  | }} | 
					
						
							| 
									
										
										
										
											2023-11-12 23:59:52 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-02 19:51:29 -05:00
										 |  |  | template<typename T> | 
					
						
							|  |  |  | struct AK::Formatter<Gfx::TIFF::Rational<T>> : Formatter<FormatString> {{ | 
					
						
							|  |  |  |     ErrorOr<void> format(FormatBuilder& builder, Gfx::TIFF::Rational<T> value) | 
					
						
							|  |  |  |     {{ | 
					
						
							|  |  |  |         return Formatter<FormatString>::format(builder, "{{}} ({{}}/{{}})"sv, | 
					
						
							|  |  |  |             static_cast<double>(value.numerator) / value.denominator, value.numerator, value.denominator); | 
					
						
							|  |  |  |     }} | 
					
						
							|  |  |  | }}; | 
					
						
							| 
									
										
										
										
											2023-12-02 20:19:53 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | template<> | 
					
						
							|  |  |  | struct AK::Formatter<Gfx::TIFF::Value> : Formatter<FormatString> {{ | 
					
						
							|  |  |  |     ErrorOr<void> format(FormatBuilder& builder, Gfx::TIFF::Value const& value) | 
					
						
							|  |  |  |     {{ | 
					
						
							|  |  |  |         String content; | 
					
						
							|  |  |  |         value.visit( | 
					
						
							|  |  |  |             [&](ByteBuffer const& buffer) {{ | 
					
						
							|  |  |  |                 content = MUST(String::formatted("Buffer of size: {{}}"sv, buffer.size())); | 
					
						
							|  |  |  |             }}, | 
					
						
							|  |  |  |             [&](auto const& other) {{ | 
					
						
							|  |  |  |                 content = MUST(String::formatted("{{}}", other)); | 
					
						
							|  |  |  |             }} | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return Formatter<FormatString>::format(builder, "{{}}"sv, content); | 
					
						
							|  |  |  |     }} | 
					
						
							|  |  |  | }}; | 
					
						
							| 
									
										
										
										
											2023-12-02 19:51:29 -05:00
										 |  |  | """
 | 
					
						
							| 
									
										
										
										
											2023-11-12 23:59:52 -05:00
										 |  |  |     return output | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def generate_tag_handler(tag: Tag) -> str: | 
					
						
							|  |  |  |     not_in_type_list = f"({' && '.join([f'type != Type::{t.name}' for t in tag.types])})" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     not_in_count_list = '' | 
					
						
							|  |  |  |     if len(tag.counts) != 0: | 
					
						
							|  |  |  |         not_in_count_list = f"|| ({' && '.join([f'count != {c}' for c in tag.counts])})" | 
					
						
							|  |  |  |     pre_condition = fR"""if ({not_in_type_list}
 | 
					
						
							|  |  |  |             {not_in_count_list}) | 
					
						
							|  |  |  |             return Error::from_string_literal("TIFFImageDecoderPlugin: Tag {tag.name} invalid");"""
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     check_value = '' | 
					
						
							|  |  |  |     if tag.associated_enum is not None: | 
					
						
							|  |  |  |         not_in_value_list = f"({' && '.join([f'v != {v.value}' for v in tag.associated_enum])})" | 
					
						
							| 
									
										
										
										
											2023-12-27 21:17:45 -05:00
										 |  |  |         check_value = fR"""
 | 
					
						
							|  |  |  |         for (u32 i = 0; i < value.size(); ++i) {{ | 
					
						
							|  |  |  |             TRY(value[i].visit( | 
					
						
							|  |  |  |                 []({tiff_type_to_cpp(tag.types[0])} const& v) -> ErrorOr<void> {{ | 
					
						
							|  |  |  |                     if ({not_in_value_list}) | 
					
						
							|  |  |  |                         return Error::from_string_literal("TIFFImageDecoderPlugin: Invalid value for tag {tag.name}"); | 
					
						
							|  |  |  |                     return {{}}; | 
					
						
							|  |  |  |                 }}, | 
					
						
							|  |  |  |                 [&](auto const&) -> ErrorOr<void> {{ | 
					
						
							|  |  |  |                     VERIFY_NOT_REACHED(); | 
					
						
							|  |  |  |                 }}) | 
					
						
							|  |  |  |             ); | 
					
						
							|  |  |  |         }} | 
					
						
							| 
									
										
										
										
											2023-11-12 23:59:52 -05:00
										 |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     output = fR"""    case {tag.id}:
 | 
					
						
							|  |  |  |         // {tag.name} | 
					
						
							| 
									
										
										
										
											2023-12-02 19:47:40 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-02 22:09:47 -05:00
										 |  |  |         dbgln_if(TIFF_DEBUG, "{tag.name}({{}}): {{}}", name_for_enum_tag_value(type), format_tiff_value(tag, value)); | 
					
						
							| 
									
										
										
										
											2023-12-02 19:47:40 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-12 23:59:52 -05:00
										 |  |  |         {pre_condition} | 
					
						
							|  |  |  |         {check_value} | 
					
						
							|  |  |  |         metadata.add_entry("{tag.name}"sv, move(value)); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return output | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def generate_tag_handler_file(tags: List[Tag]) -> str: | 
					
						
							| 
									
										
										
										
											2023-12-02 22:09:47 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     formatter_for_tag_with_enum = '\n'.join([fR"""        case {tag.id}:
 | 
					
						
							|  |  |  |             return MUST(String::from_utf8( | 
					
						
							|  |  |  |                 name_for_enum_tag_value(static_cast<{tag.associated_enum.export_name()}>(v.get<u32>()))));"""
 | 
					
						
							|  |  |  |                                              for tag in tags if tag.associated_enum]) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-09 23:08:52 -05:00
										 |  |  |     ensure_tags_are_present = '\n'.join([fR"""    if (!metadata.{pascal_case_to_snake_case(tag.name)}().has_value())
 | 
					
						
							| 
									
										
										
										
											2023-12-23 13:08:57 -05:00
										 |  |  |         return Error::from_string_literal("Unable to decode image, missing required tag {tag.name}."); | 
					
						
							|  |  |  | """ for tag in filter(lambda tag: tag.is_required, known_tags)])
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-11 23:41:37 -05:00
										 |  |  |     tiff_type_from_u16_cases = '\n'.join([fR"""    case to_underlying(Type::{t.name}):
 | 
					
						
							|  |  |  |         return Type::{t.name};""" for t in TIFFType])
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-12 23:59:52 -05:00
										 |  |  |     output = fR"""{LICENSE}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <AK/Debug.h> | 
					
						
							|  |  |  | #include <AK/String.h> | 
					
						
							|  |  |  | #include <LibGfx/ImageFormats/TIFFMetadata.h> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace Gfx::TIFF {{ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-02 22:09:47 -05:00
										 |  |  | static String value_formatter(u32 tag_id, Value const& v) {{ | 
					
						
							|  |  |  |     switch (tag_id) {{ | 
					
						
							|  |  |  | {formatter_for_tag_with_enum} | 
					
						
							|  |  |  |         default: | 
					
						
							|  |  |  |             return MUST(String::formatted("{{}}", v)); | 
					
						
							|  |  |  |     }} | 
					
						
							|  |  |  | }} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | [[maybe_unused]] static String format_tiff_value(u32 tag_id, Vector<Value> const& values) {{ | 
					
						
							| 
									
										
										
										
											2023-12-02 19:47:40 -05:00
										 |  |  |     if (values.size() == 1) | 
					
						
							| 
									
										
										
										
											2023-12-02 22:09:47 -05:00
										 |  |  |         return MUST(String::formatted("{{}}", value_formatter(tag_id, values[0]))); | 
					
						
							| 
									
										
										
										
											2023-12-02 19:47:40 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     StringBuilder builder; | 
					
						
							|  |  |  |     builder.append('['); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (u32 i = 0; i < values.size(); ++i) {{ | 
					
						
							| 
									
										
										
										
											2023-12-02 22:09:47 -05:00
										 |  |  |         builder.appendff("{{}}", value_formatter(tag_id, values[i])); | 
					
						
							| 
									
										
										
										
											2023-12-02 19:47:40 -05:00
										 |  |  |         if (i != values.size() - 1) | 
					
						
							|  |  |  |             builder.append(", "sv); | 
					
						
							|  |  |  |     }} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     builder.append(']'); | 
					
						
							|  |  |  |     return MUST(builder.to_string()); | 
					
						
							|  |  |  | }} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-23 13:08:57 -05:00
										 |  |  | {ENSURE_BASELINE_TAG_PRESENCE} | 
					
						
							|  |  |  | {{ | 
					
						
							| 
									
										
										
										
											2024-01-09 23:08:52 -05:00
										 |  |  | {ensure_tags_are_present} | 
					
						
							| 
									
										
										
										
											2023-12-23 13:08:57 -05:00
										 |  |  |     return {{}}; | 
					
						
							|  |  |  | }} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-11 23:41:37 -05:00
										 |  |  | {TIFF_TYPE_FROM_U16} | 
					
						
							|  |  |  | {{ | 
					
						
							|  |  |  |     switch (type) {{ | 
					
						
							|  |  |  | {tiff_type_from_u16_cases} | 
					
						
							|  |  |  |     default: | 
					
						
							|  |  |  |         return Error::from_string_literal("TIFFImageDecoderPlugin: Unknown type"); | 
					
						
							|  |  |  |     }} | 
					
						
							|  |  |  | }} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-23 13:08:57 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-12 23:59:52 -05:00
										 |  |  | {HANDLE_TAG_SIGNATURE} | 
					
						
							|  |  |  | {{ | 
					
						
							|  |  |  |     switch (tag) {{ | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     output += '\n'.join([generate_tag_handler(t) for t in tags]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     output += R"""
 | 
					
						
							|  |  |  |     default: | 
					
						
							| 
									
										
										
										
											2023-12-02 22:09:47 -05:00
										 |  |  |         dbgln_if(TIFF_DEBUG, "UnknownTag({}, {}): {}", | 
					
						
							|  |  |  |                 tag, name_for_enum_tag_value(type), format_tiff_value(tag, value)); | 
					
						
							| 
									
										
										
										
											2023-11-12 23:59:52 -05:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return {}; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  |     return output | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def update_file(target: Path, new_content: str): | 
					
						
							|  |  |  |     should_update = True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if target.exists(): | 
					
						
							|  |  |  |         with target.open('r') as file: | 
					
						
							|  |  |  |             content = file.read() | 
					
						
							|  |  |  |             if content == new_content: | 
					
						
							|  |  |  |                 should_update = False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if should_update: | 
					
						
							|  |  |  |         with target.open('w') as file: | 
					
						
							|  |  |  |             file.write(new_content) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def main(): | 
					
						
							|  |  |  |     parser = argparse.ArgumentParser() | 
					
						
							|  |  |  |     parser.add_argument('-o', '--output') | 
					
						
							|  |  |  |     args = parser.parse_args() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     output_path = Path(args.output) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     update_file(output_path / 'TIFFMetadata.h', generate_metadata_file(known_tags)) | 
					
						
							|  |  |  |     update_file(output_path / 'TIFFTagHandler.cpp', generate_tag_handler_file(known_tags)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if __name__ == '__main__': | 
					
						
							|  |  |  |     main() |