[Git][debian-gis-team/glymur][master] 4 commits: New upstream version 0.9.9

Antonio Valentino (@antonio.valentino) gitlab at salsa.debian.org
Fri Mar 25 07:34:44 GMT 2022



Antonio Valentino pushed to branch master at Debian GIS Project / glymur


Commits:
c6542c1c by Antonio Valentino at 2022-03-25T06:50:31+00:00
New upstream version 0.9.9
- - - - -
15b4a87b by Antonio Valentino at 2022-03-25T06:50:39+00:00
Update upstream source from tag 'upstream/0.9.9'

Update to upstream version '0.9.9'
with Debian dir a76aad7b63d86172ca5f30da42d56f59b04c0e8e
- - - - -
8eb73935 by Antonio Valentino at 2022-03-25T06:51:30+00:00
New upstream release

- - - - -
bb905696 by Antonio Valentino at 2022-03-25T06:53:39+00:00
Set distribution to unstable

- - - - -


7 changed files:

- CHANGES.txt
- debian/changelog
- docs/source/conf.py
- docs/source/whatsnew/0.9.rst
- glymur/tiff.py
- glymur/version.py
- setup.cfg


Changes:

=====================================
CHANGES.txt
=====================================
@@ -1,3 +1,6 @@
+March 23, 2022 - v0.9.9
+    Fix bug retrieving some TIFF tags on big endian systems
+
 March 5, 2022 - v0.9.8
     Fix install requirements
 


=====================================
debian/changelog
=====================================
@@ -1,3 +1,9 @@
+glymur (0.9.9-1) unstable; urgency=medium
+
+  * New upstream release.
+
+ -- Antonio Valentino <antonio.valentino at tiscali.it>  Fri, 25 Mar 2022 06:53:21 +0000
+
 glymur (0.9.8-1) unstable; urgency=medium
 
   * New upstream release.


=====================================
docs/source/conf.py
=====================================
@@ -78,7 +78,7 @@ copyright = '2013-2022, John Evans'
 # The short X.Y version.
 version = '0.9'
 # The full version, including alpha/beta/rc tags.
-release = '0.9.8'
+release = '0.9.9'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.


=====================================
docs/source/whatsnew/0.9.rst
=====================================
@@ -2,6 +2,12 @@
 Changes in glymur 0.9
 #####################
 
+****************
+Changes in 0.9.9
+****************
+
+    Fix bug retrieving some TIFF tags on big endian systems.
+
 ****************
 Changes in 0.9.8
 ****************


=====================================
glymur/tiff.py
=====================================
@@ -71,6 +71,9 @@ class Tiff2Jp2k(object):
 
         self.kwargs = kwargs
 
+        self.setup_logging(verbosity)
+
+    def setup_logging(self, verbosity):
         self.logger = logging.getLogger('tiff2jp2')
         self.logger.setLevel(verbosity)
         ch = logging.StreamHandler()
@@ -86,6 +89,7 @@ class Tiff2Jp2k(object):
 
     def run(self):
 
+        self.get_main_ifd()
         self.copy_image()
 
         if self.create_uuid:
@@ -99,10 +103,12 @@ class Tiff2Jp2k(object):
         # create a bytesio object for the IFD
         b = io.BytesIO()
 
-        with open(self.tiff_filename, 'rb') as tfp:
+        # write this 32-bit header into the UUID, no matter if we had bigtiff
+        # or regular tiff or big endian
+        data = struct.pack('<BBHI', 73, 73, 42, 8)
+        b.write(data)
 
-            endian = self._process_header(b, tfp)
-            self._process_tags(b, tfp, endian)
+        self._process_tags(b)
 
         if self.found_geotiff_tags:
             # geotiff UUID
@@ -121,22 +127,113 @@ class Tiff2Jp2k(object):
         with open(self.jp2_filename, mode='ab') as f:
             uuid_box.write(f)
 
