Blame src/convert.cpp

Packit 01d647
// ***************************************************************** -*- C++ -*-
Packit 01d647
/*
Packit 01d647
 * Copyright (C) 2004-2018 Exiv2 authors
Packit 01d647
 * This program is part of the Exiv2 distribution.
Packit 01d647
 *
Packit 01d647
 * This program is free software; you can redistribute it and/or
Packit 01d647
 * modify it under the terms of the GNU General Public License
Packit 01d647
 * as published by the Free Software Foundation; either version 2
Packit 01d647
 * of the License, or (at your option) any later version.
Packit 01d647
 *
Packit 01d647
 * This program is distributed in the hope that it will be useful,
Packit 01d647
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 01d647
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 01d647
 * GNU General Public License for more details.
Packit 01d647
 *
Packit 01d647
 * You should have received a copy of the GNU General Public License
Packit 01d647
 * along with this program; if not, write to the Free Software
Packit 01d647
 * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA.
Packit 01d647
 */
Packit 01d647
/*
Packit 01d647
  File:      convert.cpp
Packit 01d647
  Author(s): Andreas Huggel (ahu) <ahuggel@gmx.net>
Packit 01d647
             Vladimir Nadvornik (vn) <nadvornik@suse.cz>
Packit 01d647
  History:   17-Mar-08, ahu: created basic converter framework
Packit 01d647
             20-May-08, vn:  added actual conversion logic
Packit 01d647
 */
