text: import struct
  from io import BytesIO
  
  PREFIXES = [
      b"MM\x00\x2A",  # Valid TIFF header with big-endian byte order
      b"II\x2A\x00",  # Valid TIFF header with little-endian byte order
      b"MM\x2A\x00",  # Invalid TIFF header, assume big-endian
      b"II\x00\x2A",  # Invalid TIFF header, assume little-endian
  ]
  
  EXIF_HEADER = b"Exif\x00\x00"
  
  
  class ExifOrientationEditor():
      _endian = "<"
      _offset = None
  
      def __init__(self, data):
          if data[:6] != EXIF_HEADER:
              raise SyntaxError("not a TIFF file (header %r not valid)" % data[:4])
  
          # Skip 6 bytes of exif header to simplify seeks in ImageFileDirectory
          self.exif_buffer = BytesIO(data[6:])
  
          header = self._read_header()
          self._find_orientation_offset(header)
  
      def _read_header(self):
          header = self.exif_buffer.read(8)
  
          if header[:4] not in PREFIXES:
              raise SyntaxError("not a TIFF file (header %r not valid)" % header)
          prefix = header[:2]
          if prefix == b"MM":
              self._endian = ">"
          elif prefix == b"II":
              self._endian = "<"
          else:
              raise SyntaxError("not a TIFF IFD")
  
          return header
  
      def _find_orientation_offset(self, header):
          ifd_offset, = self._unpack("L", header[4:])
          self.exif_buffer.seek(ifd_offset)
  
          # Read tag directory
          for _ in range(self._unpack("H", self.exif_buffer.read(2))[0]):
              # Each tag is 12 bytes. HHL4s = tag, type, count, data
              # Read tag and ignore the rest
              tag, = self._unpack("H10x", self.exif_buffer.read(12))
              if tag == 0x0112:  # Orientation tag
                  self._offset = self.exif_buffer.tell() - 4  # Back 4 bytes to the start of data
                  break
  
      def _unpack(self, fmt, data):
          return struct.unpack(self._endian + fmt, data)
  
      def get_orientation(self):
          if self._offset is None:
              return None
  
          self.exif_buffer.seek(self._offset)
          return self._unpack('H', self.exif_buffer.read(2))[0]
  
      def set_orientation(self, value):
          if self._offset is None:
              return
  
          self.exif_buffer.seek(self._offset)
          self.exif_buffer.write(struct.pack(self._endian + 'H', value))
  
      def tobytes(self):
          self.exif_buffer.seek(0)
          return EXIF_HEADER + self.exif_buffer.read()
  