From patchwork Sat Jan 23 05:16:57 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 10966 X-Patchwork-Delegate: paul.elder@ideasonboard.com Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 3B5D5C0F2B for ; Sat, 23 Jan 2021 05:17:18 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 0740968261; Sat, 23 Jan 2021 06:17:18 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="dj9up+Cx"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 19AE368261 for ; Sat, 23 Jan 2021 06:17:16 +0100 (CET) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 64B64813; Sat, 23 Jan 2021 06:17:14 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1611379035; bh=9WkXGyOtEF2dbo92v7IcbVD/EwSZEIh7QEoB2XMX8Dc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=dj9up+Cx4nyvKIZ4LtWBYj3yLxrI+VwZMMfEggYFwyLVPHzORL//mqSsccWyaFh8E 5F9Bd4iblpgGSyeTHOYaIO9W54JvQerzfjdwPMEaZD+n7rOBfXbFOzLIJ8rwPqcean yayZvkJfrZ2mxaRSeUcgxLrKpFHeS5RlquNeUjeA= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Sat, 23 Jan 2021 14:16:57 +0900 Message-Id: <20210123051704.188117-2-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20210123051704.188117-1-paul.elder@ideasonboard.com> References: <20210123051704.188117-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 1/8] android: jpeg: exif: Expand setString to support different encodings X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" GPSProcessingMethod and UserComment in EXIF tags can be in UTF-16. Expand setString to take an encoding when the field type is undefined. Update callers accordingly. Signed-off-by: Paul Elder Reviewed-by: Laurent Pinchart --- Changes in v3: - use array to contain string encoding codes - drop JIS and Undefined - clean up setString a bit - use the endianness of the exif Changes in v2: - moved from utils into exif - support no-encoding --- src/android/jpeg/exif.cpp | 88 +++++++++++++++++++++++++++++++++++---- src/android/jpeg/exif.h | 10 ++++- 2 files changed, 89 insertions(+), 9 deletions(-) diff --git a/src/android/jpeg/exif.cpp b/src/android/jpeg/exif.cpp index 33b3fa7f..5d9492fb 100644 --- a/src/android/jpeg/exif.cpp +++ b/src/android/jpeg/exif.cpp @@ -7,6 +7,9 @@ #include "exif.h" +#include +#include + #include "libcamera/internal/log.h" #include "libcamera/internal/utils.h" @@ -178,11 +181,18 @@ void Exif::setRational(ExifIfd ifd, ExifTag tag, ExifRational item) exif_entry_unref(entry); } -void Exif::setString(ExifIfd ifd, ExifTag tag, ExifFormat format, const std::string &item) +static const std::map> stringEncodingCodes = { + { Exif::ASCII, { 0x41, 0x53, 0x43, 0x49, 0x49, 0x00, 0x00, 0x00 } }, + { Exif::Unicode, { 0x55, 0x4e, 0x49, 0x43, 0x4f, 0x44, 0x45, 0x00 } }, +}; + +void Exif::setString(ExifIfd ifd, ExifTag tag, ExifFormat format, + const std::string &item, StringEncoding encoding) { std::string ascii; size_t length; const char *str; + std::vector buf; if (format == EXIF_FORMAT_ASCII) { ascii = utils::toAscii(item); @@ -191,13 +201,47 @@ void Exif::setString(ExifIfd ifd, ExifTag tag, ExifFormat format, const std::str /* Pad 1 extra byte to null-terminate the ASCII string. */ length = ascii.length() + 1; } else { - str = item.c_str(); - - /* - * Strings stored in different formats (EXIF_FORMAT_UNDEFINED) - * are not null-terminated. - */ - length = item.length(); + std::u16string u16str; + + auto encodingString = stringEncodingCodes.find(encoding); + if (encodingString != stringEncodingCodes.end()) { + buf = { + encodingString->second.begin(), + encodingString->second.end() + }; + } + + switch (encoding) { + case Unicode: + u16str = utf8ToUtf16(item); + + buf.resize(8 + u16str.size() * 2); + for (size_t i = 0; i < u16str.size(); i++) { + if (order_ == EXIF_BYTE_ORDER_INTEL) { + buf[8 + 2 * i] = (u16str[i] & 0xff); + buf[8 + 2 * i + 1] = ((u16str[i] >> 8) & 0xff); + } else { + buf[8 + 2 * i] = ((u16str[i] >> 8) & 0xff); + buf[8 + 2 * i + 1] = (u16str[i] & 0xff); + } + } + + str = reinterpret_cast(buf.data()); + length = buf.size(); + + break; + case ASCII: + case NoEncoding: + buf.insert(buf.end(), item.begin(), item.end()); + str = reinterpret_cast(buf.data()); + + /* + * Strings stored in different formats (EXIF_FORMAT_UNDEFINED) + * are not null-terminated. + */ + length = buf.size(); + break; + } } ExifEntry *entry = createEntry(ifd, tag, format, length, length); @@ -290,6 +334,34 @@ void Exif::setThumbnail(Span thumbnail, setShort(EXIF_IFD_0, EXIF_TAG_COMPRESSION, compression); } +/** + * \brief Convert UTF-8 string to UTF-16 string + * \param[in] str String to convert + * + * \return \a str in UTF-16 + */ +std::u16string Exif::utf8ToUtf16(const std::string &str) +{ + mbstate_t state{}; + char16_t c16; + const char *ptr = str.data(); + const char *end = ptr + str.size(); + + std::u16string ret; + while (size_t rc = mbrtoc16(&c16, ptr, end - ptr + 1, &state)) { + if (rc == static_cast(-2) || + rc == static_cast(-1)) + break; + + ret.push_back(c16); + + if (rc > 0) + ptr += rc; + } + + return ret; +} + [[nodiscard]] int Exif::generate() { if (exifData_) { diff --git a/src/android/jpeg/exif.h b/src/android/jpeg/exif.h index 5cab4559..73f231b2 100644 --- a/src/android/jpeg/exif.h +++ b/src/android/jpeg/exif.h @@ -26,6 +26,12 @@ public: JPEG = 6, }; + enum StringEncoding { + NoEncoding = 0, + ASCII = 1, + Unicode = 2, + }; + void setMake(const std::string &make); void setModel(const std::string &model); @@ -46,9 +52,11 @@ private: void setShort(ExifIfd ifd, ExifTag tag, uint16_t item); void setLong(ExifIfd ifd, ExifTag tag, uint32_t item); void setString(ExifIfd ifd, ExifTag tag, ExifFormat format, - const std::string &item); + const std::string &item, StringEncoding encoding = NoEncoding); void setRational(ExifIfd ifd, ExifTag tag, ExifRational item); + std::u16string utf8ToUtf16(const std::string &str); + bool valid_; ExifData *data_;