-    def _process_tags(self, b, tfp, endian):
+    def get_main_ifd(self):
+        """
+        Read all the tags in the main IFD.  We do it this way because of the
+        difficulty in using TIFFGetFieldDefaulted when the datatype of a tag
+        can differ.
+        """
+
+        with open(self.tiff_filename, 'rb') as tfp:
+
+            self.get_endianness(tfp)
+
+            self.found_geotiff_tags = False
+
+            tag_length = 20 if self.version == _BIGTIFF else 12
+
+            # how many tags?
+            if self.version == _BIGTIFF:
+                buffer = tfp.read(8)
+                num_tags, = struct.unpack(self.endian + 'Q', buffer)
+            else:
+                buffer = tfp.read(2)
+                num_tags, = struct.unpack(self.endian + 'H', buffer)
+
+            # Ok, so now we have the IFD main body, but following that we have
+            # the tag payloads that cannot fit into 4 bytes.
+
+            # the IFD main body in the TIFF.  As it might be big endian, we
+            # cannot just process it as one big chunk.
+            buffer = tfp.read(num_tags * tag_length)
+
+            if self.version == _BIGTIFF:
+                tag_format_str = self.endian + 'HHQQ'
+                tag_payload_offset = 12
+                max_tag_payload_length = 8
+            else:
+                tag_format_str = self.endian + 'HHII'
+                tag_payload_offset = 8
+                max_tag_payload_length = 4
+
+            self.tags = {}
+
+            for idx in range(num_tags):
+
+                self.logger.debug(f'tag #: {idx}')
 
-        self.found_geotiff_tags = False
+                tag_data = buffer[idx * tag_length:(idx + 1) * tag_length]
 
-        tag_length = 20 if self.version == _BIGTIFF else 12
+                tag, dtype, nvalues, offset = struct.unpack(tag_format_str, tag_data)  # noqa : E501
+
+                if tag == 34735:
+                    self.found_geotiff_tags = True
+
+                payload_length = tag_dtype[dtype]['nbytes'] * nvalues
+
+                if payload_length > max_tag_payload_length:
+                    # the payload does not fit into the tag entry, so use the
+                    # offset to seek to that position
+                    current_position = tfp.tell()
+                    tfp.seek(offset)
+                    payload_buffer = tfp.read(payload_length)
+                    tfp.seek(current_position)
+
+                    # read the payload from the TIFF
+                    payload_format = tag_dtype[dtype]['format'] * nvalues
+                    payload = struct.unpack(
+                        self.endian + payload_format, payload_buffer
+                    )
+
+                else:
+                    # the payload DOES fit into the TIFF tag entry
+                    payload_buffer = tag_data[tag_payload_offset:]
+
+                    # read ALL of the payload buffer
+                    payload_format = (
+                        tag_dtype[dtype]['format']
+                        * int(max_tag_payload_length / tag_dtype[dtype]['nbytes'])  # noqa : E501
+                    )
+
+                    payload = struct.unpack(
+                        self.endian + payload_format, payload_buffer
+                    )
+
+                    # Extract the actual payload.  Two things going
+                    # on here.  First of all, not all of the items may
+                    # be used.  For example, if the payload length is
+                    # 4 bytes but the format string was HHH, the that
+                    # last 16 bit value is not wanted, so we should
+                    # discard it.  Second thing is that the signed and
+                    # unsigned rational datatypes effectively have twice
+                    # the number of values so we need to account for that.
+                    if dtype in [5, 10]:
+                        payload = payload[:2 * nvalues]
+                    else:
+                        payload = payload[:nvalues]
+
+                self.tags[tag] = {
+                    'dtype': dtype,
+                    'nvalues': nvalues,
+                    'payload': payload
+                }
+
+    def _process_tags(self, b):
 
         # keep this for writing to the UUID, which will always be 32-bit
         little_tiff_tag_length = 12
 
-        # how many tags?
-        if self.version == _BIGTIFF:
-            buffer = tfp.read(8)
-            num_tags, = struct.unpack(endian + 'Q', buffer)
-        else:
-            buffer = tfp.read(2)
-            num_tags, = struct.unpack(endian + 'H', buffer)
+        num_tags = len(self.tags)
 
         write_buffer = struct.pack('<H', num_tags)
         b.write(write_buffer)
@@ -146,48 +243,30 @@ class Tiff2Jp2k(object):
 
         # the IFD main body in the TIFF.  As it might be big endian, we cannot
         # just process it as one big chunk.
-        buffer = tfp.read(num_tags * tag_length)
-
-        start_of_tags_position = b.tell()
-        after_ifd_position = start_of_tags_position + len(buffer)
 
-        if self.version == _BIGTIFF:
-            tag_format_str = endian + 'HHQQ'
-            tag_payload_offset = 12
-            max_tag_payload_length = 8
-        else:
-            tag_format_str = endian + 'HHII'
-            tag_payload_offset = 8
-            max_tag_payload_length = 4
+        tag_start_loc = b.tell()
+        after_ifd_position = tag_start_loc + num_tags * little_tiff_tag_length
 
