LeoHsiao1 / pyexiv2

Read and write image metadata, including EXIF, IPTC, XMP, ICC Profile.
GNU General Public License v3.0
196 stars 39 forks source link

Open image with german umlauts #131

Closed RalfPeter closed 5 months ago

RalfPeter commented 5 months ago

Hi Leo, I just converted my (GoPro) project from piexif to your pyexiv2. It was easy and most without to much work. Ty for your work. Now i tried to open a file with name: "C:/photos/motorrad/münchen/20120701_101216.jpg'. The init of Image is encoding the filename with utf-8 and filename will not be found. If i encode myself then error is that bytes dont have a method encoding. I guess you had same problems with your mandarin filenames. Do you have a tip, how i can solve the problem? Why do you encode filename? All filenames in python (>2.9?) are already unicode. Ty for an answer

LeoHsiao1 commented 5 months ago

Hi! This is because Windows systems usually use the user's localized language rather than utf-8 encoding. If you open a file with python's open() function, it automatically guesses the encoding format of the filename. However, the underlying execution of pyexiv2 is C++ code, so it requires the user to actively specify the encoding format of the filename. See: https://github.com/LeoHsiao1/pyexiv2/blob/master/docs/Tutorial.md#class-image

LeoHsiao1 commented 5 months ago

Another option, if you don't want to specify the encoding format of each image file, you can open the image file with python's open() function and then parse the image with pyexiv2.ImageData. See: https://github.com/LeoHsiao1/pyexiv2/blob/master/docs/Tutorial.md#class-imagedata

RalfPeter commented 5 months ago

Thank for your advice. I will post my favorite solution as soon as i found.

RalfPeter commented 5 months ago

I made the following to solve my problem: i wrapped the the pyexiv2 functions into a class EExiv2, with init and a method save(), see code below:

class EExiv2: 
    def __init__(self, file: Union[str, Path], verbose=False):

        self.file = Path(file).resolve()
        if not self.file.is_file():
            raise FileNotFoundError

        self.verbose = verbose

        # initialize image
        try:
            with open(self.file, 'rb') as f:
                self.image = ImageData(f.read())
                if self.image:
                    self.data_exif = self.read_exif()
                    self.data_iptc = self.read_iptc()
                    self.data_xmp = self.image.read_xmp()
                    self.comment = self.image.read_comment()
                    self.data_icc = self.image.read_icc()
                    self.data_thumbnail = self.image.read_thumbnail()
        except TypeError as e:
            if self.verbose:
                print('EXIF_init', f"Error: {type(e).__name__} - {e.args}")
            raise TypeError

    def save(self, close=True):
        if self.image:
            self.image.modify_exif(self.data_exif)
            self.image.modify_iptc(self.data_iptc)
            self.image.modify_xmp(self.data_xmp)
            if len(self.data_icc) > 0:
                self.image.modify_icc(self.data_icc)
            if len(self.comment) > 0:
                self.image.modify_comment(self.comment)
            if len(self.data_thumbnail) > 0:
                self.image.modify_thumbnail(self.data_thumbnail)

        try:
            # Get the bytes data of the image and save it to the file
            data = self.image.get_bytes()
            with open(self.file, 'rb+') as f:
                # Empty the original file
                f.seek(0)
                f.truncate()
                f.write(data)
        except FileNotFoundError:
            print(f"File not found: {self.file}")

        if close:
            self.image.close()

Thank you for your advice.