Packit 01d647
// *****************************************************************************
Packit 01d647
// included header files
Packit 01d647
#include "config.h"
Packit 01d647
#include "types.hpp"
Packit 01d647
#include "error.hpp"
Packit 01d647
#include "exif.hpp"
Packit 01d647
#include "iptc.hpp"
Packit 01d647
#include "xmp_exiv2.hpp"
Packit 01d647
#include "futils.hpp"
Packit 01d647
#include "convert.hpp"
Packit 01d647
#include "unused.h"
Packit 01d647
Packit 01d647
// + standard includes
Packit 01d647
#include <utility>
Packit 01d647
#include <iostream>
Packit 01d647
#include <iomanip>
Packit 01d647
#include <ios>
Packit 01d647
#include <sstream>
Packit 01d647
#include <stdio.h> // for snprintf (C99)
Packit 01d647
#ifdef _MSC_VER
Packit 01d647
# define snprintf _snprintf
Packit 01d647
#endif
Packit 01d647
#include <cstring>
Packit 01d647
Packit 01d647
#if defined WIN32 && !defined __CYGWIN__
Packit 01d647
# include <windows.h>
Packit 01d647
#endif
Packit 01d647
Packit 01d647
#ifdef EXV_HAVE_ICONV
Packit 01d647
# include <iconv.h>
Packit 01d647
# include <errno.h>
Packit 01d647
#endif
Packit 01d647
Packit 01d647
// Adobe XMP Toolkit
Packit 01d647
#ifdef EXV_HAVE_XMP_TOOLKIT
Packit 01d647
# define TXMP_STRING_TYPE std::string
Packit 01d647
# ifdef EXV_ADOBE_XMPSDK
Packit 01d647
#  include <XMP.hpp>
Packit 01d647
# else
Packit 01d647
#  include <XMPSDK.hpp>
Packit 01d647
# endif
Packit 01d647
# include <MD5.h>
Packit 01d647
#endif // EXV_HAVE_XMP_TOOLKIT
Packit 01d647
Packit 01d647
// *****************************************************************************
Packit 01d647
// local declarations
Packit 01d647
namespace {
Packit 01d647
#if defined WIN32 && !defined __CYGWIN__
Packit 01d647
    // Convert string charset with Windows functions.
Packit 01d647
    bool convertStringCharsetWindows(std::string& str, const char* from, const char* to);
Packit 01d647
#endif
Packit 01d647
#if defined EXV_HAVE_ICONV
Packit 01d647
    // Convert string charset with iconv.
Packit 01d647
    bool convertStringCharsetIconv(std::string& str, const char* from, const char* to);
Packit 01d647
#endif
Packit 01d647
    /*!
Packit 01d647
      @brief Get the text value of an XmpDatum \em pos.
Packit 01d647
Packit 01d647
      If \em pos refers to a LangAltValue, \em value is set to the default language
Packit 01d647
      entry without the x-default qualifier. If there is no default but
Packit 01d647
      exactly one entry, \em value is set to this entry, without the qualifier.
Packit 01d647
      The return code indicates if the operation was successful.
Packit 01d647
     */
Packit 01d647
    bool getTextValue(std::string& value, const Exiv2::XmpData::iterator& pos);
Packit 01d647
}
Packit 01d647
Packit 01d647
// *****************************************************************************
Packit 01d647
// class member definitions
Packit 01d647
namespace Exiv2 {
Packit 01d647
Packit 01d647
    //! Metadata conversions.
Packit 01d647
    class Converter {
Packit 01d647
    public:
Packit 01d647
        /*!
Packit 01d647
          @brief Type for metadata converter functions, taking two key strings,
Packit 01d647
                 \em from and \em to.
Packit 01d647
Packit 01d647
          These functions have access to both the source and destination metadata
Packit 01d647
          containers and store the result directly in the destination container.
Packit 01d647
         */
Packit 01d647
        typedef void (Converter::*ConvertFct)(const char* from, const char* to);
Packit 01d647
        //! Structure to define conversions between two keys.
Packit 01d647
        struct Conversion {
Packit 01d647
            MetadataId  metadataId_; //!< Type of metadata for the first key.
Packit 01d647
            const char* key1_;       //!< First metadata key.
Packit 01d647
            const char* key2_;       //!< Second metadata key (always an XMP key for now).
Packit 01d647
            ConvertFct  key1ToKey2_; //!< Conversion from first to second key.
Packit 01d647
            ConvertFct  key2ToKey1_; //!< Conversion from second to first key.
Packit 01d647
        };
Packit 01d647
    public:
Packit 01d647
        //! @name Creators
Packit 01d647
        //@{
Packit 01d647
        //! Constructor for Exif tags and XMP properties.
Packit 01d647
        Converter(ExifData& exifData, XmpData& xmpData);
Packit 01d647
        //! Constructor for Iptc tags and XMP properties.
Packit 01d647
        Converter(IptcData& iptcData, XmpData& xmpData, const char *iptcCharset = 0);
Packit 01d647
        //@}
Packit 01d647
Packit 01d647
        //! @name Manipulators
Packit 01d647
        //@{
Packit 01d647
        //! Convert Exif tags or IPTC datasets to XMP properties according to the conversion table.
Packit 01d647
        void cnvToXmp();
Packit 01d647
        //! Convert XMP properties to Exif tags or IPTC datasets according to the conversion table.
Packit 01d647
        void cnvFromXmp();
Packit 01d647
        /*!
Packit 01d647
          @brief Set the erase flag.
Packit 01d647
Packit 01d647
          This flag indicates whether successfully converted source records are erased.
Packit 01d647
         */
Packit 01d647
        void setErase(bool onoff =true) { erase_ = onoff; }
Packit 01d647
        /*!
Packit 01d647
          @brief Set the overwrite flag.
Packit 01d647
Packit 01d647
          This flag indicates whether existing target records are overwritten.
Packit 01d647
         */
Packit 01d647
        void setOverwrite(bool onoff =true) { overwrite_ = onoff; }
Packit 01d647
        //@}
Packit 01d647
Packit 01d647
        //! @name Conversion functions (manipulators)
Packit 01d647
        //@{
Packit 01d647
        /*!
Packit 01d647
          @brief Do nothing conversion function.
Packit 01d647
Packit 01d647
          Use when, for example, a one-way conversion is needed.
Packit 01d647
         */
Packit 01d647
        void cnvNone(const char*, const char*);
Packit 01d647
        /*!
Packit 01d647
          @brief Simple Exif to XMP conversion function.
Packit 01d647
Packit 01d647
          Sets the XMP property to an XmpText value containing the Exif value string.
Packit 01d647
         */
Packit 01d647
        void cnvExifValue(const char* from, const char* to);
Packit 01d647
        /*!
Packit 01d647
          @brief Convert the tag Exif.Photo.UserComment to XMP.
Packit 01d647
Packit 01d647
          Todo: Convert the Exif comment to UTF-8 if necessary.
Packit 01d647
         */
Packit 01d647
        void cnvExifComment(const char* from, const char* to);
Packit 01d647
        /*!
Packit 01d647
          @brief Converts Exif tag with multiple components to XMP array.
Packit 01d647
Packit 01d647
          Converts Exif tag with multiple components to XMP array. This function is
Packit 01d647
          used for ComponentsConfiguration tag.
Packit 01d647
         */
Packit 01d647
        void cnvExifArray(const char* from, const char* to);
Packit 01d647
        /*!
Packit 01d647
          @brief Exif date to XMP conversion function.
Packit 01d647
Packit 01d647
          Sets the XMP property to an XmpText value containing date and time. This function
Packit 01d647
          combines values from multiple Exif tags as described in XMP specification. It
Packit 01d647
          is used for DateTime, DateTimeOriginal, DateTimeDigitized and GPSTimeStamp.
Packit 01d647
         */
Packit 01d647
        void cnvExifDate(const char* from, const char* to);
Packit 01d647
        /*!
Packit 01d647
          @brief Exif version to XMP conversion function.
Packit 01d647
Packit 01d647
          Converts ExifVersion tag to XmpText value.
Packit 01d647
         */
Packit 01d647
        void cnvExifVersion(const char* from, const char* to);
Packit 01d647
        /*!
Packit 01d647
          @brief Exif GPS version to XMP conversion function.
Packit 01d647
Packit 01d647
          Converts GPSVersionID tag to XmpText value.
Packit 01d647
         */
Packit 01d647
        void cnvExifGPSVersion(const char* from, const char* to);
Packit 01d647
        /*!
Packit 01d647
          @brief Exif Flash to XMP conversion function.
Packit 01d647
Packit 01d647
          Converts Flash tag to XMP structure.
Packit 01d647
         */
Packit 01d647
        void cnvExifFlash(const char* from, const char* to);
Packit 01d647
        /*!
Packit 01d647
          @brief Exif GPS coordinate to XMP conversion function.
Packit 01d647
Packit 01d647
          Converts GPS coordinates tag to XmpText value. It combines multiple Exif tags
Packit 01d647
          as described in XMP specification.
Packit 01d647
         */
Packit 01d647
        void cnvExifGPSCoord(const char* from, const char* to);
Packit 01d647
        /*!
Packit 01d647
          @brief Simple XMP to Exif conversion function.
Packit 01d647
Packit 01d647
          Sets the Exif tag according to the XMP property.
Packit 01d647
          For LangAlt values, only the x-default entry is used.
Packit 01d647
Packit 01d647
          Todo: Escape non-ASCII characters in XMP text values
Packit 01d647
         */
Packit 01d647
        void cnvXmpValue(const char* from, const char* to);
Packit 01d647
        /*!
Packit 01d647
          @brief Convert the tag Xmp.exif.UserComment to Exif.
Packit 01d647
         */
Packit 01d647
        void cnvXmpComment(const char* from, const char* to);
Packit 01d647
        /*!
Packit 01d647
          @brief Converts XMP array to Exif tag with multiple components.
Packit 01d647
Packit 01d647
          Converts XMP array to Exif tag with multiple components. This function is
Packit 01d647
          used for ComponentsConfiguration tag.
Packit 01d647
         */
Packit 01d647
        void cnvXmpArray(const char* from, const char* to);
Packit 01d647
        /*!
Packit 01d647
          @brief XMP to Exif date conversion function.
Packit 01d647
Packit 01d647
          Converts the XmpText value to Exif date and time. This function
Packit 01d647
          sets multiple Exif tags as described in XMP specification. It
Packit 01d647
          is used for DateTime, DateTimeOriginal, DateTimeDigitized and GPSTimeStamp.
Packit 01d647
         */
Packit 01d647
        void cnvXmpDate(const char* from, const char* to);
Packit 01d647
        /*!
Packit 01d647
          @brief XMP to Exif version conversion function.
Packit 01d647
Packit 01d647
          Converts XmpText value to ExifVersion tag.
Packit 01d647
         */
Packit 01d647
        void cnvXmpVersion(const char* from, const char* to);
Packit 01d647
        /*!
Packit 01d647
          @brief XMP to Exif GPS version conversion function.
Packit 01d647
Packit 01d647
          Converts XmpText value to GPSVersionID tag.
Packit 01d647
         */
Packit 01d647
        void cnvXmpGPSVersion(const char* from, const char* to);
Packit 01d647
        /*!
Packit 01d647
          @brief XMP to Exif Flash conversion function.
Packit 01d647
Packit 01d647
          Converts XMP structure to Flash tag.
Packit 01d647
         */
Packit 01d647
        void cnvXmpFlash(const char* from, const char* to);
Packit 01d647
        /*!
Packit 01d647
          @brief XMP to Exif GPS coordinate conversion function.
Packit 01d647
Packit 01d647
          Converts XmpText value to GPS coordinates tags. It sets multiple Exif tags
Packit 01d647
          as described in XMP specification.
Packit 01d647
         */
Packit 01d647
        void cnvXmpGPSCoord(const char* from, const char* to);
Packit 01d647
        /*!
Packit 01d647
          @brief IPTC dataset to XMP conversion function.
Packit 01d647
Packit 01d647
          Multiple IPTC datasets with the same key are converted to an XMP array.
Packit 01d647
         */
Packit 01d647
        void cnvIptcValue(const char* from, const char* to);
Packit 01d647
        /*!
Packit 01d647
          @brief XMP to IPTC dataset conversion function.
Packit 01d647
Packit 01d647
          Each array element of an XMP array value is added as one IPTC dataset.
Packit 01d647
         */
Packit 01d647
        void cnvXmpValueToIptc(const char* from, const char* to);
Packit 01d647
        /*!
Packit 01d647
          @brief Write exif:NativeDigest and tiff:NativeDigest properties to XMP.
Packit 01d647
Packit 01d647
          Compute digests from Exif values and write them to  exif:NativeDigest
Packit 01d647
          and tiff:NativeDigest properties. This should be compatible with XMP SDK.
Packit 01d647
         */
Packit 01d647
        void writeExifDigest();
Packit 01d647
        /*!
Packit 01d647
          @brief Copies metadata in appropriate direction.
Packit 01d647
Packit 01d647
          From values of exif:NativeDigest and tiff:NativeDigest detects which of
Packit 01d647
          XMP and Exif was updated more recently and copies metadata in appropriate direction.
Packit 01d647
         */
Packit 01d647
        void syncExifWithXmp();
Packit 01d647
        //@}
Packit 01d647
Packit 01d647
        //! @name Accessors
Packit 01d647
        //@{
Packit 01d647
        //! Get the value of the erase flag, see also setErase(bool on).
Packit 01d647
        bool erase() const { return erase_; }
Packit 01d647
        //! Get the value of the overwrite flag, see also setOverwrite(bool on).
Packit 01d647
        bool overwrite() const { return overwrite_; }
Packit 01d647
        //@}
Packit 01d647
Packit 01d647
    private:
Packit 01d647
        bool prepareExifTarget(const char* to, bool force =false);
Packit 01d647
        bool prepareIptcTarget(const char* to, bool force =false);
Packit 01d647
        bool prepareXmpTarget(const char* to, bool force =false);
Packit 01d647
        std::string computeExifDigest(bool tiff);
Packit 01d647
        std::string computeIptcDigest();
Packit 01d647
Packit 01d647
        // DATA
Packit 01d647
        static const Conversion conversion_[];  //
Packit 01d647
        bool erase_;
Packit 01d647
        bool overwrite_;
Packit 01d647
        ExifData *exifData_;
Packit 01d647
        IptcData *iptcData_;
Packit 01d647
        XmpData  *xmpData_;
Packit 01d647
        const char *iptcCharset_;
Packit 01d647
Packit 01d647
    }; // class Converter
Packit 01d647
Packit 01d647
    // Order is important for computing digests
Packit 01d647
    const Converter::Conversion Converter::conversion_[] = {
Packit 01d647
        { mdExif, "Exif.Image.ImageWidth",                "Xmp.tiff.ImageWidth",                &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Image.ImageLength",               "Xmp.tiff.ImageLength",               &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Image.BitsPerSample",             "Xmp.tiff.BitsPerSample",             &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Image.Compression",               "Xmp.tiff.Compression",               &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Image.PhotometricInterpretation", "Xmp.tiff.PhotometricInterpretation", &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Image.Orientation",               "Xmp.tiff.Orientation",               &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Image.SamplesPerPixel",           "Xmp.tiff.SamplesPerPixel",           &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Image.PlanarConfiguration",       "Xmp.tiff.PlanarConfiguration",       &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Image.YCbCrSubSampling",          "Xmp.tiff.YCbCrSubSampling",          &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Image.YCbCrPositioning",          "Xmp.tiff.YCbCrPositioning",          &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Image.XResolution",               "Xmp.tiff.XResolution",               &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Image.YResolution",               "Xmp.tiff.YResolution",               &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Image.ResolutionUnit",            "Xmp.tiff.ResolutionUnit",            &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Image.TransferFunction",          "Xmp.tiff.TransferFunction",          &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Image.WhitePoint",                "Xmp.tiff.WhitePoint",                &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Image.PrimaryChromaticities",     "Xmp.tiff.PrimaryChromaticities",     &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Image.YCbCrCoefficients",         "Xmp.tiff.YCbCrCoefficients",         &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Image.ReferenceBlackWhite",       "Xmp.tiff.ReferenceBlackWhite",       &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Image.DateTime",                  "Xmp.xmp.ModifyDate",                 &Converter::cnvExifDate , &Converter::cnvXmpDate  }, // MWG Guidelines
Packit 01d647
        { mdExif, "Exif.Image.ImageDescription",          "Xmp.dc.description",                 &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Image.Make",                      "Xmp.tiff.Make",                      &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Image.Model",                     "Xmp.tiff.Model",                     &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Image.Software",                  "Xmp.tiff.Software",                  &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Image.Artist",                    "Xmp.dc.creator",                     &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Image.Rating",                    "Xmp.xmp.Rating",                     &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Image.Copyright",                 "Xmp.dc.rights",                      &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Photo.ExifVersion",               "Xmp.exif.ExifVersion",               &Converter::cnvExifVersion, &Converter::cnvXmpVersion },
Packit 01d647
        { mdExif, "Exif.Photo.FlashpixVersion",           "Xmp.exif.FlashpixVersion",           &Converter::cnvExifVersion, &Converter::cnvXmpVersion },
Packit 01d647
        { mdExif, "Exif.Photo.ColorSpace",                "Xmp.exif.ColorSpace",                &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Photo.ComponentsConfiguration",   "Xmp.exif.ComponentsConfiguration",   &Converter::cnvExifArray, &Converter::cnvXmpArray },
Packit 01d647
        { mdExif, "Exif.Photo.CompressedBitsPerPixel",    "Xmp.exif.CompressedBitsPerPixel",    &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Photo.PixelXDimension",           "Xmp.exif.PixelXDimension",           &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Photo.PixelYDimension",           "Xmp.exif.PixelYDimension",           &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Photo.UserComment",               "Xmp.exif.UserComment",               &Converter::cnvExifComment, &Converter::cnvXmpComment },
Packit 01d647
        { mdExif, "Exif.Photo.RelatedSoundFile",          "Xmp.exif.RelatedSoundFile",          &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Photo.DateTimeOriginal",          "Xmp.photoshop.DateCreated",          &Converter::cnvExifDate,  &Converter::cnvXmpDate  }, // MWG Guidelines
Packit 01d647
        { mdExif, "Exif.Photo.DateTimeDigitized",         "Xmp.xmp.CreateDate",                 &Converter::cnvExifDate,  &Converter::cnvXmpDate  }, // MWG Guidelines
Packit 01d647
        { mdExif, "Exif.Photo.ExposureTime",              "Xmp.exif.ExposureTime",              &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Photo.FNumber",                   "Xmp.exif.FNumber",                   &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Photo.ExposureProgram",           "Xmp.exif.ExposureProgram",           &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Photo.SpectralSensitivity",       "Xmp.exif.SpectralSensitivity",       &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Photo.ISOSpeedRatings",           "Xmp.exif.ISOSpeedRatings",           &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Photo.OECF",                      "Xmp.exif.OECF",                      &Converter::cnvExifValue, &Converter::cnvXmpValue }, // FIXME ?
Packit 01d647
        { mdExif, "Exif.Photo.ShutterSpeedValue",         "Xmp.exif.ShutterSpeedValue",         &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Photo.ApertureValue",             "Xmp.exif.ApertureValue",             &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Photo.BrightnessValue",           "Xmp.exif.BrightnessValue",           &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Photo.ExposureBiasValue",         "Xmp.exif.ExposureBiasValue",         &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Photo.MaxApertureValue",          "Xmp.exif.MaxApertureValue",          &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Photo.SubjectDistance",           "Xmp.exif.SubjectDistance",           &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Photo.MeteringMode",              "Xmp.exif.MeteringMode",              &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Photo.LightSource",               "Xmp.exif.LightSource",               &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Photo.Flash",                     "Xmp.exif.Flash",                     &Converter::cnvExifFlash, &Converter::cnvXmpFlash },
Packit 01d647
        { mdExif, "Exif.Photo.FocalLength",               "Xmp.exif.FocalLength",               &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Photo.SubjectArea",               "Xmp.exif.SubjectArea",               &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Photo.FlashEnergy",               "Xmp.exif.FlashEnergy",               &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Photo.SpatialFrequencyResponse",  "Xmp.exif.SpatialFrequencyResponse",  &Converter::cnvExifValue, &Converter::cnvXmpValue }, // FIXME ?
Packit 01d647
        { mdExif, "Exif.Photo.FocalPlaneXResolution",     "Xmp.exif.FocalPlaneXResolution",     &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Photo.FocalPlaneYResolution",     "Xmp.exif.FocalPlaneYResolution",     &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Photo.FocalPlaneResolutionUnit",  "Xmp.exif.FocalPlaneResolutionUnit",  &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Photo.SubjectLocation",           "Xmp.exif.SubjectLocation",           &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Photo.ExposureIndex",             "Xmp.exif.ExposureIndex",             &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Photo.SensingMethod",             "Xmp.exif.SensingMethod",             &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Photo.FileSource",                "Xmp.exif.FileSource",                &Converter::cnvExifValue, &Converter::cnvXmpValue }, // FIXME ?
Packit 01d647
        { mdExif, "Exif.Photo.SceneType",                 "Xmp.exif.SceneType",                 &Converter::cnvExifValue, &Converter::cnvXmpValue }, // FIXME ?
Packit 01d647
        { mdExif, "Exif.Photo.CFAPattern",                "Xmp.exif.CFAPattern",                &Converter::cnvExifValue, &Converter::cnvXmpValue }, // FIXME ?
Packit 01d647
        { mdExif, "Exif.Photo.CustomRendered",            "Xmp.exif.CustomRendered",            &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Photo.ExposureMode",              "Xmp.exif.ExposureMode",              &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Photo.WhiteBalance",              "Xmp.exif.WhiteBalance",              &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Photo.DigitalZoomRatio",          "Xmp.exif.DigitalZoomRatio",          &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Photo.FocalLengthIn35mmFilm",     "Xmp.exif.FocalLengthIn35mmFilm",     &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Photo.SceneCaptureType",          "Xmp.exif.SceneCaptureType",          &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Photo.GainControl",               "Xmp.exif.GainControl",               &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Photo.Contrast",                  "Xmp.exif.Contrast",                  &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Photo.Saturation",                "Xmp.exif.Saturation",                &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Photo.Sharpness",                 "Xmp.exif.Sharpness",                 &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Photo.DeviceSettingDescription",  "Xmp.exif.DeviceSettingDescription",  &Converter::cnvExifValue, &Converter::cnvXmpValue }, // FIXME ?
Packit 01d647
        { mdExif, "Exif.Photo.SubjectDistanceRange",      "Xmp.exif.SubjectDistanceRange",      &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.Photo.ImageUniqueID",             "Xmp.exif.ImageUniqueID",             &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.GPSInfo.GPSVersionID",            "Xmp.exif.GPSVersionID",              &Converter::cnvExifGPSVersion, &Converter::cnvXmpGPSVersion },
Packit 01d647
        { mdExif, "Exif.GPSInfo.GPSLatitude",             "Xmp.exif.GPSLatitude",               &Converter::cnvExifGPSCoord, &Converter::cnvXmpGPSCoord },
Packit 01d647
        { mdExif, "Exif.GPSInfo.GPSLongitude",            "Xmp.exif.GPSLongitude",              &Converter::cnvExifGPSCoord, &Converter::cnvXmpGPSCoord },
Packit 01d647
        { mdExif, "Exif.GPSInfo.GPSAltitudeRef",          "Xmp.exif.GPSAltitudeRef",            &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.GPSInfo.GPSAltitude",             "Xmp.exif.GPSAltitude",               &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.GPSInfo.GPSTimeStamp",            "Xmp.exif.GPSTimeStamp",              &Converter::cnvExifDate,  &Converter::cnvXmpDate  }, // FIXME ?
Packit 01d647
        { mdExif, "Exif.GPSInfo.GPSSatellites",           "Xmp.exif.GPSSatellites",             &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.GPSInfo.GPSStatus",               "Xmp.exif.GPSStatus",                 &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.GPSInfo.GPSMeasureMode",          "Xmp.exif.GPSMeasureMode",            &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.GPSInfo.GPSDOP",                  "Xmp.exif.GPSDOP",                    &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.GPSInfo.GPSSpeedRef",             "Xmp.exif.GPSSpeedRef",               &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.GPSInfo.GPSSpeed",                "Xmp.exif.GPSSpeed",                  &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.GPSInfo.GPSTrackRef",             "Xmp.exif.GPSTrackRef",               &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.GPSInfo.GPSTrack",                "Xmp.exif.GPSTrack",                  &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.GPSInfo.GPSImgDirectionRef",      "Xmp.exif.GPSImgDirectionRef",        &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.GPSInfo.GPSImgDirection",         "Xmp.exif.GPSImgDirection",           &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.GPSInfo.GPSMapDatum",             "Xmp.exif.GPSMapDatum",               &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.GPSInfo.GPSDestLatitude",         "Xmp.exif.GPSDestLatitude",           &Converter::cnvExifGPSCoord, &Converter::cnvXmpGPSCoord },
Packit 01d647
        { mdExif, "Exif.GPSInfo.GPSDestLongitude",        "Xmp.exif.GPSDestLongitude",          &Converter::cnvExifGPSCoord, &Converter::cnvXmpGPSCoord },
Packit 01d647
        { mdExif, "Exif.GPSInfo.GPSDestBearingRef",       "Xmp.exif.GPSDestBearingRef",         &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.GPSInfo.GPSDestBearing",          "Xmp.exif.GPSDestBearing",            &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.GPSInfo.GPSDestDistanceRef",      "Xmp.exif.GPSDestDistanceRef",        &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.GPSInfo.GPSDestDistance",         "Xmp.exif.GPSDestDistance",           &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
        { mdExif, "Exif.GPSInfo.GPSProcessingMethod",     "Xmp.exif.GPSProcessingMethod",       &Converter::cnvExifValue, &Converter::cnvXmpValue }, // FIXME ?
Packit 01d647
        { mdExif, "Exif.GPSInfo.GPSAreaInformation",      "Xmp.exif.GPSAreaInformation",        &Converter::cnvExifValue, &Converter::cnvXmpValue }, // FIXME ?
Packit 01d647
        { mdExif, "Exif.GPSInfo.GPSDifferential",         "Xmp.exif.GPSDifferential",           &Converter::cnvExifValue, &Converter::cnvXmpValue },
Packit 01d647
Packit 01d647
        { mdIptc, "Iptc.Application2.ObjectName",         "Xmp.dc.title",                       &Converter::cnvIptcValue, &Converter::cnvXmpValueToIptc },
Packit 01d647
        { mdIptc, "Iptc.Application2.Urgency",            "Xmp.photoshop.Urgency",              &Converter::cnvIptcValue, &Converter::cnvXmpValueToIptc },
Packit 01d647
        { mdIptc, "Iptc.Application2.Category",           "Xmp.photoshop.Category",             &Converter::cnvIptcValue, &Converter::cnvXmpValueToIptc },
Packit 01d647
        { mdIptc, "Iptc.Application2.SuppCategory",       "Xmp.photoshop.SupplementalCategories", &Converter::cnvIptcValue, &Converter::cnvXmpValueToIptc },
Packit 01d647
        { mdIptc, "Iptc.Application2.Keywords",           "Xmp.dc.subject",                     &Converter::cnvIptcValue, &Converter::cnvXmpValueToIptc },
Packit 01d647
        { mdIptc, "Iptc.Application2.SubLocation",        "Xmp.iptc.Location",                  &Converter::cnvIptcValue, &Converter::cnvXmpValueToIptc },
Packit 01d647
        { mdIptc, "Iptc.Application2.SpecialInstructions","Xmp.photoshop.Instructions",         &Converter::cnvIptcValue, &Converter::cnvXmpValueToIptc },
Packit 01d647
        { mdIptc, "Iptc.Application2.DateCreated",        "Xmp.photoshop.DateCreated",          &Converter::cnvNone, &Converter::cnvXmpValueToIptc }, // FIXME to IPTC Date and IPTC Time
Packit 01d647
        { mdIptc, "Iptc.Application2.DigitizationDate",   "Xmp.xmp.CreateDate",                 &Converter::cnvNone, &Converter::cnvXmpValueToIptc }, // FIXME to IPTC Date and IPTC Time
Packit 01d647
        { mdIptc, "Iptc.Application2.Byline",             "Xmp.dc.creator",                     &Converter::cnvIptcValue, &Converter::cnvXmpValueToIptc },
Packit 01d647
        { mdIptc, "Iptc.Application2.BylineTitle",        "Xmp.photoshop.AuthorsPosition",      &Converter::cnvIptcValue, &Converter::cnvXmpValueToIptc },
Packit 01d647
        { mdIptc, "Iptc.Application2.City",               "Xmp.photoshop.City",                 &Converter::cnvIptcValue, &Converter::cnvXmpValueToIptc },
Packit 01d647
        { mdIptc, "Iptc.Application2.ProvinceState",      "Xmp.photoshop.State",                &Converter::cnvIptcValue, &Converter::cnvXmpValueToIptc },
Packit 01d647
        { mdIptc, "Iptc.Application2.CountryCode",        "Xmp.iptc.CountryCode",               &Converter::cnvIptcValue, &Converter::cnvXmpValueToIptc },
Packit 01d647
        { mdIptc, "Iptc.Application2.CountryName",        "Xmp.photoshop.Country",              &Converter::cnvIptcValue, &Converter::cnvXmpValueToIptc },
Packit 01d647
        { mdIptc, "Iptc.Application2.TransmissionReference", "Xmp.photoshop.TransmissionReference", &Converter::cnvIptcValue, &Converter::cnvXmpValueToIptc },
Packit 01d647
        { mdIptc, "Iptc.Application2.Headline",            "Xmp.photoshop.Headline",            &Converter::cnvIptcValue, &Converter::cnvXmpValueToIptc },
Packit 01d647
        { mdIptc, "Iptc.Application2.Credit",             "Xmp.photoshop.Credit",               &Converter::cnvIptcValue, &Converter::cnvXmpValueToIptc },
Packit 01d647
        { mdIptc, "Iptc.Application2.Source",             "Xmp.photoshop.Source",               &Converter::cnvIptcValue, &Converter::cnvXmpValueToIptc },
Packit 01d647
        { mdIptc, "Iptc.Application2.Copyright",          "Xmp.dc.rights",                      &Converter::cnvIptcValue, &Converter::cnvXmpValueToIptc },
Packit 01d647
        { mdIptc, "Iptc.Application2.Caption",            "Xmp.dc.description",                 &Converter::cnvIptcValue, &Converter::cnvXmpValueToIptc },
Packit 01d647
        { mdIptc, "Iptc.Application2.Writer",             "Xmp.photoshop.CaptionWriter",        &Converter::cnvIptcValue, &Converter::cnvXmpValueToIptc }
Packit 01d647
Packit 01d647
    };
Packit 01d647
Packit 01d647
    Converter::Converter(ExifData& exifData, XmpData& xmpData)
Packit 01d647
        : erase_(false), overwrite_(true), exifData_(&exifData), iptcData_(0), xmpData_(&xmpData), iptcCharset_(0)
Packit 01d647
    {
Packit 01d647
    }
Packit 01d647
Packit 01d647
    Converter::Converter(IptcData& iptcData, XmpData& xmpData, const char *iptcCharset)
Packit 01d647
        : erase_(false), overwrite_(true), exifData_(0), iptcData_(&iptcData), xmpData_(&xmpData), iptcCharset_(iptcCharset)
Packit 01d647
    {
Packit 01d647
    }
Packit 01d647
Packit 01d647
    void Converter::cnvToXmp()
Packit 01d647
    {
Packit 01d647
        for (unsigned int i = 0; i < EXV_COUNTOF(conversion_); ++i) {
Packit 01d647
            const Conversion& c = conversion_[i];
Packit 01d647
            if (   (c.metadataId_ == mdExif && exifData_)
Packit 01d647
                || (c.metadataId_ == mdIptc && iptcData_)) {
Packit 01d647
                EXV_CALL_MEMBER_FN(*this, c.key1ToKey2_)(c.key1_, c.key2_);
Packit 01d647
            }
Packit 01d647
        }
Packit 01d647
    }
Packit 01d647
Packit 01d647
    void Converter::cnvFromXmp()
Packit 01d647
    {
Packit 01d647
        for (unsigned int i = 0; i < EXV_COUNTOF(conversion_); ++i) {
Packit 01d647
            const Conversion& c = conversion_[i];
Packit 01d647
            if (   (c.metadataId_ == mdExif && exifData_)
Packit 01d647
                || (c.metadataId_ == mdIptc && iptcData_)) {
Packit 01d647
                EXV_CALL_MEMBER_FN(*this, c.key2ToKey1_)(c.key2_, c.key1_);
Packit 01d647
            }
Packit 01d647
        }
Packit 01d647
    }
Packit 01d647
Packit 01d647
    void Converter::cnvNone(const char*, const char*)
Packit 01d647
    {
Packit 01d647
        return;
Packit 01d647
    }
Packit 01d647
Packit 01d647
    bool Converter::prepareExifTarget(const char* to, bool force)
Packit 01d647
    {
Packit 01d647
        Exiv2::ExifData::iterator pos = exifData_->findKey(ExifKey(to));
Packit 01d647
        if (pos == exifData_->end()) return true;
Packit 01d647
        if (!overwrite_ && !force) return false;
Packit 01d647
        exifData_->erase(pos);
Packit 01d647
        return true;
Packit 01d647
    }
Packit 01d647
Packit 01d647
    bool Converter::prepareIptcTarget(const char* to, bool force)
Packit 01d647
    {
Packit 01d647
        Exiv2::IptcData::iterator pos = iptcData_->findKey(IptcKey(to));
Packit 01d647
        if (pos == iptcData_->end()) return true;
Packit 01d647
        if (!overwrite_ && !force) return false;
Packit 01d647
        while ((pos = iptcData_->findKey(IptcKey(to))) != iptcData_->end()) {
Packit 01d647
            iptcData_->erase(pos);
Packit 01d647
        }
Packit 01d647
        return true;
Packit 01d647
    }
Packit 01d647
Packit 01d647
    bool Converter::prepareXmpTarget(const char* to, bool force)
Packit 01d647
    {
Packit 01d647
        Exiv2::XmpData::iterator pos = xmpData_->findKey(XmpKey(to));
Packit 01d647
        if (pos == xmpData_->end()) return true;
Packit 01d647
        if (!overwrite_ && !force) return false;
Packit 01d647
        xmpData_->erase(pos);
Packit 01d647
        return true;
Packit 01d647
    }
Packit 01d647
Packit 01d647
    void Converter::cnvExifValue(const char* from, const char* to)
Packit 01d647
    {
Packit 01d647
        Exiv2::ExifData::iterator pos = exifData_->findKey(ExifKey(from));
Packit 01d647
        if (pos == exifData_->end()) return;
Packit 01d647
        std::string value = pos->toString();
Packit 01d647
        if (!pos->value().ok()) {
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
            EXV_WARNING << "Failed to convert " << from << " to " << to << "\n";
Packit 01d647
#endif
Packit 01d647
            return;
Packit 01d647
        }
Packit 01d647
        if (!prepareXmpTarget(to)) return;
Packit 01d647
        (*xmpData_)[to] = value;
Packit 01d647
        if (erase_) exifData_->erase(pos);
Packit 01d647
    }
Packit 01d647
Packit 01d647
    void Converter::cnvExifComment(const char* from, const char* to)
Packit 01d647
    {
Packit 01d647
        Exiv2::ExifData::iterator pos = exifData_->findKey(ExifKey(from));
Packit 01d647
        if (pos == exifData_->end()) return;
Packit 01d647
        if (!prepareXmpTarget(to)) return;
Packit 01d647
        const CommentValue* cv = dynamic_cast<const CommentValue*>(&pos->value());
Packit 01d647
        if (cv == 0) {
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
            EXV_WARNING << "Failed to convert " << from << " to " << to << "\n";
Packit 01d647
#endif
Packit 01d647
            return;
Packit 01d647
        }
Packit 01d647
        // Todo: Convert to UTF-8 if necessary
Packit 01d647
        (*xmpData_)[to] = cv->comment();
Packit 01d647
        if (erase_) exifData_->erase(pos);
Packit 01d647
    }
Packit 01d647
Packit 01d647
    void Converter::cnvExifArray(const char* from, const char* to)
Packit 01d647
    {
Packit 01d647
        Exiv2::ExifData::iterator pos = exifData_->findKey(ExifKey(from));
Packit 01d647
        if (pos == exifData_->end()) return;
Packit 01d647
        if (!prepareXmpTarget(to)) return;
Packit 01d647
        for (int i = 0; i < pos->count(); ++i) {
Packit 01d647
            std::string value = pos->toString(i);
Packit 01d647
            if (!pos->value().ok()) {
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
                EXV_WARNING << "Failed to convert " << from << " to " << to << "\n";
Packit 01d647
#endif
Packit 01d647
                return;
Packit 01d647
            }
Packit 01d647
            (*xmpData_)[to] = value;
Packit 01d647
        }
Packit 01d647
        if (erase_) exifData_->erase(pos);
Packit 01d647
    }
Packit 01d647
Packit 01d647
    void Converter::cnvExifDate(const char* from, const char* to)
Packit 01d647
    {
Packit 01d647
        Exiv2::ExifData::iterator pos = exifData_->findKey(ExifKey(from));
Packit 01d647
        if (pos == exifData_->end()) return;
Packit 01d647
        if (!prepareXmpTarget(to)) return;
Packit 01d647
        int year, month, day, hour, min, sec;
Packit 01d647
        std::string subsec;
Packit 01d647
        char buf[30];
Packit 01d647
Packit 01d647
        if (std::string(from) != "Exif.GPSInfo.GPSTimeStamp") {
Packit 01d647
            std::string value = pos->toString();
Packit 01d647
            if (!pos->value().ok()) {
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
                EXV_WARNING << "Failed to convert " << from << " to " << to << "\n";
Packit 01d647
#endif
Packit 01d647
                return;
Packit 01d647
            }
Packit 01d647
            if (sscanf(value.c_str(), "%d:%d:%d %d:%d:%d", &year, &month, &day, &hour, &min, &sec) != 6) {
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
                EXV_WARNING << "Failed to convert " << from << " to " << to
Packit 01d647
                            << ", unable to parse '" << value << "'\n";
Packit 01d647
#endif
Packit 01d647
                return;
Packit 01d647
            }
Packit 01d647
        }
Packit 01d647
        else { // "Exif.GPSInfo.GPSTimeStamp"
Packit 01d647
Packit 01d647
            bool ok = true;
Packit 01d647
            if (pos->count() != 3) ok = false;
Packit 01d647
            if (ok) {
Packit 01d647
                for (int i = 0; i < 3; ++i) {
Packit 01d647
                    if (pos->toRational(i).second == 0) {
Packit 01d647
                        ok = false;
Packit 01d647
                        break;
Packit 01d647
                    }
Packit 01d647
                }
Packit 01d647
            }
Packit 01d647
            if (!ok) {
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
                EXV_WARNING << "Failed to convert " << from << " to " << to << "\n";
Packit 01d647
#endif
Packit 01d647
                return;
Packit 01d647
            }
Packit 01d647
Packit 01d647
            double dhour = pos->toFloat(0);
Packit 01d647
            double dmin = pos->toFloat(1);
Packit 01d647
            // Hack: Need Value::toDouble
Packit 01d647
            Rational r = pos->toRational(2);
Packit 01d647
            double dsec = static_cast<double>(r.first)/r.second;
Packit 01d647
Packit 01d647
            if (!pos->value().ok()) {
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
                EXV_WARNING << "Failed to convert " << from << " to " << to << "\n";
Packit 01d647
#endif
Packit 01d647
                return;
Packit 01d647
            }
Packit 01d647
Packit 01d647
            dsec = dhour * 3600.0 + dmin * 60.0 + dsec;
Packit 01d647
Packit 01d647
            hour = static_cast<int>(dsec / 3600.0);
Packit 01d647
            dsec -= hour * 3600;
Packit 01d647
            min = static_cast<int>(dsec / 60.0);
Packit 01d647
            dsec -= min * 60;
Packit 01d647
            sec = static_cast<int>(dsec);
Packit 01d647
            dsec -= sec;
Packit 01d647
Packit 01d647
            snprintf(buf, sizeof(buf), "%.9f", dsec);
Packit 01d647
            buf[sizeof(buf) - 1] = 0;
Packit 01d647
            buf[1] = '.'; // some locales use ','
Packit 01d647
            subsec = buf + 1;
Packit 01d647
Packit 01d647
            Exiv2::ExifData::iterator datePos = exifData_->findKey(ExifKey("Exif.GPSInfo.GPSDateStamp"));
Packit 01d647
            if (datePos == exifData_->end()) {
Packit 01d647
                datePos = exifData_->findKey(ExifKey("Exif.Photo.DateTimeOriginal"));
Packit 01d647
            }
Packit 01d647
            if (datePos == exifData_->end()) {
Packit 01d647
                datePos = exifData_->findKey(ExifKey("Exif.Photo.DateTimeDigitized"));
Packit 01d647
            }
Packit 01d647
            if (datePos == exifData_->end()) {
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
                EXV_WARNING << "Failed to convert " << from << " to " << to << "\n";
Packit 01d647
#endif
Packit 01d647
                return;
Packit 01d647
            }
Packit 01d647
            std::string value = datePos->toString();
Packit 01d647
            if (sscanf(value.c_str(), "%d:%d:%d", &year, &month, &day) != 3) {
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
                EXV_WARNING << "Failed to convert " << from << " to " << to
Packit 01d647
                            << ", unable to parse '" << value << "'\n";
Packit 01d647
#endif
Packit 01d647
                return;
Packit 01d647
            }
Packit 01d647
        }
Packit 01d647
Packit 01d647
        const char* subsecTag = 0;
Packit 01d647
        if (std::string(from) == "Exif.Image.DateTime") {
Packit 01d647
            subsecTag = "Exif.Photo.SubSecTime";
Packit 01d647
        }
Packit 01d647
        else if (std::string(from) == "Exif.Photo.DateTimeOriginal") {
Packit 01d647
            subsecTag = "Exif.Photo.SubSecTimeOriginal";
Packit 01d647
        }
Packit 01d647
        else if (std::string(from) == "Exif.Photo.DateTimeDigitized") {
Packit 01d647
            subsecTag = "Exif.Photo.SubSecTimeDigitized";
Packit 01d647
        }
Packit 01d647
Packit 01d647
        if (subsecTag) {
Packit 01d647
            ExifData::iterator subsec_pos = exifData_->findKey(ExifKey(subsecTag));
Packit 01d647
            if (   subsec_pos != exifData_->end()
Packit 01d647
                && subsec_pos->typeId() == asciiString) {
Packit 01d647
                std::string ss = subsec_pos->toString();
Packit 01d647
                if (!ss.empty()) {
Packit 01d647
                    bool ok = false;
Packit 01d647
                    stringTo<long>(ss, ok);
Packit 01d647
                    if (ok) subsec = std::string(".") + ss;
Packit 01d647
                }
Packit 01d647
            }
Packit 01d647
            if (erase_) exifData_->erase(subsec_pos);
Packit 01d647
        }
Packit 01d647
Packit 01d647
        if (subsec.size() > 10) subsec = subsec.substr(0, 10);
Packit 01d647
        snprintf(buf, sizeof(buf), "%4d-%02d-%02dT%02d:%02d:%02d%s",
Packit 01d647
                 year, month, day, hour, min, sec, subsec.c_str());
Packit 01d647
        buf[sizeof(buf) - 1] = 0;
Packit 01d647
Packit 01d647
        (*xmpData_)[to] = buf;
Packit 01d647
        if (erase_) exifData_->erase(pos);
Packit 01d647
    }
Packit 01d647
Packit 01d647
    void Converter::cnvExifVersion(const char* from, const char* to)
Packit 01d647
    {
Packit 01d647
        Exiv2::ExifData::iterator pos = exifData_->findKey(ExifKey(from));
Packit 01d647
        if (pos == exifData_->end()) return;
Packit 01d647
        if (!prepareXmpTarget(to)) return;
Packit 01d647
        std::ostringstream value;
Packit 01d647
        for (int i = 0; i < pos->count(); ++i) {
Packit 01d647
            value << static_cast<char>(pos->toLong(i));
Packit 01d647
        }
Packit 01d647
        (*xmpData_)[to] = value.str();
Packit 01d647
        if (erase_) exifData_->erase(pos);
Packit 01d647
    }
Packit 01d647
Packit 01d647
    void Converter::cnvExifGPSVersion(const char* from, const char* to)
Packit 01d647
    {
Packit 01d647
        Exiv2::ExifData::iterator pos = exifData_->findKey(ExifKey(from));
Packit 01d647
        if (pos == exifData_->end()) return;
Packit 01d647
        if (!prepareXmpTarget(to)) return;
Packit 01d647
        std::ostringstream value;
Packit 01d647
        for (int i = 0; i < pos->count(); ++i) {
Packit 01d647
            if (i > 0) value << '.';
Packit 01d647
            value << pos->toLong(i);
Packit 01d647
        }
Packit 01d647
        (*xmpData_)[to] = value.str();
Packit 01d647
        if (erase_) exifData_->erase(pos);
Packit 01d647
    }
Packit 01d647
Packit 01d647
    void Converter::cnvExifFlash(const char* from, const char* to)
Packit 01d647
    {
Packit 01d647
        Exiv2::ExifData::iterator pos = exifData_->findKey(ExifKey(from));
Packit 01d647
        if (pos == exifData_->end() || pos->count() == 0) return;
Packit 01d647
        if (!prepareXmpTarget(to)) return;
Packit 01d647
        int value = pos->toLong();
Packit 01d647
        if (!pos->value().ok()) {
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
            EXV_WARNING << "Failed to convert " << from << " to " << to << "\n";
Packit 01d647
#endif
Packit 01d647
            return;
Packit 01d647
        }
Packit 01d647
Packit 01d647
        (*xmpData_)["Xmp.exif.Flash/exif:Fired"] = static_cast<bool>(value & 1);
Packit 01d647
        (*xmpData_)["Xmp.exif.Flash/exif:Return"] = (value >> 1) & 3;
Packit 01d647
        (*xmpData_)["Xmp.exif.Flash/exif:Mode"] = (value >> 3) & 3;
Packit 01d647
        (*xmpData_)["Xmp.exif.Flash/exif:Function"] = static_cast<bool>((value >> 5) & 1);
Packit 01d647
        (*xmpData_)["Xmp.exif.Flash/exif:RedEyeMode"] = static_cast<bool>((value >> 6) & 1);
Packit 01d647
Packit 01d647
        if (erase_) exifData_->erase(pos);
Packit 01d647
    }
Packit 01d647
Packit 01d647
    void Converter::cnvExifGPSCoord(const char* from, const char* to)
Packit 01d647
    {
Packit 01d647
        Exiv2::ExifData::iterator pos = exifData_->findKey(ExifKey(from));
Packit 01d647
        if (pos == exifData_->end()) return;
Packit 01d647
        if (!prepareXmpTarget(to)) return;
Packit 01d647
        if (pos->count() != 3) {
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
            EXV_WARNING << "Failed to convert " << from << " to " << to << "\n";
Packit 01d647
#endif
Packit 01d647
            return;
Packit 01d647
        }
Packit 01d647
        Exiv2::ExifData::iterator refPos = exifData_->findKey(ExifKey(std::string(from) + "Ref"));
Packit 01d647
        if (refPos == exifData_->end()) {
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
            EXV_WARNING << "Failed to convert " << from << " to " << to << "\n";
Packit 01d647
#endif
Packit 01d647
            return;
Packit 01d647
        }
Packit 01d647
        double deg[3];
Packit 01d647
        for (int i = 0; i < 3; ++i) {
Packit 01d647
            const int32_t z = pos->toRational(i).first;
Packit 01d647
            const int32_t d = pos->toRational(i).second;
Packit 01d647
            if (d == 0) {
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
                EXV_WARNING << "Failed to convert " << from << " to " << to << "\n";
Packit 01d647
#endif
Packit 01d647
                return;
Packit 01d647
            }
Packit 01d647
            // Hack: Need Value::toDouble
Packit 01d647
            deg[i] = static_cast<double>(z)/d;
Packit 01d647
        }
Packit 01d647
        double min = deg[0] * 60.0 + deg[1] + deg[2] / 60.0;
Packit 01d647
        int ideg = static_cast<int>(min / 60.0);
Packit 01d647
        min -= ideg * 60;
Packit 01d647
        std::ostringstream oss;
Packit 01d647
        oss << ideg << ","
Packit 01d647
            << std::fixed << std::setprecision(7) << min
Packit 01d647
            << refPos->toString().c_str()[0];
Packit 01d647
        (*xmpData_)[to] = oss.str();
Packit 01d647
Packit 01d647
        if (erase_) exifData_->erase(pos);
Packit 01d647
        if (erase_) exifData_->erase(refPos);
Packit 01d647
    }
Packit 01d647
Packit 01d647
    void Converter::cnvXmpValue(const char* from, const char* to)
Packit 01d647
    {
Packit 01d647
        Exiv2::XmpData::iterator pos = xmpData_->findKey(XmpKey(from));
Packit 01d647
        if (pos == xmpData_->end()) return;
Packit 01d647
        if (!prepareExifTarget(to)) return;
Packit 01d647
        std::string value;
Packit 01d647
        if (!getTextValue(value, pos)) {
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
            EXV_WARNING << "Failed to convert " << from << " to " << to << "\n";
Packit 01d647
#endif
Packit 01d647
            return;
Packit 01d647
        }
Packit 01d647
        // Todo: Escape non-ASCII characters in XMP text values
Packit 01d647
        ExifKey key(to);
Packit 01d647
        Exifdatum ed(key);
Packit 01d647
        if (0 == ed.setValue(value)) {
Packit 01d647
            exifData_->add(ed);
Packit 01d647
        }
Packit 01d647
        if (erase_) xmpData_->erase(pos);
Packit 01d647
    }
Packit 01d647
Packit 01d647
    void Converter::cnvXmpComment(const char* from, const char* to)
Packit 01d647
    {
Packit 01d647
        if (!prepareExifTarget(to)) return;
Packit 01d647
        Exiv2::XmpData::iterator pos = xmpData_->findKey(XmpKey(from));
Packit 01d647
        if (pos == xmpData_->end()) return;
Packit 01d647
        std::string value;
Packit 01d647
        if (!getTextValue(value, pos)) {
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
            EXV_WARNING << "Failed to convert " << from << " to " << to << "\n";
Packit 01d647
#endif
Packit 01d647
            return;
Packit 01d647
        }
Packit 01d647
        // Assumes the XMP value is encoded in UTF-8, as it should be
Packit 01d647
        (*exifData_)[to] = "charset=Unicode " + value;
Packit 01d647
        if (erase_) xmpData_->erase(pos);
Packit 01d647
    }
Packit 01d647
Packit 01d647
    void Converter::cnvXmpArray(const char* from, const char* to)
Packit 01d647
    {
Packit 01d647
        if (!prepareExifTarget(to)) return;
Packit 01d647
        Exiv2::XmpData::iterator pos = xmpData_->findKey(XmpKey(from));
Packit 01d647
        if (pos == xmpData_->end()) return;
Packit 01d647
        std::ostringstream array;
Packit 01d647
        for (int i = 0; i < pos->count(); ++i) {
Packit 01d647
            std::string value = pos->toString(i);
Packit 01d647
            if (!pos->value().ok()) {
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
                EXV_WARNING << "Failed to convert " << from << " to " << to << "\n";
Packit 01d647
#endif
Packit 01d647
                return;
Packit 01d647
            }
Packit 01d647
            array << value;
Packit 01d647
            if (i != pos->count() - 1) array << " ";
Packit 01d647
        }
Packit 01d647
        (*exifData_)[to] = array.str();
Packit 01d647
        if (erase_) xmpData_->erase(pos);
Packit 01d647
    }
Packit 01d647
Packit 01d647
    void Converter::cnvXmpDate(const char* from, const char* to)
Packit 01d647
    {
Packit 01d647
        Exiv2::XmpData::iterator pos = xmpData_->findKey(XmpKey(from));
Packit 01d647
        if (pos == xmpData_->end()) return;
Packit 01d647
        if (!prepareExifTarget(to)) return;
Packit 01d647
#ifdef EXV_HAVE_XMP_TOOLKIT
Packit 01d647
        std::string value = pos->toString();
Packit 01d647
        if (!pos->value().ok()) {
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
            EXV_WARNING << "Failed to convert " << from << " to " << to << "\n";
Packit 01d647
#endif
Packit 01d647
            return;
Packit 01d647
        }
Packit 01d647
        XMP_DateTime datetime;
Packit 01d647
        try {
Packit 01d647
            SXMPUtils::ConvertToDate(value, &datetime);
Packit 01d647
        }
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
        catch (const XMP_Error& e) {
Packit 01d647
            EXV_WARNING << "Failed to convert " << from << " to " << to << " (" << e.GetErrMsg() << ")\n";
Packit 01d647
            return;
Packit 01d647
        }
Packit 01d647
#else
Packit 01d647
        catch (const XMP_Error&) {
Packit 01d647
            return;
Packit 01d647
        }
Packit 01d647
#endif // SUPPRESS_WARNINGS
Packit 01d647
        char buf[30];
Packit 01d647
        if (std::string(to) != "Exif.GPSInfo.GPSTimeStamp") {
Packit 01d647
Packit 01d647
            SXMPUtils::ConvertToLocalTime(&datetime);
Packit 01d647
Packit 01d647
            snprintf(buf, sizeof(buf), "%4d:%02d:%02d %02d:%02d:%02d",
Packit 01d647
                     static_cast<int>(datetime.year),
Packit 01d647
                     static_cast<int>(datetime.month),
Packit 01d647
                     static_cast<int>(datetime.day),
Packit 01d647
                     static_cast<int>(datetime.hour),
Packit 01d647
                     static_cast<int>(datetime.minute),
Packit 01d647
                     static_cast<int>(datetime.second));
Packit 01d647
            buf[sizeof(buf) - 1] = 0;
Packit 01d647
            (*exifData_)[to] = buf;
Packit 01d647
Packit 01d647
            if (datetime.nanoSecond) {
Packit 01d647
                const char* subsecTag = 0;
Packit 01d647
                if (std::string(to) == "Exif.Image.DateTime") {
Packit 01d647
                    subsecTag = "Exif.Photo.SubSecTime";
Packit 01d647
                }
Packit 01d647
                else if (std::string(to) == "Exif.Photo.DateTimeOriginal") {
Packit 01d647
                    subsecTag = "Exif.Photo.SubSecTimeOriginal";
Packit 01d647
                }
Packit 01d647
                else if (std::string(to) == "Exif.Photo.DateTimeDigitized") {
Packit 01d647
                    subsecTag = "Exif.Photo.SubSecTimeDigitized";
Packit 01d647
                }
Packit 01d647
                if (subsecTag) {
Packit 01d647
                    prepareExifTarget(subsecTag, true);
Packit 01d647
                    (*exifData_)[subsecTag] = toString(datetime.nanoSecond);
Packit 01d647
                }
Packit 01d647
            }
Packit 01d647
        }
Packit 01d647
        else { // "Exif.GPSInfo.GPSTimeStamp"
Packit 01d647
Packit 01d647
            // Ignore the time zone, assuming the time is in UTC as it should be
Packit 01d647
Packit 01d647
            URational rhour(datetime.hour, 1);
Packit 01d647
            URational rmin(datetime.minute, 1);
Packit 01d647
            URational rsec(datetime.second, 1);
Packit 01d647
            if (datetime.nanoSecond != 0) {
Packit 01d647
                if (datetime.second != 0) {
Packit 01d647
                    // Add the seconds to rmin so that the ns fit into rsec
Packit 01d647
                    rmin.second = 60;
Packit 01d647
                    rmin.first *= 60;
Packit 01d647
                    rmin.first += datetime.second;
Packit 01d647
                }
Packit 01d647
                rsec.second = 1000000000;
Packit 01d647
                rsec.first = datetime.nanoSecond;
Packit 01d647
            }
Packit 01d647
Packit 01d647
            std::ostringstream array;
Packit 01d647
            array << rhour << " " << rmin << " " << rsec;
Packit 01d647
            (*exifData_)[to] = array.str();
Packit 01d647
Packit 01d647
            prepareExifTarget("Exif.GPSInfo.GPSDateStamp", true);
Packit 01d647
            snprintf(buf, sizeof(buf), "%4d:%02d:%02d",
Packit 01d647
                    static_cast<int>(datetime.year),
Packit 01d647
                    static_cast<int>(datetime.month),
Packit 01d647
                    static_cast<int>(datetime.day));
Packit 01d647
            buf[sizeof(buf) - 1] = 0;
Packit 01d647
            (*exifData_)["Exif.GPSInfo.GPSDateStamp"] = buf;
Packit 01d647
        }
Packit 01d647
Packit 01d647
        if (erase_) xmpData_->erase(pos);
Packit 01d647
#else
Packit 01d647
# ifndef SUPPRESS_WARNINGS
Packit 01d647
        EXV_WARNING << "Failed to convert " << from << " to " << to << "\n";
Packit 01d647
# endif
Packit 01d647
#endif // !EXV_HAVE_XMP_TOOLKIT
Packit 01d647
    }
Packit 01d647
Packit 01d647
    void Converter::cnvXmpVersion(const char* from, const char* to)
Packit 01d647
    {
Packit 01d647
        Exiv2::XmpData::iterator pos = xmpData_->findKey(XmpKey(from));
Packit 01d647
        if (pos == xmpData_->end()) return;
Packit 01d647
        if (!prepareExifTarget(to)) return;
Packit 01d647
        std::string value = pos->toString();
Packit 01d647
        if (!pos->value().ok() || value.length() < 4) {
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
            EXV_WARNING << "Failed to convert " << from << " to " << to << "\n";
Packit 01d647
#endif
Packit 01d647
            return;
Packit 01d647
        }
Packit 01d647
        std::ostringstream array;
Packit 01d647
Packit 01d647
        array << static_cast<int>(value[0]) << " "
Packit 01d647
              << static_cast<int>(value[1]) << " "
Packit 01d647
              << static_cast<int>(value[2]) << " "
Packit 01d647
              << static_cast<int>(value[3]);
Packit 01d647
Packit 01d647
        (*exifData_)[to] = array.str();
Packit 01d647
        if (erase_) xmpData_->erase(pos);
Packit 01d647
    }
Packit 01d647
Packit 01d647
    void Converter::cnvXmpGPSVersion(const char* from, const char* to)
Packit 01d647
    {
Packit 01d647
        Exiv2::XmpData::iterator pos = xmpData_->findKey(XmpKey(from));
Packit 01d647
        if (pos == xmpData_->end()) return;
Packit 01d647
        if (!prepareExifTarget(to)) return;
Packit 01d647
        std::string value = pos->toString();
Packit 01d647
        if (!pos->value().ok()) {
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
            EXV_WARNING << "Failed to convert " << from << " to " << to << "\n";
Packit 01d647
#endif
Packit 01d647
            return;
Packit 01d647
        }
Packit 01d647
Packit 01d647
        for (unsigned i = 0; i < value.length(); ++i) {
Packit 01d647
            if (value[i] == '.') value[i] = ' ';
Packit 01d647
        }
Packit 01d647
        (*exifData_)[to] = value;
Packit 01d647
        if (erase_) xmpData_->erase(pos);
Packit 01d647
Packit 01d647
    }
Packit 01d647
Packit 01d647
    void Converter::cnvXmpFlash(const char* from, const char* to)
Packit 01d647
    {
Packit 01d647
        Exiv2::XmpData::iterator pos = xmpData_->findKey(XmpKey(std::string(from) + "/exif:Fired"));
Packit 01d647
        if (pos == xmpData_->end()) return;
Packit 01d647
        if (!prepareExifTarget(to)) return;
Packit 01d647
        unsigned short value = 0;
Packit 01d647
Packit 01d647
        if (pos != xmpData_->end() && pos->count() > 0) {
Packit 01d647
            int fired = pos->toLong();
Packit 01d647
            if (pos->value().ok())
Packit 01d647
                value |= fired & 1;
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
            else
Packit 01d647
                EXV_WARNING << "Failed to convert " << std::string(from) + "/exif:Fired" << " to " << to << "\n";
Packit 01d647
#endif
Packit 01d647
        }
Packit 01d647
        pos = xmpData_->findKey(XmpKey(std::string(from) + "/exif:Return"));
Packit 01d647
        if (pos != xmpData_->end() && pos->count() > 0) {
Packit 01d647
            int ret = pos->toLong();
Packit 01d647
            if (pos->value().ok())
Packit 01d647
                value |= (ret & 3) << 1;
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
            else
Packit 01d647
                EXV_WARNING << "Failed to convert " << std::string(from) + "/exif:Return" << " to " << to << "\n";
Packit 01d647
#endif
Packit 01d647
        }
Packit 01d647
        pos = xmpData_->findKey(XmpKey(std::string(from) + "/exif:Mode"));
Packit 01d647
        if (pos != xmpData_->end() && pos->count() > 0) {
Packit 01d647
            int mode = pos->toLong();
Packit 01d647
            if (pos->value().ok())
Packit 01d647
                value |= (mode & 3) << 3;
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
            else
Packit 01d647
                EXV_WARNING << "Failed to convert " << std::string(from) + "/exif:Mode" << " to " << to << "\n";
Packit 01d647
#endif
Packit 01d647
        }
Packit 01d647
        pos = xmpData_->findKey(XmpKey(std::string(from) + "/exif:Function"));
Packit 01d647
        if (pos != xmpData_->end() && pos->count() > 0) {
Packit 01d647
            int function = pos->toLong();
Packit 01d647
            if (pos->value().ok())
Packit 01d647
                value |= (function & 1) << 5;
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
            else
Packit 01d647
                EXV_WARNING << "Failed to convert " << std::string(from) + "/exif:Function" << " to " << to << "\n";
Packit 01d647
#endif
Packit 01d647
        }
Packit 01d647
        pos = xmpData_->findKey(XmpKey(std::string(from) + "/exif:RedEyeMode"));
Packit 01d647
        if (pos != xmpData_->end() && pos->count() > 0) {
Packit 01d647
            int red = pos->toLong();
Packit 01d647
            if (pos->value().ok())
Packit 01d647
                value |= (red & 1) << 6;
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
            else
Packit 01d647
                EXV_WARNING << "Failed to convert " << std::string(from) + "/exif:RedEyeMode" << " to " << to << "\n";
Packit 01d647
#endif
Packit 01d647
        }
Packit 01d647
Packit 01d647
        (*exifData_)[to] = value;
Packit 01d647
        if (erase_) xmpData_->erase(pos);
Packit 01d647
    }
Packit 01d647
Packit 01d647
    void Converter::cnvXmpGPSCoord(const char* from, const char* to)
Packit 01d647
    {
Packit 01d647
        Exiv2::XmpData::iterator pos = xmpData_->findKey(XmpKey(from));
Packit 01d647
        if (pos == xmpData_->end()) return;
Packit 01d647
        if (!prepareExifTarget(to)) return;
Packit 01d647
        std::string value = pos->toString();
Packit 01d647
        if (!pos->value().ok()) {
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
            EXV_WARNING << "Failed to convert " << from << " to " << to << "\n";
Packit 01d647
#endif
Packit 01d647
            return;
Packit 01d647
        }
Packit 01d647
        if (value.empty()) {
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
            EXV_WARNING << from << " is empty\n";
Packit 01d647
#endif
Packit 01d647
            return;
Packit 01d647
        }
Packit 01d647
Packit 01d647
        double deg = 0.0;
Packit 01d647
        double min = 0.0;
Packit 01d647
        double sec = 0.0;
Packit 01d647
        char ref  = value[value.length() - 1];
Packit 01d647
        char sep1 = '\0';
Packit 01d647
        char sep2 = '\0';
Packit 01d647
Packit 01d647
        value.erase(value.length() - 1);
Packit 01d647
Packit 01d647
        std::istringstream in(value);
Packit 01d647
Packit 01d647
        in >> deg >> sep1 >> min >> sep2;
Packit 01d647
Packit 01d647
        if (sep2 == ',') {
Packit 01d647
            in >> sec;
Packit 01d647
        }
Packit 01d647
        else {
Packit 01d647
            sec = (min - static_cast<int>(min)) * 60.0;
Packit 01d647
            min = static_cast<double>(static_cast<int>(min));
Packit 01d647
            sep2 = ',';
Packit 01d647
        }
Packit 01d647
Packit 01d647
        if (   in.bad() || !(ref == 'N' || ref == 'S' || ref == 'E' || ref == 'W')
Packit 01d647
            || sep1 != ',' || sep2 != ',' || !in.eof()) {
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
            EXV_WARNING << "Failed to convert " << from << " to " << to << "\n";
Packit 01d647
#endif
Packit 01d647
            return;
Packit 01d647
        }
Packit 01d647
Packit 01d647
        Rational rdeg = floatToRationalCast(static_cast<float>(deg));
Packit 01d647
        Rational rmin = floatToRationalCast(static_cast<float>(min));
Packit 01d647
        Rational rsec = floatToRationalCast(static_cast<float>(sec));
Packit 01d647
Packit 01d647
        std::ostringstream array;
Packit 01d647
        array << rdeg << " " << rmin << " " << rsec;
Packit 01d647
        (*exifData_)[to] = array.str();
Packit 01d647
Packit 01d647
        prepareExifTarget((std::string(to) + "Ref").c_str(), true);
Packit 01d647
        char ref_str[2] = {ref, 0};
Packit 01d647
        (*exifData_)[std::string(to) + "Ref"] = ref_str;
Packit 01d647
Packit 01d647
        if (erase_) xmpData_->erase(pos);
Packit 01d647
    }
Packit 01d647
Packit 01d647
    void Converter::cnvIptcValue(const char* from, const char* to)
Packit 01d647
    {
Packit 01d647
        Exiv2::IptcData::iterator pos = iptcData_->findKey(IptcKey(from));
Packit 01d647
        if (pos == iptcData_->end()) return;
Packit 01d647
        if (!prepareXmpTarget(to)) return;
Packit 01d647
        while (pos != iptcData_->end()) {
Packit 01d647
            if (pos->key() == from) {
Packit 01d647
                std::string value = pos->toString();
Packit 01d647
                if (!pos->value().ok()) {
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
                    EXV_WARNING << "Failed to convert " << from << " to " << to << "\n";
Packit 01d647
#endif
Packit 01d647
                    ++pos;
Packit 01d647
                    continue;
Packit 01d647
                }
Packit 01d647
                if (iptcCharset_) convertStringCharset(value, iptcCharset_, "UTF-8");
Packit 01d647
                (*xmpData_)[to] = value;
Packit 01d647
                if (erase_) {
Packit 01d647
                    pos = iptcData_->erase(pos);
Packit 01d647
                    continue;
Packit 01d647
                }
Packit 01d647
            }
Packit 01d647
            ++pos;
Packit 01d647
        }
Packit 01d647
    }
Packit 01d647
Packit 01d647
    void Converter::cnvXmpValueToIptc(const char* from, const char* to)
Packit 01d647
    {
Packit 01d647
        XmpData::iterator pos = xmpData_->findKey(XmpKey(from));
Packit 01d647
        if (pos == xmpData_->end()) return;
Packit 01d647
        if (!prepareIptcTarget(to)) return;
Packit 01d647
Packit 01d647
        if (pos->typeId() == langAlt || pos->typeId() == xmpText) {
Packit 01d647
            std::string value;
Packit 01d647
            if (!getTextValue(value, pos)) {
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
                EXV_WARNING << "Failed to convert " << from << " to " << to << "\n";
Packit 01d647
#endif
Packit 01d647
                return;
Packit 01d647
            }
Packit 01d647
            (*iptcData_)[to] = value;
Packit 01d647
            (*iptcData_)["Iptc.Envelope.CharacterSet"] = "\033%G"; // indicate UTF-8 encoding
Packit 01d647
            if (erase_) xmpData_->erase(pos);
Packit 01d647
            return;
Packit 01d647
        }
Packit 01d647
Packit 01d647
        int count = pos->count();
Packit 01d647
        bool added = false;
Packit 01d647
        for (int i = 0; i < count; ++i) {
Packit 01d647
            std::string value = pos->toString(i);
Packit 01d647
            if (!pos->value().ok()) {
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
                EXV_WARNING << "Failed to convert " << from << " to " << to << "\n";
Packit 01d647
#endif
Packit 01d647
                continue;
Packit 01d647
            }
Packit 01d647
            IptcKey key(to);
Packit 01d647
            Iptcdatum id(key);
Packit 01d647
            id.setValue(value);
Packit 01d647
            iptcData_->add(id);
Packit 01d647
            added = true;
Packit 01d647
        }
Packit 01d647
        if (added) (*iptcData_)["Iptc.Envelope.CharacterSet"] = "\033%G"; // indicate UTF-8 encoding
Packit 01d647
        if (erase_) xmpData_->erase(pos);
Packit 01d647
    }
Packit 01d647
Packit 01d647
#ifdef EXV_HAVE_XMP_TOOLKIT
Packit 01d647
    std::string Converter::computeExifDigest(bool tiff)
Packit 01d647
    {
Packit 01d647
        std::ostringstream res;
Packit 01d647
        MD5_CTX    context;
Packit 01d647
        unsigned char digest[16];
Packit 01d647
Packit 01d647
        MD5Init ( &context );
Packit 01d647
        for (unsigned int i = 0; i < EXV_COUNTOF(conversion_); ++i) {
Packit 01d647
            const Conversion& c = conversion_[i];
Packit 01d647
            if (c.metadataId_ == mdExif) {
Packit 01d647
                Exiv2::ExifKey key(c.key1_);
Packit 01d647
                if (tiff && key.groupName() != "Image") continue;
Packit 01d647
                if (!tiff && key.groupName() == "Image") continue;
Packit 01d647
Packit 01d647
                if (!res.str().empty()) res << ',';
Packit 01d647
                res << key.tag();
Packit 01d647
                Exiv2::ExifData::iterator pos = exifData_->findKey(key);
Packit 01d647
                if (pos == exifData_->end()) continue;
Packit 01d647
                DataBuf data(pos->size());
Packit 01d647
                pos->copy(data.pData_, littleEndian /* FIXME ? */);
Packit 01d647
                MD5Update ( &context, data.pData_, data.size_);
Packit 01d647
            }
Packit 01d647
        }
Packit 01d647
        MD5Final(digest, &context);
Packit 01d647
        res << ';';
Packit 01d647
        res << std::setw(2) << std::setfill('0') << std::hex << std::uppercase;
Packit 01d647
        for (int i = 0; i < 16; ++i) {
Packit 01d647
            res << static_cast<int>(digest[i]);
Packit 01d647
        }
Packit 01d647
        return res.str();
Packit 01d647
    }
Packit 01d647
#else
Packit 01d647
    std::string Converter::computeExifDigest(bool)
Packit 01d647
    {
Packit 01d647
        return std::string("");
Packit 01d647
    }
Packit 01d647
#endif
Packit 01d647
Packit 01d647
Packit 01d647
    void Converter::writeExifDigest()
Packit 01d647
    {
Packit 01d647
#ifdef EXV_HAVE_XMP_TOOLKIT
Packit 01d647
        (*xmpData_)["Xmp.tiff.NativeDigest"] = computeExifDigest(true);
Packit 01d647
        (*xmpData_)["Xmp.exif.NativeDigest"] = computeExifDigest(false);
Packit 01d647
#endif
Packit 01d647
    }
Packit 01d647
Packit 01d647
    void Converter::syncExifWithXmp()
Packit 01d647
    {
Packit 01d647
        Exiv2::XmpData::iterator td = xmpData_->findKey(XmpKey("Xmp.tiff.NativeDigest"));
Packit 01d647
        Exiv2::XmpData::iterator ed = xmpData_->findKey(XmpKey("Xmp.exif.NativeDigest"));
Packit 01d647
        if (td != xmpData_->end() && ed != xmpData_->end()) {
Packit 01d647
            if (td->value().toString() == computeExifDigest(true) &&
Packit 01d647
                ed->value().toString() == computeExifDigest(false)) {
Packit 01d647
                // We have both digests and the values match
Packit 01d647
                // XMP is up-to-date, we should update Exif
Packit 01d647
                setOverwrite(true);
Packit 01d647
                setErase(false);
Packit 01d647
Packit 01d647
                cnvFromXmp();
Packit 01d647
                writeExifDigest();
Packit 01d647
                return;
Packit 01d647
            }
Packit 01d647
            else {
Packit 01d647
                // We have both digests and the values do not match
Packit 01d647
                // Exif was modified after XMP, we should update XMP
Packit 01d647
                setOverwrite(true);
Packit 01d647
                setErase(false);
Packit 01d647
Packit 01d647
                cnvToXmp();
Packit 01d647
                writeExifDigest();
Packit 01d647
                return;
Packit 01d647
            }
Packit 01d647
        }
Packit 01d647
        else {
Packit 01d647
            // We don't have both digests, it is probably the first conversion to XMP
Packit 01d647
            setOverwrite(false); // to be safe
Packit 01d647
            setErase(false);
Packit 01d647
Packit 01d647
            cnvToXmp();
Packit 01d647
            writeExifDigest();
Packit 01d647
            return;
Packit 01d647
        }
Packit 01d647
    }
Packit 01d647
Packit 01d647
    std::string Converter::computeIptcDigest()
Packit 01d647
    {
Packit 01d647
#ifdef EXV_HAVE_XMP_TOOLKIT
Packit 01d647
        std::ostringstream res;
Packit 01d647
        MD5_CTX context;
Packit 01d647
        unsigned char digest[16];
Packit 01d647
Packit 01d647
        MD5Init(&context);
Packit 01d647
Packit 01d647
        DataBuf data = IptcParser::encode(*iptcData_);
Packit 01d647
        MD5Update(&context, data.pData_, data.size_);
Packit 01d647
        MD5Final(digest, &context);
Packit 01d647
        res << std::setw(2) << std::setfill('0') << std::hex << std::uppercase;
Packit 01d647
        for (int i = 0; i < 16; ++i) {
Packit 01d647
            res << static_cast<int>(digest[i]);
Packit 01d647
        }
Packit 01d647
        return res.str();
Packit 01d647
#else
Packit 01d647
        return std::string("");
Packit 01d647
#endif
Packit 01d647
    }
Packit 01d647
Packit 01d647
Packit 01d647
    // *************************************************************************
Packit 01d647
    // free functions
Packit 01d647
    void copyExifToXmp(const ExifData& exifData, XmpData& xmpData)
Packit 01d647
    {
Packit 01d647
        Converter converter(const_cast<ExifData&>(exifData), xmpData);
Packit 01d647
        converter.cnvToXmp();
Packit 01d647
    }
Packit 01d647
Packit 01d647
    void moveExifToXmp(ExifData& exifData, XmpData& xmpData)
Packit 01d647
    {
Packit 01d647
        Converter converter(exifData, xmpData);
Packit 01d647
        converter.setErase();
Packit 01d647
        converter.cnvToXmp();
Packit 01d647
    }
Packit 01d647
Packit 01d647
    void copyXmpToExif(const XmpData& xmpData, ExifData& exifData)
Packit 01d647
    {
Packit 01d647
        Converter converter(exifData, const_cast<XmpData&>(xmpData));
Packit 01d647
        converter.cnvFromXmp();
Packit 01d647
    }
Packit 01d647
Packit 01d647
    void moveXmpToExif(XmpData& xmpData, ExifData& exifData)
Packit 01d647
    {
Packit 01d647
        Converter converter(exifData, xmpData);
Packit 01d647
        converter.setErase();
Packit 01d647
        converter.cnvFromXmp();
Packit 01d647
    }
Packit 01d647
Packit 01d647
    void syncExifWithXmp(ExifData& exifData, XmpData& xmpData)
Packit 01d647
    {
Packit 01d647
        Converter converter(exifData, xmpData);
Packit 01d647
        converter.syncExifWithXmp();
Packit 01d647
    }
Packit 01d647
Packit 01d647
    void copyIptcToXmp(const IptcData& iptcData, XmpData& xmpData, const char *iptcCharset)
Packit 01d647
    {
Packit 01d647
        if (!iptcCharset) iptcCharset = iptcData.detectCharset();
Packit 01d647
        if (!iptcCharset) iptcCharset = "ISO-8859-1";
Packit 01d647
Packit 01d647
        Converter converter(const_cast<IptcData&>(iptcData), xmpData, iptcCharset);
Packit 01d647
        converter.cnvToXmp();
Packit 01d647
    }
Packit 01d647
Packit 01d647
    void moveIptcToXmp(IptcData& iptcData, XmpData& xmpData, const char *iptcCharset)
Packit 01d647
    {
Packit 01d647
        if (!iptcCharset) iptcCharset = iptcData.detectCharset();
Packit 01d647
        if (!iptcCharset) iptcCharset = "ISO-8859-1";
Packit 01d647
        Converter converter(iptcData, xmpData, iptcCharset);
Packit 01d647
        converter.setErase();
Packit 01d647
        converter.cnvToXmp();
Packit 01d647
    }
Packit 01d647
Packit 01d647
    void copyXmpToIptc(const XmpData& xmpData, IptcData& iptcData)
Packit 01d647
    {
Packit 01d647
        Converter converter(iptcData, const_cast<XmpData&>(xmpData));
Packit 01d647
        converter.cnvFromXmp();
Packit 01d647
    }
Packit 01d647
Packit 01d647
    void moveXmpToIptc(XmpData& xmpData, IptcData& iptcData)
Packit 01d647
    {
Packit 01d647
        Converter converter(iptcData, xmpData);
Packit 01d647
        converter.setErase();
Packit 01d647
        converter.cnvFromXmp();
Packit 01d647
    }
Packit 01d647
Packit 01d647
    bool convertStringCharset(std::string &str, const char* from, const char* to)
Packit 01d647
    {
Packit 01d647
        if (0 == strcmp(from, to)) return true; // nothing to do
Packit 01d647
        bool ret = false;
Packit 01d647
#if defined EXV_HAVE_ICONV
Packit 01d647
        ret = convertStringCharsetIconv(str, from, to);
Packit 01d647
#elif defined WIN32 && !defined __CYGWIN__
Packit 01d647
        ret = convertStringCharsetWindows(str, from, to);
Packit 01d647
#else
Packit 01d647
# ifndef SUPPRESS_WARNINGS
Packit 01d647
        EXV_WARNING << "Charset conversion required but no character mapping functionality available.\n";
Packit 01d647
# endif
Packit 01d647
        UNUSED(str);
Packit 01d647
#endif
Packit 01d647
        return ret;
Packit 01d647
    }
Packit 01d647
}                                       // namespace Exiv2
Packit 01d647
Packit 01d647
// *****************************************************************************
Packit 01d647
// local definitions
Packit 01d647
namespace {
Packit 01d647
Packit 01d647
    using namespace Exiv2;
Packit 01d647
Packit 01d647
#if defined WIN32 && !defined __CYGWIN__
Packit 01d647
    bool swapBytes(std::string& str)
Packit 01d647
    {
Packit 01d647
        // Naive byte-swapping, I'm sure this can be done more efficiently
Packit 01d647
        if (str.size() & 1) {
Packit 01d647
#ifdef EXIV2_DEBUG_MESSAGES
Packit 01d647
            EXV_DEBUG << "swapBytes: Size " << str.size() << " of input string is not even.\n";
Packit 01d647
#endif
Packit 01d647
            return false;
Packit 01d647
        }
Packit 01d647
        for (unsigned int i = 0; i < str.size() / 2; ++i) {
Packit 01d647
            char t = str[2 * i];
Packit 01d647
            str[2 * i] = str[2 * i + 1];
Packit 01d647
            str[2 * i + 1] = t;
Packit 01d647
        }
Packit 01d647
        return true;
Packit 01d647
    }
Packit 01d647
Packit 01d647
    bool mb2wc(UINT cp, std::string& str)
Packit 01d647
    {
Packit 01d647
        if (str.empty()) return true;
Packit 01d647
        int len = MultiByteToWideChar(cp, 0, str.c_str(), (int)str.size(), 0, 0);
Packit 01d647
        if (len == 0) {
Packit 01d647
#ifdef EXIV2_DEBUG_MESSAGES
Packit 01d647
            EXV_DEBUG << "mb2wc: Failed to determine required size of output buffer.\n";
Packit 01d647
#endif
Packit 01d647
            return false;
Packit 01d647
        }
Packit 01d647
        std::vector<std::string::value_type> out;
Packit 01d647
        out.resize(len * 2);
Packit 01d647
        int ret = MultiByteToWideChar(cp, 0, str.c_str(), (int)str.size(), (LPWSTR)&out[0], len * 2);
Packit 01d647
        if (ret == 0) {
Packit 01d647
#ifdef EXIV2_DEBUG_MESSAGES
Packit 01d647
            EXV_DEBUG << "mb2wc: Failed to convert the input string to a wide character string.\n";
Packit 01d647
#endif
Packit 01d647
            return false;
Packit 01d647
        }
Packit 01d647
        str.assign(out.begin(), out.end());
Packit 01d647
        return true;
Packit 01d647
    }
Packit 01d647
Packit 01d647
    bool wc2mb(UINT cp, std::string& str)
Packit 01d647
    {
Packit 01d647
        if (str.empty()) return true;
Packit 01d647
        if (str.size() & 1) {
Packit 01d647
#ifdef EXIV2_DEBUG_MESSAGES
Packit 01d647
            EXV_DEBUG << "wc2mb: Size " << str.size() << " of input string is not even.\n";
Packit 01d647
#endif
Packit 01d647
            return false;
Packit 01d647
        }
Packit 01d647
        int len = WideCharToMultiByte(cp, 0, (LPCWSTR)str.data(), (int)str.size() / 2, 0, 0, 0, 0);
Packit 01d647
        if (len == 0) {
Packit 01d647
#ifdef EXIV2_DEBUG_MESSAGES
Packit 01d647
            EXV_DEBUG << "wc2mb: Failed to determine required size of output buffer.\n";
Packit 01d647
#endif
Packit 01d647
            return false;
Packit 01d647
        }
Packit 01d647
        std::vector<std::string::value_type> out;
Packit 01d647
        out.resize(len);
Packit 01d647
        int ret = WideCharToMultiByte(cp, 0, (LPCWSTR)str.data(), (int)str.size() / 2, (LPSTR)&out[0], len, 0, 0);
Packit 01d647
        if (ret == 0) {
Packit 01d647
#ifdef EXIV2_DEBUG_MESSAGES
Packit 01d647
            EXV_DEBUG << "wc2mb: Failed to convert the input string to a multi byte string.\n";
Packit 01d647
#endif
Packit 01d647
            return false;
Packit 01d647
        }
Packit 01d647
        str.assign(out.begin(), out.end());
Packit 01d647
        return true;
Packit 01d647
    }
Packit 01d647
Packit 01d647
    bool utf8ToUcs2be(std::string& str)
Packit 01d647
    {
Packit 01d647
        bool ret = mb2wc(CP_UTF8, str);
Packit 01d647
        if (ret) ret = swapBytes(str);
Packit 01d647
        return ret;
Packit 01d647
    }
Packit 01d647
Packit 01d647
    bool utf8ToUcs2le(std::string& str)
Packit 01d647
    {
Packit 01d647
        return mb2wc(CP_UTF8, str);
Packit 01d647
    }
Packit 01d647
Packit 01d647
    bool ucs2beToUtf8(std::string& str)
Packit 01d647
    {
Packit 01d647
        bool ret = swapBytes(str);
Packit 01d647
        if (ret) ret = wc2mb(CP_UTF8, str);
Packit 01d647
        return ret;
Packit 01d647
    }
Packit 01d647
Packit 01d647
    bool ucs2beToUcs2le(std::string& str)
Packit 01d647
    {
Packit 01d647
        return swapBytes(str);
Packit 01d647
    }
Packit 01d647
Packit 01d647
    bool ucs2leToUtf8(std::string& str)
Packit 01d647
    {
Packit 01d647
        return wc2mb(CP_UTF8, str);
Packit 01d647
    }
Packit 01d647
Packit 01d647
    bool ucs2leToUcs2be(std::string& str)
Packit 01d647
    {
Packit 01d647
        return swapBytes(str);
Packit 01d647
    }
Packit 01d647
Packit 01d647
    bool iso88591ToUtf8(std::string& str)
Packit 01d647
    {
Packit 01d647
        bool ret = mb2wc(28591, str);
Packit 01d647
        if (ret) ret = wc2mb(CP_UTF8, str);
Packit 01d647
        return ret;
Packit 01d647
    }
Packit 01d647
Packit 01d647
    bool asciiToUtf8(std::string& /*str*/)
Packit 01d647
    {
Packit 01d647
        // nothing to do
Packit 01d647
        return true;
Packit 01d647
    }
Packit 01d647
Packit 01d647
    typedef bool (*ConvFct)(std::string& str);
Packit 01d647
Packit 01d647
    struct ConvFctList {
Packit 01d647
        bool operator==(const std::pair<const char*, const char*> &fromTo) const
Packit 01d647
            { return 0 == strcmp(from_, fromTo.first) && 0 == strcmp(to_, fromTo.second); }
Packit 01d647
        const char* from_;
Packit 01d647
        const char* to_;
Packit 01d647
        ConvFct convFct_;
Packit 01d647
    };
Packit 01d647
Packit 01d647
    const ConvFctList convFctList[] = {
Packit 01d647
        { "UTF-8",      "UCS-2BE", utf8ToUcs2be   },
Packit 01d647
        { "UTF-8",      "UCS-2LE", utf8ToUcs2le   },
Packit 01d647
        { "UCS-2BE",    "UTF-8",   ucs2beToUtf8   },
Packit 01d647
        { "UCS-2BE",    "UCS-2LE", ucs2beToUcs2le },
Packit 01d647
        { "UCS-2LE",    "UTF-8",   ucs2leToUtf8   },
Packit 01d647
        { "UCS-2LE",    "UCS-2BE", ucs2leToUcs2be },
Packit 01d647
        { "ISO-8859-1", "UTF-8",   iso88591ToUtf8 },
Packit 01d647
        { "ASCII",      "UTF-8",   asciiToUtf8    }
Packit 01d647
        // Update the convertStringCharset() documentation if you add more here!
Packit 01d647
    };
Packit 01d647
Packit 01d647
    bool convertStringCharsetWindows(std::string& str, const char* from, const char* to)
Packit 01d647
    {
Packit 01d647
        bool ret = false;
Packit 01d647
        const ConvFctList* p = find(convFctList, std::make_pair(from, to));
Packit 01d647
        std::string tmpstr = str;
Packit 01d647
        if (p) ret = p->convFct_(tmpstr);
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
        else {
Packit 01d647
            EXV_WARNING << "No Windows function to map character string from " << from << " to " << to << " available.\n";
Packit 01d647
        }
Packit 01d647
#endif
Packit 01d647
        if (ret) str = tmpstr;
Packit 01d647
        return ret;
Packit 01d647
    }
Packit 01d647
Packit 01d647
#endif // defined WIN32 && !defined __CYGWIN__
Packit 01d647
#if defined EXV_HAVE_ICONV
Packit 01d647
    bool convertStringCharsetIconv(std::string& str, const char* from, const char* to)
Packit 01d647
    {
Packit 01d647
        if (0 == strcmp(from, to)) return true; // nothing to do
Packit 01d647
Packit 01d647
        bool ret = true;
Packit 01d647
        iconv_t cd;
Packit 01d647
        cd = iconv_open(to, from);
Packit 01d647
        if (cd == (iconv_t)(-1)) {
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
            EXV_WARNING << "iconv_open: " << strError() << "\n";
Packit 01d647
#endif
Packit 01d647
            return false;
Packit 01d647
        }
Packit 01d647
        std::string outstr;
Packit 01d647
        EXV_ICONV_CONST char* inptr = const_cast<char*>(str.c_str());
Packit 01d647
        size_t inbytesleft = str.length();
Packit 01d647
        while (inbytesleft) {
Packit 01d647
            char outbuf[256];
Packit 01d647
            char* outptr = outbuf;
Packit 01d647
            size_t outbytesleft = sizeof(outbuf);
Packit 01d647
            size_t rc = iconv(cd,
Packit 01d647
                              &inptr,
Packit 01d647
                              &inbytesleft,
Packit 01d647
                              &outptr,
Packit 01d647
                              &outbytesleft);
Packit 01d647
            const size_t outbytesProduced = sizeof(outbuf) - outbytesleft;
Packit 01d647
            if (rc == size_t(-1) && errno != E2BIG) {
Packit 01d647
#ifndef SUPPRESS_WARNINGS
Packit 01d647
                EXV_WARNING << "iconv: " << strError()
Packit 01d647
                            << " inbytesleft = " << inbytesleft << "\n";
Packit 01d647
#endif
Packit 01d647
                ret = false;
Packit 01d647
                break;
Packit 01d647
            }
Packit 01d647
            outstr.append(std::string(outbuf, outbytesProduced));
Packit 01d647
        }
Packit 01d647
        if (cd != (iconv_t)(-1)) {
Packit 01d647
            iconv_close(cd);
Packit 01d647
        }
Packit 01d647
Packit 01d647
        if (ret) str = outstr;
Packit 01d647
        return ret;
Packit 01d647
    }
Packit 01d647
Packit 01d647
#endif // EXV_HAVE_ICONV
Packit 01d647
    bool getTextValue(std::string& value, const XmpData::iterator& pos)
Packit 01d647
    {
Packit 01d647
        if (pos->typeId() == langAlt) {
Packit 01d647
            // get the default language entry without x-default qualifier
Packit 01d647
            value = pos->toString(0);
Packit 01d647
            if (!pos->value().ok() && pos->count() == 1) {
Packit 01d647
                // If there is no default but exactly one entry, take that
Packit 01d647
                // without the qualifier
Packit 01d647
                value = pos->toString();
Packit 01d647
                if (   pos->value().ok()
Packit 01d647
                    && value.length() > 5 && value.substr(0, 5) == "lang=") {
Packit 01d647
                    const std::string::size_type first_space_pos = value.find_first_of(' ');
Packit 01d647
                    if (first_space_pos != std::string::npos) {
Packit 01d647
                        value = value.substr(first_space_pos + 1);
Packit 01d647
                    }
Packit 01d647
                    else {
Packit 01d647
                        value.clear();
Packit 01d647
                    }
Packit 01d647
                }
Packit 01d647
            }
Packit 01d647
        }
Packit 01d647
        else {
Packit 01d647
            value = pos->toString();
Packit 01d647
        }
Packit 01d647
        return pos->value().ok();
Packit 01d647
    }
Packit 01d647
Packit 01d647
}