Skip to content

Commit

Permalink
Transcode Windows paths to current code page
Browse files Browse the repository at this point in the history
This is an alternative approach to using the std::wstring versions of
exiv2 methods that are not available in v0.28.x (see
Exiv2/exiv2#2637). It should also allow non
ascii paths to be used when libexiv2 has been compiled without wstring
methods enabled.

The proper approach is to set the Windows code page to utf-8, but this
probably won't work on old Windows, and it's not clear how to do it for
a Python application. Instead we transcode paths from utf-8 to the
current code page, which could fail.
  • Loading branch information
jim-easterbrook committed Dec 21, 2023
1 parent c1d4aa6 commit 1d499ed
Show file tree
Hide file tree
Showing 11 changed files with 244 additions and 10 deletions.
7 changes: 7 additions & 0 deletions src/interface/basicio.i
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
%include "shared/enum.i"
%include "shared/keep_reference.i"
%include "shared/unique_ptr.i"
%include "shared/windows_path.i"

%include "std_string.i"

Expand Down Expand Up @@ -65,6 +66,12 @@ UNIQUE_PTR(Exiv2::BasicIo);
%noexception Exiv2::RemoteIo::tell;
%noexception Exiv2::MemIo::write;

// Convert path encoding on Windows
WINDOWS_PATH(const std::wstring& wpath)
WINDOWS_PATH(const std::wstring& wOrgPathpath)
WINDOWS_PATH(const std::wstring& wOrgPath)
WINDOWS_PATH(const std::wstring& wurl)

// BasicIo return values keep a reference to the Image they refer to
KEEP_REFERENCE(Exiv2::BasicIo&)

Expand Down
9 changes: 4 additions & 5 deletions src/interface/exif.i
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,10 @@
%include "shared/containers.i"
%include "shared/data_iterator.i"
%include "shared/keep_reference.i"
%include "shared/windows_path.i"

%include "stdint.i"
%include "std_string.i"
#ifndef SWIGIMPORTED
#ifdef EXV_UNICODE_PATH
%include "std_wstring.i"
#endif
#endif

%import "metadatum.i"
%import "tags.i"
Expand All @@ -53,6 +49,9 @@ DATA_ITERATOR_CLASSES(

DATA_CONTAINER(Exiv2::ExifData, Exiv2::Exifdatum, Exiv2::ExifKey)

// Convert path encoding on Windows
WINDOWS_PATH(const std::wstring& wpath)

// Ignore const overloads of some methods
%ignore Exiv2::ExifData::operator[];
%ignore Exiv2::ExifData::begin() const;
Expand Down
9 changes: 4 additions & 5 deletions src/interface/image.i
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,9 @@
%include "shared/buffers.i"
%include "shared/enum.i"
%include "shared/keep_reference.i"
%include "shared/windows_path.i"

%include "std_string.i"
#ifndef SWIGIMPORTED
#ifdef EXV_UNICODE_PATH
%include "std_wstring.i"
#endif
#endif

%import "basicio.i";
%import "exif.i";
Expand All @@ -57,6 +53,9 @@ INPUT_BUFFER_RO_EX(const Exiv2::byte* data, size_t size)
}
%}

// Convert path encoding on Windows
WINDOWS_PATH(const std::string& path)

// Simplify handling of default parameters
%typemap(default) bool useCurl {$1 = true;}
%ignore Exiv2::ImageFactory::createIo(std::string const &);
Expand Down
4 changes: 4 additions & 0 deletions src/interface/preview.i
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
%include "shared/preamble.i"
%include "shared/buffers.i"
%include "shared/keep_reference.i"
%include "shared/windows_path.i"

%include "std_string.i"
%include "std_vector.i"
Expand All @@ -41,6 +42,9 @@
%noexception Exiv2::PreviewImage::wextension;
%noexception Exiv2::PreviewImage::width;

// Convert path encoding on Windows
WINDOWS_PATH(const std::wstring& wpath)

// Convert getPreviewProperties result to a Python tuple
%template() std::vector<Exiv2::PreviewProperties>;

Expand Down
54 changes: 54 additions & 0 deletions src/interface/shared/windows_path.i
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// python-exiv2 - Python interface to libexiv2
// http://github.com/jim-easterbrook/python-exiv2
// Copyright (C) 2023 Jim Easterbrook [email protected]
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.


// If exiv2's wstring methods are available then use them!
#ifdef EXV_UNICODE_PATH
%include "std_wstring.i"
#endif