-        for idx in range(num_tags):
+        # We write a little-TIFF IFD
+        max_tag_payload_length = 4
 
-            self.logger.debug(f'tag #: {idx}')
+        for idx, tag in enumerate(self.tags):
 
-            b.seek(start_of_tags_position + idx * little_tiff_tag_length)
+            self.logger.debug(f'tag #: {tag}')
 
-            tag_data = buffer[idx * tag_length:(idx + 1) * tag_length]
+            b.seek(tag_start_loc + idx * little_tiff_tag_length)
 
-            tag, dtype, nvalues, offset = struct.unpack(tag_format_str, tag_data)  # noqa : E501
-
-            if tag == 34735:
-                self.found_geotiff_tags = True
+            dtype = self.tags[tag]['dtype']
+            nvalues = self.tags[tag]['nvalues']
+            payload = self.tags[tag]['payload']
 
             payload_length = tag_dtype[dtype]['nbytes'] * nvalues
 
             if payload_length > max_tag_payload_length:
-                # the payload does not fit into the tag entry, so use the
-                # offset to seek to that position
-                current_position = tfp.tell()
-                tfp.seek(offset)
-                payload_buffer = tfp.read(payload_length)
-                tfp.seek(current_position)
+                # the payload does not fit into the tag entry
 
                 # read the payload from the TIFF
                 payload_format = tag_dtype[dtype]['format'] * nvalues
-                payload = struct.unpack(
-                    endian + payload_format, payload_buffer
-                )
 
                 # write the tag entry to the UUID
                 new_offset = after_ifd_position
@@ -211,7 +290,6 @@ class Tiff2Jp2k(object):
 
             else:
                 # the payload DOES fit into the TIFF tag entry
-                payload_buffer = tag_data[tag_payload_offset:]
 
                 # read ALL of the payload buffer
                 payload_format = (
@@ -219,22 +297,6 @@ class Tiff2Jp2k(object):
                     * int(max_tag_payload_length / tag_dtype[dtype]['nbytes'])
                 )
 
-                payload = struct.unpack(
-                    endian + payload_format, payload_buffer
-                )
-
-                # Extract the actual payload.  Two things going on here.  First
-                # of all, not all of the items may be used.  For example, if
-                # the payload length is 4 bytes but the format string was HHH,
-                # the that last 16 bit value is not wanted, so we should
-                # discard it.  Second thing is that the signed and unsigned
-                # rational datatypes effectively have twice the number of
-                # values so we need to account for that.
-                if dtype in [5, 10]:
-                    payload = payload[:2 * nvalues]
-                else:
-                    payload = payload[:nvalues]
-
                 # Does it fit into the UUID tag entry (4 bytes)?
                 if payload_length <= 4:
 
@@ -274,6 +336,41 @@ class Tiff2Jp2k(object):
                     after_ifd_position = b.tell()
                     b.seek(cpos)
 
+    def get_endianness(self, tfp):
+        """
+        Set the endian-ness of the TIFF
+        """
+
+        buffer = tfp.read(4)
+        data = struct.unpack('BB', buffer[:2])
+
+        # big endian or little endian?
+        if data[0] == 73 and data[1] == 73:
+            # little endian
+            self.endian = '<'
+        elif data[0] == 77 and data[1] == 77:
+            # big endian
+            self.endian = '>'
+        else:
+            msg = (
+                f"The byte order indication in the TIFF header "
+                f"({data}) is invalid.  It should be either "
+                f"{bytes([73, 73])} or {bytes([77, 77])}."
+            )
+            raise RuntimeError(msg)
+
+        # version number and offset to the first IFD
+        version, = struct.unpack(self.endian + 'H', buffer[2:4])
+        self.version = _TIFF if version == 42 else _BIGTIFF
+
+        if self.version == _BIGTIFF:
+            buffer = tfp.read(12)
+            _, _, offset = struct.unpack(self.endian + 'HHQ', buffer)
+        else:
+            buffer = tfp.read(4)
+            offset, = struct.unpack(self.endian + 'I', buffer)
+        tfp.seek(offset)
+
     def _process_header(self, b, tfp):
 
         buffer = tfp.read(4)
@@ -313,6 +410,27 @@ class Tiff2Jp2k(object):
 
         return endian
 
