docs: def read_uic_tag(fh, tagid, planecount, offset):
      """Read a single UIC tag value from file and return tag name and value.
  
      UIC1Tags use an offset.
  
      """
      def read_int(count=1):
          value = struct.unpack('<%iI' % count, fh.read(4*count))
          return value[0] if count == 1 else value
  
      try:
          name, dtype = TIFF.UIC_TAGS[tagid]
      except IndexError:
          # unknown tag
          return '_TagId%i' % tagid, read_int()
  
      Fraction = TIFF.UIC_TAGS[4][1]
  
      if offset:
          pos = fh.tell()
          if dtype not in (int, None):
              off = read_int()
              if off < 8:
                  if dtype is str:
                      return name, ''
                  log.warning("read_uic_tag: invalid offset for tag '%s' (%i)",
                              name, off)
                  return name, off
              fh.seek(off)
  
      if dtype is None:
          # skip
          name = '_' + name
          value = read_int()
      elif dtype is int:
          # int
          value = read_int()
      elif dtype is Fraction:
          # fraction
          value = read_int(2)
          value = value[0] / value[1]
      elif dtype is julian_datetime:
          # datetime
          value = julian_datetime(*read_int(2))
      elif dtype is read_uic_image_property:
          # ImagePropertyEx
          value = read_uic_image_property(fh)
      elif dtype is str:
          # pascal string
          size = read_int()
          if 0 <= size < 2**10:
              value = struct.unpack('%is' % size, fh.read(size))[0][:-1]
              value = bytes2str(stripnull(value))
          elif offset:
              value = ''
              log.warning("read_uic_tag: corrupt string in tag '%s'", name)
          else:
              raise ValueError('read_uic_tag: invalid string size %i' % size)
      elif dtype == '%ip':
          # sequence of pascal strings
          value = []
          for _ in range(planecount):
              size = read_int()
              if 0 <= size < 2**10:
                  string = struct.unpack('%is' % size, fh.read(size))[0][:-1]
                  string = bytes2str(stripnull(string))
                  value.append(string)
              elif offset:
                  log.warning("read_uic_tag: corrupt string in tag '%s'", name)
              else:
                  raise ValueError('read_uic_tag: invalid string size: %i' %
                                   size)
      else:
          # struct or numpy type
          dtype = '<' + dtype
          if '%i' in dtype:
              dtype = dtype % planecount
          if '(' in dtype:
              # numpy type
              value = fh.read_array(dtype, 1)[0]
              if value.shape[-1] == 2:
                  # assume fractions
                  value = value[..., 0] / value[..., 1]
          else:
              # struct format
              value = struct.unpack(dtype, fh.read(struct.calcsize(dtype)))
              if len(value) == 1:
                  value = value[0]
  
      if offset:
          fh.seek(pos + 4)
  
      return name, value