// Function to convert utf-8 string to current code page
%fragment("transcode_path", "header") {
static void transcode_path(std::string *path) {
%#ifdef _WIN32
UINT acp = GetACP();
if (acp == CP_UTF8)
return;
// Convert utf-8 path to active code page, via widechar version
int wide_len = MultiByteToWideChar(CP_UTF8, 0, &(*path)[0], -1, NULL, 0);
std::wstring wide_str;
wide_str.resize(wide_len);
if (MultiByteToWideChar(CP_UTF8, 0, &(*path)[0], -1,
&wide_str[0], (int)wide_str.size()) >= 0) {
int new_len = WideCharToMultiByte(acp, 0, &wide_str[0], -1,
NULL, 0, NULL, NULL);
path->resize(new_len);
WideCharToMultiByte(acp, 0, &wide_str[0], -1,
&(*path)[0], (int)path->size(), NULL, NULL);
}
%#endif
};
}

// Macro to convert Windows paths from utf-8 to current code page
%define WINDOWS_PATH(signature)
#ifndef EXV_UNICODE_PATH
%typemap(check, fragment="transcode_path") signature {
transcode_path($1);
}
#endif
%enddef // WINDOWS_PATH
33 changes: 33 additions & 0 deletions src/swig-0_27_0/image_wrap.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -4455,6 +4455,27 @@ SWIG_AsVal_unsigned_SS_short (PyObject * obj, unsigned short *val)
return res;
}


static void transcode_path(std::string *path) {
#ifdef _WIN32
UINT acp = GetACP();
if (acp == CP_UTF8)
return;
// Convert utf-8 path to active code page, via widechar version
int wide_len = MultiByteToWideChar(CP_UTF8, 0, &(*path)[0], -1, NULL, 0);
std::wstring wide_str;
wide_str.resize(wide_len);
if (MultiByteToWideChar(CP_UTF8, 0, &(*path)[0], -1,
&wide_str[0], (int)wide_str.size()) >= 0) {
int new_len = WideCharToMultiByte(acp, 0, &wide_str[0], -1,
NULL, 0, NULL, NULL);
path->resize(new_len);
WideCharToMultiByte(acp, 0, &wide_str[0], -1,
&(*path)[0], (int)path->size(), NULL, NULL);
}
#endif
};

