diff --git a/doc/templates/Makefile b/doc/templates/Makefile index 65445490e0..45da813d15 100644 --- a/doc/templates/Makefile +++ b/doc/templates/Makefile @@ -112,7 +112,8 @@ TABLES = Exif \ Sony1MltCsOld \ Sony1MltCsA100 \ Sony2Fp \ - SonyMisc1 + SonyMisc1 \ + SonyMisc2b SCHEMA = xmp_dc \ xmp_dwc \ diff --git a/man/man1/exiv2.1 b/man/man1/exiv2.1 index 38809bd942..eca1716eb1 100644 --- a/man/man1/exiv2.1 +++ b/man/man1/exiv2.1 @@ -643,6 +643,7 @@ SubImage8 MinoltaCs7D NikonFl1 Olympus Panasonic Son SubImage9 MinoltaCsNew NikonFl2 Olympus2 PanasonicRaw Sony2Cs2 SubThumb1 MinoltaCsOld NikonFl3 OlympusCs Sony2Fp Thumbnail NikonIi OlympusEq Sigma SonyMisc1 + SonyMisc2b SonyMinolta .fi .sp 1 diff --git a/src/makernote_int.cpp b/src/makernote_int.cpp index f101b4ec15..48827d1550 100644 --- a/src/makernote_int.cpp +++ b/src/makernote_int.cpp @@ -81,8 +81,11 @@ // ***************************************************************************** namespace { // Todo: Can be generalized further - get any tag as a string/long/... + //! Get the Value for a tag within a particular group + const Exiv2::Value* getExifValue(Exiv2::Internal::TiffComponent* const pRoot, const uint16_t& tag, const Exiv2::Internal::IfdId& group); //! Get the model name from tag Exif.Image.Model std::string getExifModel(Exiv2::Internal::TiffComponent* pRoot); + //! Nikon en/decryption function void ncrypt(Exiv2::byte* pData, uint32_t size, uint32_t count, uint32_t serial); } // namespace @@ -1201,19 +1204,47 @@ namespace Exiv2 { } return 0; } + int sonyMisc2bSelector(uint16_t /*tag*/, const byte* /*pData*/, uint32_t /*size*/, TiffComponent* const pRoot) + { + // From Exiftool: https://github.com/exiftool/exiftool/blob/master/lib/Image/ExifTool/Sony.pm + // > First byte must be 9 or 12 or 13 or 15 or 16 and 4th byte must be 2 (deciphered) + const auto value = getExifValue(pRoot, 0x9404, Exiv2::Internal::sony1Id); + if (!value || value->count() < 4) + return -1; + + switch (value->toLong(0)) { // Using encrypted values + case 231: // 231 == 9 + case 234: // 234 == 12 + case 205: // 205 == 13 + case 138: // 138 == 15 + case 112: // 112 == 16 + return value->toLong(3) == 8 ? 0 : -1; // 8 == 2 + default: + break; + } + return -1; + } } // namespace Internal } // namespace Exiv2 // ***************************************************************************** // local definitions namespace { - std::string getExifModel(Exiv2::Internal::TiffComponent* const pRoot) + const Exiv2::Value* getExifValue(Exiv2::Internal::TiffComponent* const pRoot, const uint16_t& tag, const Exiv2::Internal::IfdId& group) { - Exiv2::Internal::TiffFinder finder(0x0110, Exiv2::Internal::ifd0Id); // Exif.Image.Model + Exiv2::Internal::TiffFinder finder(tag, group); + if (!pRoot) + return nullptr; pRoot->accept(finder); auto te = dynamic_cast(finder.result()); - if (!te || !te->pValue() || te->pValue()->count() == 0) return std::string(); - return te->pValue()->toString(); + return (!te || !te->pValue()) ? nullptr : te->pValue(); + } + + std::string getExifModel(Exiv2::Internal::TiffComponent* const pRoot) + { + // Lookup the Exif.Image.Model tag + const auto value = getExifValue(pRoot, 0x0110, Exiv2::Internal::ifd0Id); + return (!value || value->count() == 0) ? std::string("") : std::string(value->toString()); } void ncrypt(Exiv2::byte* pData, uint32_t size, uint32_t count, uint32_t serial) diff --git a/src/makernote_int.hpp b/src/makernote_int.hpp index abc1aa0994..e4a70fa613 100644 --- a/src/makernote_int.hpp +++ b/src/makernote_int.hpp @@ -718,6 +718,17 @@ namespace Exiv2 { */ int sony2FpSelector(uint16_t tag, const byte* pData, uint32_t size, TiffComponent* const pRoot); + /*! + @brief Function to select cfg + def of the SonyMisc2b (tag 9404b) complex binary array. + + @param tag Tag number of the binary array + @param pData Pointer to the raw array data. + @param size Size of the array data. + @param pRoot Pointer to the root component of the TIFF tree. + @return An index into the array set, -1 if no match was found. + */ + int sonyMisc2bSelector(uint16_t tag, const byte* pData, uint32_t size, TiffComponent* const pRoot); + /*! @brief Function to select cfg + def of a Nikon complex binary array. diff --git a/src/sonymn_int.cpp b/src/sonymn_int.cpp index 8a2e7d59af..0cc9fe6f31 100644 --- a/src/sonymn_int.cpp +++ b/src/sonymn_int.cpp @@ -35,6 +35,7 @@ #include #include #include +#include // ***************************************************************************** // class member definitions @@ -908,6 +909,107 @@ namespace Exiv2 { return os; } + //! Lookup table to translate Sony Exposure Program 3 values to readable labels + constexpr TagDetails sonyExposureProgram3[] = { + { 0, N_("Program AE") }, + { 1, N_("Aperture-priority AE") }, + { 2, N_("Shutter speed priority AE") }, + { 3, N_("Manual") }, + { 4, N_("Auto") }, + { 5, N_("iAuto") }, + { 6, N_("Superior Auto") }, + { 7, N_("iAuto+") }, + { 8, N_("Portrait") }, + { 9, N_("Landscape") }, + { 10, N_("Twilight") }, + { 11, N_("Twilight Portrait") }, + { 12, N_("Sunset") }, + { 14, N_("Action (High speed)") }, + { 16, N_("Sports") }, + { 17, N_("Handheld Night Shot") }, + { 18, N_("Anti Motion Blur") }, + { 19, N_("High Sensitivity") }, + { 21, N_("Beach") }, + { 22, N_("Snow") }, + { 23, N_("Fireworks") }, + { 26, N_("Underwater") }, + { 27, N_("Gourmet") }, + { 28, N_("Pet") }, + { 29, N_("Macro") }, + { 30, N_("Backlight Correction HDR") }, + { 33, N_("Sweep Panorama") }, + { 36, N_("Background Defocus") }, + { 37, N_("Soft Skin") }, + { 42, N_("3D Image") }, + { 43, N_("Cont. Priority AE") }, + { 45, N_("Document") }, + { 46, N_("Party") } + }; + + //! Sony Tag 9404b SonyMisc2b tags + constexpr TagInfo SonyMakerNote::tagInfoSonyMisc2b_[] = { + {12, "ExposureProgram", N_("Exposure program"), + N_("Exposure program"), + sonyMisc2bId, makerTags, unsignedByte, -1, EXV_PRINT_TAG(sonyExposureProgram3)}, + {14, "IntelligentAuto", N_("Intelligent auto"), + N_("Whether intelligent auto was used"), + sonyMisc2bId, makerTags, unsignedByte, -1, printMinoltaSonyBoolValue}, + {30, "LensZoomPosition", N_("Lens zoom position"), + N_("Lens zoom position (in %)"), + sonyMisc2bId, makerTags, unsignedShort, -1, printSonyMisc2bLensZoomPosition}, + {32, "FocusPosition2", N_("Focus position 2"), + N_("Focus position 2"), + sonyMisc2bId, makerTags, unsignedByte, -1, printSonyMisc2bFocusPosition2}, + // End of list marker + {0xffff, "(UnknownSonyMisc2bTag)", "(Unknown SonyMisc2b tag)", + "(Unknown SonyMisc2b tag)", + sonyMisc2bId, makerTags, unsignedByte, -1, printValue} + }; + + const TagInfo* SonyMakerNote::tagListSonyMisc2b() + { + return tagInfoSonyMisc2b_; + } + + std::ostream& SonyMakerNote::printSonyMisc2bLensZoomPosition(std::ostream& os, const Value& value, const ExifData* metadata) + { + if (value.count() != 1) + return os << "(" << value << ")"; + + auto pos = metadata->findKey(ExifKey("Exif.Image.Model")); + if (pos == metadata->end()) + return os << "(" << value << ")"; + + // Models that do not support this tag + std::string model = pos->toString(); + for (auto& m : { "SLT-", "HV", "ILCA-"}) { + if (model.find(m) != std::string::npos) + return os << N_("n/a"); + } + + os << std::round((value.toLong()/10.24)) << "%"; + + return os; + } + + std::ostream& SonyMakerNote::printSonyMisc2bFocusPosition2(std::ostream& os, const Value& value, const ExifData* metadata) + { + if (value.count() != 1) + return os << "(" << value << ")"; + + auto pos = metadata->findKey(ExifKey("Exif.Image.Model")); + if (pos == metadata->end()) + return os << "(" << value << ")"; + + // Models that do not support this tag + std::string model = pos->toString(); + for (auto& m : { "SLT-", "HV", "ILCA-"}) { + if (model.find(m) != std::string::npos) + return os << N_("n/a"); + } + + return os << value; + } //! Sony Tag 2010 Sony2010 (Miscellaneous) constexpr TagInfo SonyMakerNote::tagInfo2010e_[] = { diff --git a/src/sonymn_int.hpp b/src/sonymn_int.hpp index 877e2b8498..479a2f6d9d 100644 --- a/src/sonymn_int.hpp +++ b/src/sonymn_int.hpp @@ -51,6 +51,8 @@ namespace Exiv2 { static const TagInfo* tagListFp(); //! Return read-only list of built-in Sony Misc1 tags (Tag 9403) static const TagInfo* tagListSonyMisc1(); + //! Return read-only list of built-in Sony Misc2b tags (Tag 9404) + static const TagInfo* tagListSonyMisc2b(); static const TagInfo* tagList2010e(); @@ -62,6 +64,10 @@ namespace Exiv2 { static std::ostream& printSony2FpFocusPosition2(std::ostream&, const Value&, const ExifData* metadata); //! Print Sony temperature values (in degrees Celsius) static std::ostream& printTemperatureInDegC(std::ostream&, const Value&, const ExifData*); + //! Print SonyMisc2b Lens Zoom Position value + static std::ostream& printSonyMisc2bLensZoomPosition(std::ostream&, const Value&, const ExifData* metadata); + //! Print SonyMisc2b Focus Position 2 value + static std::ostream& printSonyMisc2bFocusPosition2(std::ostream&, const Value&, const ExifData* metadata); //! Print Sony Camera Model static std::ostream& print0xb000(std::ostream&, const Value&, const ExifData*); //! Print Full and Preview Image size @@ -74,6 +80,7 @@ namespace Exiv2 { static const TagInfo tagInfoCs2_[]; static const TagInfo tagInfoFp_[]; static const TagInfo tagInfoSonyMisc1_[]; + static const TagInfo tagInfoSonyMisc2b_[]; static const TagInfo tagInfo2010e_[]; }; // class SonyMakerNote diff --git a/src/tags_int.cpp b/src/tags_int.cpp index a96433890f..3668d45aa6 100644 --- a/src/tags_int.cpp +++ b/src/tags_int.cpp @@ -168,6 +168,7 @@ namespace Exiv2 { { sony2Cs2Id, "Makernote", "Sony2Cs2", SonyMakerNote::tagListCs2 }, { sony2FpId, "Makernote", "Sony2Fp", SonyMakerNote::tagListFp }, { sonyMisc1Id, "Makernote", "SonyMisc1", SonyMakerNote::tagListSonyMisc1}, + { sonyMisc2bId, "Makernote", "SonyMisc2b", SonyMakerNote::tagListSonyMisc2b}, { sony2010eId, "Makernote", "Sony2010e", SonyMakerNote::tagList2010e }, { lastId, "(Last IFD info)", "(Last IFD item)", nullptr } }; diff --git a/src/tags_int.hpp b/src/tags_int.hpp index 879a0fe57e..c71fb01986 100644 --- a/src/tags_int.hpp +++ b/src/tags_int.hpp @@ -175,6 +175,7 @@ namespace Exiv2 { sony2Cs2Id, sony2FpId, sonyMisc1Id, + sonyMisc2bId, sony2010eId, sony1MltCs7DId, sony1MltCsOldId, diff --git a/src/tiffimage_int.cpp b/src/tiffimage_int.cpp index 7ffedf67bb..99c2973868 100644 --- a/src/tiffimage_int.cpp +++ b/src/tiffimage_int.cpp @@ -846,6 +846,29 @@ namespace Exiv2 { { 0x05, ttSignedByte , 1 }, // Exif.SonyMisc1.CameraTemperature }; + constexpr ArrayCfg sonyMisc2bCfg = { + sonyMisc2bId, // Group for the elements + littleEndian, // Little endian + ttUnsignedByte, // Type for array entry and size element + sonyTagDecipher, // (uint16_t, const byte*, uint32_t, TiffComponent* const); + false, // No size element + false, // No fillers + false, // Don't concatenate gaps + { 0, ttUnsignedByte, 1 } + }; + + constexpr ArrayDef sonyMisc2bDef[] = { + { 12, ttUnsignedByte , 1 }, // Exif.SonyMisc2b.ExposureProgram + { 14, ttUnsignedByte , 1 }, // Exif.SonyMisc2b.IntelligentAuto + { 30, ttUnsignedShort , 1 }, // Exif.SonyMisc2b.LensZoomPosition + { 32, ttUnsignedByte , 1 }, // Exif.SonyMisc2b.FocusPosition2 + }; + + //! SonyMisc2b configurations and definitions + constexpr ArraySet sonyMisc2bSet[] = { + { sonyMisc2bCfg, sonyMisc2bDef, EXV_COUNTOF(sonyMisc2bDef) } + }; + constexpr ArrayCfg sony2010eCfg = { sony2010eId, // Group for the elements invalidByteOrder, // inherit from file. Usually littleEndian @@ -1117,6 +1140,7 @@ namespace Exiv2 { { Tag::root, sony2010eId, sony1Id, 0x2010 }, { Tag::root, sony2FpId, sony1Id, 0x9402 }, { Tag::root, sonyMisc1Id, sony1Id, 0x9403 }, + { Tag::root, sonyMisc2bId, sony1Id, 0x9404 }, { Tag::root, sony1CsId, sony1Id, 0x0114 }, { Tag::root, sony1Cs2Id, sony1Id, 0x0114 }, { Tag::root, sonyMltId, sony1Id, 0xb028 }, @@ -1128,6 +1152,7 @@ namespace Exiv2 { { Tag::root, sony2010eId, sony2Id, 0x2010 }, { Tag::root, sony2FpId, sony2Id, 0x9402 }, { Tag::root, sonyMisc1Id, sony2Id, 0x9403 }, + { Tag::root, sonyMisc2bId, sony2Id, 0x9404 }, { Tag::root, sony2CsId, sony2Id, 0x0114 }, { Tag::root, sony2Cs2Id, sony2Id, 0x0114 }, { Tag::root, minoltaId, exifId, 0x927c }, @@ -1566,6 +1591,10 @@ namespace Exiv2 { { Tag::all, sony2FpId, newTiffBinaryElement }, { 0x9402, sony1Id, EXV_COMPLEX_BINARY_ARRAY(sony2FpSet, sony2FpSelector) }, + // Tag 0x9404 SonyMisc2b + { Tag::all, sonyMisc2bId, newTiffBinaryElement }, + { 0x9404, sony1Id, EXV_COMPLEX_BINARY_ARRAY(sonyMisc2bSet, sonyMisc2bSelector) }, + // Tag 0x9403 SonyMisc1 { Tag::all, sonyMisc1Id, newTiffBinaryElement }, { 0x9403, sony1Id, EXV_BINARY_ARRAY(sonyMisc1Cfg, sonyMisc1Def) }, @@ -1591,6 +1620,10 @@ namespace Exiv2 { { Tag::all, sonyMisc1Id, newTiffBinaryElement }, { 0x9403, sony2Id, EXV_BINARY_ARRAY(sonyMisc1Cfg, sonyMisc1Def) }, + // Tag 0x9404 SonyMisc2b + { Tag::all, sonyMisc2bId, newTiffBinaryElement }, + { 0x9404, sony2Id, EXV_COMPLEX_BINARY_ARRAY(sonyMisc2bSet, sonyMisc2bSelector) }, + // Sony2 makernote { 0x0114, sony2Id, EXV_COMPLEX_BINARY_ARRAY(sony2CsSet, sonyCsSelector) }, { Tag::next, sony2Id, ignoreTiffComponent }, diff --git a/tests/bugfixes/github/test_pr_1792.py b/tests/bugfixes/github/test_pr_1792.py new file mode 100644 index 0000000000..b2643faf48 --- /dev/null +++ b/tests/bugfixes/github/test_pr_1792.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- + +from system_tests import CaseMeta, path + +class SonyMisc2bTestSupported(metaclass=CaseMeta): + + filename = path("$data_path/test_issue_1464.exv") + commands = ["$exiv2 -pa --grep SonyMisc2b $filename"] + + stdout = ["""Exif.SonyMisc2b.ExposureProgram Byte 1 Shutter speed priority AE +Exif.SonyMisc2b.IntelligentAuto Byte 1 Off +Exif.SonyMisc2b.LensZoomPosition Short 1 100% +Exif.SonyMisc2b.FocusPosition2 Byte 1 0 +""" + ] + stderr = [""] + retval = [0] + +# An example of a Sony camera model that does NOT support SonyMisc2b +class SonyMisc2bTestUnsupported(metaclass=CaseMeta): + + filename = path("$data_path/exiv2-pr906.exv") + commands = ["$exiv2 -pa --grep SonyMisc2b $filename"] + + stdout = ["""""" + ] + stderr = [""] + retval = [1]