+    def get_tag_value(self, tagnum):
+        """
+        Return the value associated with the tag.  Some tags are not actually
+        written into the IFD, but are instead "defaulted".
+
+        Returns
+        -------
+        tag value
+        """
+
+        if tagnum not in self.tags and tagnum == 284:
+            # PlanarConfig is not always written into the IFD, defaults to 1
+            return 1
+
+        if tagnum not in self.tags and tagnum == 339:
+            # SampleFormat is not always written into the IFD, defaults to 1
+            return 1
+
+        # The tag value is always stored as a tuple with at least one member.
+        return self.tags[tagnum]['payload'][0]
+
     def copy_image(self):
         """
         Transfer the image data from the TIFF to the JPEG 2000 file.  If the
@@ -324,13 +442,13 @@ class Tiff2Jp2k(object):
         else:
             isTiled = False
 
-        photo = libtiff.getFieldDefaulted(self.tiff_fp, 'Photometric')
-        imagewidth = libtiff.getFieldDefaulted(self.tiff_fp, 'ImageWidth')
-        imageheight = libtiff.getFieldDefaulted(self.tiff_fp, 'ImageLength')
-        spp = libtiff.getFieldDefaulted(self.tiff_fp, 'SamplesPerPixel')
-        sf = libtiff.getFieldDefaulted(self.tiff_fp, 'SampleFormat')
-        bps = libtiff.getFieldDefaulted(self.tiff_fp, 'BitsPerSample')
-        planar = libtiff.getFieldDefaulted(self.tiff_fp, 'PlanarConfig')
+        photo = self.get_tag_value(262)
+        imagewidth = self.get_tag_value(256)
+        imageheight = self.get_tag_value(257)
+        spp = self.get_tag_value(277)
+        sf = self.get_tag_value(339)
+        bps = self.get_tag_value(258)
+        planar = self.get_tag_value(284)
 
         if sf not in [libtiff.SampleFormat.UINT, libtiff.SampleFormat.VOID]:
             sampleformat_str = self.tagvalue2str(libtiff.SampleFormat, sf)
@@ -364,11 +482,11 @@ class Tiff2Jp2k(object):
             raise RuntimeError(msg)
 
         if libtiff.isTiled(self.tiff_fp):
-            tw = libtiff.getFieldDefaulted(self.tiff_fp, 'TileWidth')
-            th = libtiff.getFieldDefaulted(self.tiff_fp, 'TileLength')
+            tw = self.get_tag_value(322)
+            th = self.get_tag_value(323)
         else:
             tw = imagewidth
-            rps = libtiff.getFieldDefaulted(self.tiff_fp, 'RowsPerStrip')
+            rps = self.get_tag_value(278)
             num_strips = libtiff.numberOfStrips(self.tiff_fp)
 
         if self.tilesize is not None:
@@ -428,7 +546,9 @@ class Tiff2Jp2k(object):
                 )
                 warnings.warn(msg)
 
-            image = libtiff.readRGBAImageOriented(self.tiff_fp)
+            image = libtiff.readRGBAImageOriented(
+                self.tiff_fp, imagewidth, imageheight
+            )
 
             if spp < 4:
                 image = image[:, :, :3]


=====================================
glymur/version.py
=====================================
@@ -21,7 +21,7 @@ from .lib import tiff
 
 # Do not change the format of this next line!  Doing so risks breaking
 # setup.py
-version = "0.9.8"
+version = "0.9.9"
 
 version_tuple = parse(version).release
 


=====================================
setup.cfg
=====================================
@@ -1,6 +1,6 @@
 [metadata]
 name = Glymur
-version = 0.9.8
+version = 0.9.9
 author = 'John Evans'
 author_email = "John Evans" <john.g.evans.ne at gmail.com>
 license = 'MIT'



View it on GitLab: https://salsa.debian.org/debian-gis-team/glymur/-/compare/abe9fdb4f42c52ae1151f298399f3ce1415751af...bb905696b372c527049d438b0c40b14bee28320c

-- 
View it on GitLab: https://salsa.debian.org/debian-gis-team/glymur/-/compare/abe9fdb4f42c52ae1151f298399f3ce1415751af...bb905696b372c527049d438b0c40b14bee28320c
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-grass-devel/attachments/20220325/0cd20072/attachment-0001.htm>


More information about the Pkg-grass-devel mailing list