#ifdef __cplusplus
extern "C" {
#endif
Expand Down Expand Up @@ -5966,6 +5987,9 @@ SWIGINTERN PyObject *_wrap_ImageFactory_createIo(PyObject *self, PyObject *args)
}
arg2 = static_cast< bool >(val2);
}
{
transcode_path(arg1);
}
{
try {
result = Exiv2::ImageFactory::createIo((std::string const &)*arg1,arg2);
Expand Down Expand Up @@ -6021,6 +6045,9 @@ SWIGINTERN PyObject *_wrap_ImageFactory_open__SWIG_0(PyObject *self, Py_ssize_t
}
arg2 = static_cast< bool >(val2);
}
{
transcode_path(arg1);
}
{
try {
{
Expand Down Expand Up @@ -6171,6 +6198,9 @@ SWIGINTERN PyObject *_wrap_ImageFactory_create__SWIG_0(PyObject *self, Py_ssize_
}
arg2 = ptr;
}
{
transcode_path(arg2);
}
{
try {
{
Expand Down Expand Up @@ -6285,6 +6315,9 @@ SWIGINTERN PyObject *_wrap_ImageFactory_getType__SWIG_0(PyObject *self, Py_ssize
}
arg1 = ptr;
}
{
transcode_path(arg1);
}
{
try {
result = (int)Exiv2::ImageFactory::getType((std::string const &)*arg1);
Expand Down
24 changes: 24 additions & 0 deletions src/swig-0_27_0/preview_wrap.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -5434,6 +5434,27 @@ SWIG_AsPtr_std_string (PyObject * obj, std::string **val)
}


static void transcode_path(std::string *path) {
#ifdef _WIN32
UINT acp = GetACP();
if (acp == CP_UTF8)
return;
// Convert utf-8 path to active code page, via widechar version
int wide_len = MultiByteToWideChar(CP_UTF8, 0, &(*path)[0], -1, NULL, 0);
std::wstring wide_str;
wide_str.resize(wide_len);
if (MultiByteToWideChar(CP_UTF8, 0, &(*path)[0], -1,
&wide_str[0], (int)wide_str.size()) >= 0) {
int new_len = WideCharToMultiByte(acp, 0, &wide_str[0], -1,
NULL, 0, NULL, NULL);
path->resize(new_len);
WideCharToMultiByte(acp, 0, &wide_str[0], -1,
&(*path)[0], (int)path->size(), NULL, NULL);
}
#endif
};


#define SWIG_From_long PyInt_FromLong

SWIGINTERN size_t Exiv2_PreviewImage___len__(Exiv2::PreviewImage *self){
Expand Down Expand Up @@ -5986,6 +6007,9 @@ SWIGINTERN PyObject *_wrap_PreviewImage_writeFile(PyObject *self, PyObject *args
}
arg2 = ptr;
}
{
transcode_path(arg2);
}
{
try {
result = (long)((Exiv2::PreviewImage const *)arg1)->writeFile((std::string const &)*arg2);
Expand Down
33 changes: 33 additions & 0 deletions src/swig-0_27_4/image_wrap.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -4455,6 +4455,27 @@ SWIG_AsVal_unsigned_SS_short (PyObject * obj, unsigned short *val)
return res;
}


static void transcode_path(std::string *path) {
#ifdef _WIN32
UINT acp = GetACP();
if (acp == CP_UTF8)
return;
// Convert utf-8 path to active code page, via widechar version
int wide_len = MultiByteToWideChar(CP_UTF8, 0, &(*path)[0], -1, NULL, 0);
std::wstring wide_str;
wide_str.resize(wide_len);
if (MultiByteToWideChar(CP_UTF8, 0, &(*path)[0], -1,
&wide_str[0], (int)wide_str.size()) >= 0) {
int new_len = WideCharToMultiByte(acp, 0, &wide_str[0], -1,
NULL, 0, NULL, NULL);
path->resize(new_len);
WideCharToMultiByte(acp, 0, &wide_str[0], -1,
&(*path)[0], (int)path->size(), NULL, NULL);
}
#endif
};

#ifdef __cplusplus
extern "C" {
#endif
Expand Down Expand Up @@ -5966,6 +5987,9 @@ SWIGINTERN PyObject *_wrap_ImageFactory_createIo(PyObject *self, PyObject *args)
}
arg2 = static_cast< bool >(val2);
}
{
transcode_path(arg1);
}
{
try {
result = Exiv2::ImageFactory::createIo((std::string const &)*arg1,arg2);
Expand Down Expand Up @@ -6021,6 +6045,9 @@ SWIGINTERN PyObject *_wrap_ImageFactory_open__SWIG_0(PyObject *self, Py_ssize_t
}
arg2 = static_cast< bool >(val2);
}
{
transcode_path(arg1);
}
{
try {
{
Expand Down Expand Up @@ -6171,6 +6198,9 @@ SWIGINTERN PyObject *_wrap_ImageFactory_create__SWIG_0(PyObject *self, Py_ssize_
}
arg2 = ptr;
}
{
transcode_path(arg2);
}
{
try {
{
Expand Down Expand Up @@ -6285,6 +6315,9 @@ SWIGINTERN PyObject *_wrap_ImageFactory_getType__SWIG_0(PyObject *self, Py_ssize
}
arg1 = ptr;
}
{
transcode_path(arg1);
}
{
try {
result = (int)Exiv2::ImageFactory::getType((std::string const &)*arg1);
Expand Down
24 changes: 24 additions & 0 deletions src/swig-0_27_4/preview_wrap.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -5434,6 +5434,27 @@ SWIG_AsPtr_std_string (PyObject * obj, std::string **val)
}


static void transcode_path(std::string *path) {
#ifdef _WIN32
UINT acp = GetACP();
if (acp == CP_UTF8)
return;
// Convert utf-8 path to active code page, via widechar version
int wide_len = MultiByteToWideChar(CP_UTF8, 0, &(*path)[0], -1, NULL, 0);
std::wstring wide_str;
wide_str.resize(wide_len);
if (MultiByteToWideChar(CP_UTF8, 0, &(*path)[0], -1,
&wide_str[0], (int)wide_str.size()) >= 0) {
int new_len = WideCharToMultiByte(acp, 0, &wide_str[0], -1,
NULL, 0, NULL, NULL);
path->resize(new_len);
WideCharToMultiByte(acp, 0, &wide_str[0], -1,
&(*path)[0], (int)path->size(), NULL, NULL);
}
#endif
};


#define SWIG_From_long PyInt_FromLong

SWIGINTERN size_t Exiv2_PreviewImage___len__(Exiv2::PreviewImage *self){
Expand Down Expand Up @@ -5986,6 +6007,9 @@ SWIGINTERN PyObject *_wrap_PreviewImage_writeFile(PyObject *self, PyObject *args
}
arg2 = ptr;
}
{
transcode_path(arg2);
}
{
try {
result = (long)((Exiv2::PreviewImage const *)arg1)->writeFile((std::string const &)*arg2);
Expand Down
Loading

0 comments on commit 1d499ed

Please sign in to comment.