Repository: psd-tools/psd-tools
Pull request #99 (2019-03-14 03:14:15)

1.8.11
- introduce terminology module (#98)
- reduce memory use in read (#58)
- add main testing


Patch:
diff --git a/CHANGES.rst b/CHANGES.rst
index ebee649..5cbea5c 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,3 +1,10 @@
+1.8.11 (2019-03-14)
+-------------------
+
+- introduce terminology module;
+- reduce memory use in read;
+- add main testing.
+
 1.8.10 (2019-02-27)
 -------------------
 
diff --git a/docs/index.rst b/docs/index.rst
index 992ea65..224b3d2 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -102,6 +102,7 @@ Not supported:
     reference/psd_tools.psd.patterns
     reference/psd_tools.psd.tagged_blocks
     reference/psd_tools.psd.vector
+    reference/psd_tools.terminology
 
 Indices and tables
 ------------------
diff --git a/docs/reference/psd_tools.terminology.rst b/docs/reference/psd_tools.terminology.rst
new file mode 100644
index 0000000..9519718
--- /dev/null
+++ b/docs/reference/psd_tools.terminology.rst
@@ -0,0 +1,60 @@
+psd\_tools\.terminology
+=======================
+
+.. automodule:: psd_tools.terminology
+
+Klass
+-----
+
+.. autoclass:: psd_tools.terminology.Klass
+    :members:
+    :undoc-members:
+    :private-members:
+
+Enum
+----
+
+.. autoclass:: psd_tools.terminology.Enum
+    :members:
+    :undoc-members:
+    :private-members:
+
+Event
+-----
+
+.. autoclass:: psd_tools.terminology.Event
+    :members:
+    :undoc-members:
+    :private-members:
+
+Form
+----
+
+.. autoclass:: psd_tools.terminology.Form
+    :members:
+    :undoc-members:
+    :private-members:
+
+Key
+---
+
+.. autoclass:: psd_tools.terminology.Key
+    :members:
+    :undoc-members:
+    :private-members:
+
+Type
+----
+
+.. autoclass:: psd_tools.terminology.Type
+    :members:
+    :undoc-members:
+    :private-members:
+
+Unit
+----
+
+.. autoclass:: psd_tools.terminology.Unit
+    :members:
+    :undoc-members:
+    :private-members:
diff --git a/src/psd_tools/__main__.py b/src/psd_tools/__main__.py
index a679da8..146d551 100644
--- a/src/psd_tools/__main__.py
+++ b/src/psd_tools/__main__.py
@@ -14,7 +14,7 @@ logger = logging.getLogger(__name__)
 logger.addHandler(logging.StreamHandler())
 
 
-def main():
+def main(argv=None):
     """
     psd-tools command line utility.
 
@@ -34,7 +34,7 @@ def main():
         psd-tools export example.psd[0] example-0.png  # Export layer as PNG
     """
 
-    args = docopt.docopt(main.__doc__, version=__version__)
+    args = docopt.docopt(main.__doc__, version=__version__, argv=argv)
 
     if args['--verbose']:
         logger.setLevel(logging.DEBUG)
@@ -63,7 +63,7 @@ def main():
 
     elif args['debug']:
         psd = PSDImage.open(args['<input_file>'])
-        pprint(psd._psd)
+        pprint(psd._record)
 
 if __name__ == "__main__":
     main()
diff --git a/src/psd_tools/api/adjustments.py b/src/psd_tools/api/adjustments.py
index 0baa027..976a723 100644
--- a/src/psd_tools/api/adjustments.py
+++ b/src/psd_tools/api/adjustments.py
@@ -57,13 +57,13 @@ class GradientFill(FillLayer):
         """
         Kind of the gradient, one of the following:
 
-         - `linear`
-         - `radial`
-         - `angle`
-         - `reflected`
-         - `diamond`
+         - `Linear`
+         - `Radial`
+         - `Angle`
+         - `Reflected`
+         - `Diamond`
         """
-        return self._data.get(b'Type').enum.name.lower()
+        return self._data.get(b'Type').get_name()
 
     @property
     def data(self):
diff --git a/src/psd_tools/api/composer.py b/src/psd_tools/api/composer.py
index cb344e1..ae27b3c 100644
--- a/src/psd_tools/api/composer.py
+++ b/src/psd_tools/api/composer.py
@@ -304,8 +304,8 @@ def draw_gradient_fill(image, setting, blend=True):
         return None
 
     angle = float(setting.get(b'Angl'))
-    gradient_kind = setting.get(b'Type').enum.name.lower()
-    if gradient_kind == 'linear':
+    gradient_kind = setting.get(b'Type').get_name()
+    if gradient_kind == 'Linear':
         Z = _make_linear_gradient(image.width, image.height, -angle)
     else:
         logger.warning('Only linear gradient is supported.')
diff --git a/src/psd_tools/api/effects.py b/src/psd_tools/api/effects.py
index de6c0c7..141cf6c 100644
--- a/src/psd_tools/api/effects.py
+++ b/src/psd_tools/api/effects.py
@@ -4,7 +4,8 @@ Effects module.
 from __future__ import absolute_import, unicode_literals
 import logging
 
-from psd_tools.constants import TaggedBlockID, DescriptorClassID
+from psd_tools.constants import TaggedBlockID
+from psd_tools.terminology import Klass, Enum, Key
 from psd_tools.utils import new_registry
 from psd_tools.psd.base import ValueElement
 from psd_tools.psd.descriptor import List
@@ -32,22 +33,22 @@ class Effects(object):
 
         self._items = []
         for key in (self._data or []):
-            if key in (b'masterFXSwitch', DescriptorClassID.SCALE):
+            if key in (b'masterFXSwitch', Key.Scale.value):
                 continue
             value = self._data[key]
             if not isinstance(value, List):
                 value = [value]
             for item in value:
-                if not item.get(DescriptorClassID.ENABLED):
+                if not item.get(Key.Enabled):
                     continue
                 kls = _TYPES.get(item.classID)
-                assert kls is not None
+                assert kls is not None, 'kls not found for %r' % item.classID
                 self._items.append(kls(item, layer._psd.image_resources))
 
     @property
     def scale(self):
         """Scale value."""
-        return self._data.get(DescriptorClassID.SCALE).value
+        return self._data.get(Key.Scale).value
 
     @property
     def enabled(self):
@@ -93,7 +94,7 @@ class _Effect(object):
     @property
     def enabled(self):
         """Whether if the effect is enabled."""
-        return bool(self.value.get(DescriptorClassID.ENABLED))
+        return bool(self.value.get(Key.Enabled))
 
     @property
     def present(self):
@@ -108,7 +109,7 @@ class _Effect(object):
     @property
     def opacity(self):
         """Layer effect opacity in percentage."""
-        return self.value.get(DescriptorClassID.OPACITY).value
+        return self.value.get(Key.Opacity).value
 
     def __repr__(self):
         return self.__class__.__name__
@@ -123,61 +124,60 @@ class _ColorMixin(object):
     @property
     def color(self):
         """Color."""
-        return self.value.get(DescriptorClassID.COLOR)
+        return self.value.get(Key.Color)
 
     @property
     def blend_mode(self):
         """Effect blending mode."""
-        mode = self.value.get(DescriptorClassID.MODE)
-        return mode.enum.name.lower().replace('blend_', '')
+        return self.value.get(Key.Mode).get_name()
 
 
 class _ChokeNoiseMixin(_ColorMixin):
     @property
     def choke(self):
         """Choke level."""
-        return self.value.get(DescriptorClassID.CHOKE).value
+        return self.value.get(Key.ChokeMatte).value
 
     @property
     def size(self):
         """Size in pixels."""
-        return self.value.get(DescriptorClassID.BLUR).value
+        return self.value.get(Key.Blur).value
 
     @property
     def noise(self):
         """Noise level."""
-        return self.value.get(DescriptorClassID.NOISE).value
+        return self.value.get(Key.Noise).value
 
     @property
     def anti_aliased(self):
         """Angi-aliased."""
-        return bool(self.value.get(DescriptorClassID.ANTI_ALIASED))
+        return bool(self.value.get(Key.AntiAlias))
 
     @property
     def contour(self):
         """Contour configuration."""
-        return self.value.get(DescriptorClassID.STOP_OPACITY)
+        return self.value.get(Key.TransferSpec)
 
 
 class _AngleMixin(object):
     @property
     def use_global_light(self):
         """Using global light."""
-        return bool(self.value.get(DescriptorClassID.USE_GLOBAL_LIGHT))
+        return bool(self.value.get(Key.UseGlobalAngle))
 
     @property
     def angle(self):
         """Angle value."""
         if self.use_global_light:
             return self._image_resources.get_data('global_angle', 30.0)
-        return self.value.get(DescriptorClassID.LIGHT_ANGLE).value
+        return self.value.get(Key.LocalLightingAngle).value
 
 
 class _GradientMixin(object):
     @property
     def gradient(self):
         """Gradient configuration."""
-        return self.value.get(DescriptorClassID.GRADIENT_CONFIG)
+        return self.value.get(Key.Gradient)
 
 
 class _PatternMixin(object):
@@ -185,7 +185,7 @@ class _PatternMixin(object):
     def pattern(self):
         """Pattern config."""
         # TODO: Expose nested property.
-        return self.value.get(DescriptorClassID.PATTERN)
+        return self.value.get(b'Ptrn')  # Enum.Pattern. Seems a bug.
 
 
 class _ShadowEffect(_Effect, _ChokeNoiseMixin, _AngleMixin):
@@ -193,27 +193,25 @@ class _ShadowEffect(_Effect, _ChokeNoiseMixin, _AngleMixin):
     @property
     def distance(self):
         """Distance."""
-        return self.value.get(DescriptorClassID.DISTANCE).value
+        return self.value.get(Key.Distance).value
 
 
 class _GlowEffect(_Effect, _ChokeNoiseMixin, _GradientMixin):
     """Base class for glow effect."""
     @property
     def glow_type(self):
-        """Glow type. 'softer' or 'precise'."""
-        value = self.value.get(DescriptorClassID.GLOW_TYPE).enum.name.lower()
-        value = value.replace('bevel_', '')
-        return {'smooth': 'softer'}.get(value, 'precise')
+        """Glow type."""
+        return self.value.get(Key.GlowTechnique).get_name()
 
     @property
     def quality_range(self):
         """Quality range."""
-        return self.value.get(DescriptorClassID.QUARYTY_RANGE).value
+        return self.value.get(Key.InputRange).value
 
     @property
     def quality_jitter(self):
         """Quality jitter"""
-        return self.value.get(DescriptorClassID.QUALITY_JITTER).value
+        return self.value.get(Key.ShadingNoise).value
 
 
 class _OverlayEffect(_Effect):
@@ -224,21 +222,20 @@ class _AlignScaleMixin(object):
     @property
     def blend_mode(self):
         """Effect blending mode."""
-        mode = self.value.get(DescriptorClassID.MODE)
-        return mode.enum.name.lower().replace('blend_', '')
+        return self.value.get(Key.Mode).get_name()
 
     @property
     def scale(self):
         """Scale value."""
-        return self.value.get(DescriptorClassID.SCALE).value
+        return self.value.get(Key.Scale).value
 
     @property
     def aligned(self):
         """Aligned."""
-        return bool(self.value.get(DescriptorClassID.ALIGNED))
+        return bool(self.value.get(Key.Alignment))
 
 
-@register(DescriptorClassID.DROP_SHADOW)
+@register(Klass.DropShadow.value)
 class DropShadow(_ShadowEffect):
     @property
     def layer_knocks_out(self):
@@ -246,39 +243,37 @@ class DropShadow(_ShadowEffect):
         return bool(self.value.get(b'layerConceals'))
 
 
-@register(DescriptorClassID.INNER_SHADOW)
+@register(Klass.InnerShadow.value)
 class InnerShadow(_ShadowEffect):
     pass
 
 
-@register(DescriptorClassID.OUTER_GLOW)
+@register(Klass.OuterGlow.value)
 class OuterGlow(_GlowEffect):
     @property
     def spread(self):
-        return self.value.get(DescriptorClassID.QUALITY_JITTER).value
+        return self.value.get(Key.ShadingNoise).value
 
 
-@register(DescriptorClassID.INNER_GLOW)
+@register(Klass.InnerGlow.value)
 class InnerGlow(_GlowEffect):
     @property
     def glow_source(self):
-        """Elements source, 'edge' or 'center'."""
-        value = self.value.get(DescriptorClassID.GLOW_SOURCE).enum.name
-        value = value.lower().replace('glow_source_', '')
-        return value
+        """Elements source."""
+        return self.value.get(Key.InnerGlowSource).get_name()
 
 
-@register(DescriptorClassID.COLOR_OVERLAY)
+@register(Klass.SolidFill.value)
 class ColorOverlay(_OverlayEffect, _ColorMixin):
     pass
 
 
-@register(DescriptorClassID.GRADIENT_OVERLAY)
+@register(b'GrFl')  # Equalt to Enum.GradientFill. This seems a bug.
 class GradientOverlay(_OverlayEffect, _AlignScaleMixin, _GradientMixin):
     @property
     def angle(self):
         """Angle value."""
-        return self.value.get(DescriptorClassID.ANGLE).value
+        return self.value.get(Key.Angle).value
 
     @property
     def type(self):
@@ -286,23 +281,22 @@ class GradientOverlay(_OverlayEffect, _AlignScaleMixin, _GradientMixin):
         Gradient type, one of `linear`, `radial`, `angle`, `reflected`, or
         `diamond`.
         """
-        value = self.value.get(DescriptorClassID.TYPE).enum.name.lower()
-        return value.replace('gradient_', '')
+        return self.value.get(Key.Type).get_name()
 
     @property
     def reversed(self):
         """Reverse flag."""
-        return bool(self.value.get(DescriptorClassID.REVERSED))
+        return bool(self.value.get(Key.Reverse))
 
     @property
     def dithered(self):
         """Dither flag."""
-        return bool(self.value.get(DescriptorClassID.DITHERED))
+        return bool(self.value.get(Key.Dither))
 
     @property
     def offset(self):
         """Offset value."""
-        return self.value.get(DescriptorClassID.OFFSET)
+        return self.value.get(Key.Offset)
 
 
 @register(b'patternFill')
@@ -313,24 +307,25 @@ class PatternOverlay(_OverlayEffect, _AlignScaleMixin, _PatternMixin):
         return self.value.get(b'phase')
 
 
-@register(DescriptorClassID.STROKE)
+@register(Klass.FrameFX.value)
 class Stroke(_Effect, _ColorMixin, _PatternMixin, _GradientMixin):
 
     @property
     def position(self):
-        """Position of the stroke, `inner`, `outer`, or `center`."""
-        value = self.value.get(b'Styl')
-        return value.enum.name.lower().replace('stroke_', '')
+        """
+        Position of the stroke, InsetFrame, OutsetFrame, or CenteredFrame.
+        """
+        return self.value.get(Key.Style).get_name()
 
     @property
     def fill_type(self):
-        """Fill type, solid-color, gradient, or pattern."""
-        return self.value.get(b'PntT').enum.name.lower().replace('_', '-')
+        """Fill type, SolidColor, Gradient, or Pattern."""
+        return self.value.get(Key.PaintType).get_name()
 
     @property
     def size(self):
         """Size value."""
-        return self.value.get(b'Sz  ').value
+        return self.value.get(Key.SizeKey).value
 
     @property
     def overprint(self):
@@ -338,82 +333,76 @@ class Stroke(_Effect, _ColorMixin, _PatternMixin, _GradientMixin):
         return bool(self.value.get(b'overprint'))
 
 
-@register(DescriptorClassID.BEVEL_EMBOSS)
+@register(Klass.BevelEmboss.value)
 class BevelEmboss(_Effect, _AngleMixin):
 
     @property
     def highlight_mode(self):
         """Highlight blending mode."""
-        value = self.value.get(b'hglM').enum
-        value = value.decode() if isinstance(value, bytes) else value.name
-        return value.lower().replace('blend_', '').replace('_', '-')
+        return self.value.get(Key.HighlightMode).get_name()
 
     @property
     def highlight_color(self):
         """Highlight color value."""
-        return self.value.get(b'hglC')
+        return self.value.get(Key.HighlightColor)
 
     @property
     def highlight_opacity(self):
         """Highlight opacity value."""
-        return self.value.get(b'hglO').value
+        return self.value.get(Key.HighlightOpacity).value
 
     @property
     def shadow_mode(self):
         """Shadow blending mode."""
-        value = self.value.get(b'sdwM').enum
-        value = value.decode() if isinstance(value, bytes) else value.name
-        return value.lower().replace('blend_', '').replace('_', '-')
+        return self.value.get(Key.ShadowMode).get_name()
 
     @property
     def shadow_color(self):
         """Shadow color value."""
-        return self.value.get(b'sdwC')
+        return self.value.get(Key.ShadowColor)
 
     @property
     def shadow_opacity(self):
         """Shadow opacity value."""
-        return self.value.get(b'sdwO').value
+        return self.value.get(Key.ShadowOpacity).value
 
     @property
     def bevel_type(self):
-        """Bevel type, one of `smooth`, `chiesel-hard`, `chiesel-soft`."""
-        value = self.value.get(b'bvlT').enum.name.lower()
-        return value.replace('bevel_', '').replace('_', '-')
+        """Bevel type, one of `SoftMatte`, `HardLight`, `SoftLight`."""
+        return self.value.get(Key.BevelTechnique).get_name()
 
     @property
     def bevel_style(self):
         """
-        Bevel style, one of `outer`, `inner`, `emboss`,
-        `pillow-emboss`, or `stroke-emboss`.
+        Bevel style, one of `OuterBevel`, `InnerBevel`, `Emboss`,
+        `PillowEmboss`, or `StrokeEmboss`.
         """
-        value = self.value.get(b'bvlS').enum.name.lower()
-        return value.replace('bevel_style_', '').replace('_', '-')
+        return self.value.get(Key.BevelStyle).get_name()
 
     @property
     def altitude(self):
         """Altitude value."""
-        return self.value.get(b'Lald').value
+        return self.value.get(Key.LocalLightingAltitude).value
 
     @property
     def depth(self):
         """Depth value."""
-        return self.value.get(b'srgR').value
+        return self.value.get(Key.StrengthRatio).value
 
     @property
     def size(self):
         """Size value in pixel."""
-        return self.value.get(b'blur').value
+        return self.value.get(Key.Blur).value
 
     @property
     def direction(self):
-        """Direction, either `in` or `out`."""
-        return self.value.get(b'bvlD').enum.name.lower()
+        """Direction, either `StampIn` or `StampOut`."""
+        return self.value.get(Key.BevelDirection).get_name()
 
     @property
     def contour(self):
         """Contour configuration."""
-        return self.value.get(b'TrnS')
+        return self.value.get(Key.TransferSpec)
 
     @property
     def anti_aliased(self):
@@ -423,7 +412,7 @@ class BevelEmboss(_Effect, _AngleMixin):
     @property
     def soften(self):
         """Soften value."""
-        return self.value.get(b'Sftn').value
+        return self.value.get(Key.Softness).value
 
     @property
     def use_shape(self):
@@ -436,35 +425,35 @@ class BevelEmboss(_Effect, _AngleMixin):
         return bool(self.value.get(b'useTexture'))
 
 
-@register(DescriptorClassID.SATIN)
+@register(Klass.ChromeFX.value)
 class Satin(_Effect, _ColorMixin):
     """ Satin effect """
     @property
     def anti_aliased(self):
         """Anti-aliased."""
-        return bool(self.value.get(b'AntA'))
+        return bool(self.value.get(Key.AntiAlias))
 
     @property
     def inverted(self):
         """Inverted."""
-        return bool(self.value.get(b'Invr'))
+        return bool(self.value.get(Key.Invert))
 
     @property
     def angle(self):
         """Angle value."""
-        return self.value.get(b'lagl').value
+        return self.value.get(Key.LocalLightingAngle).value
 
     @property
     def distance(self):
         """Distance value."""
-        return self.value.get(b'Dstn').value
+        return self.value.get(Key.Distance).value
 
     @property
     def size(self):
         """Size value in pixel."""
-        return self.value.get(b'blur').value
+        return self.value.get(Key.Blur).value
 
     @property
     def contour(self):
         """Contour configuration."""
-        return self.value.get(b'MpgS')
+        return self.value.get(Key.MappingShape)
diff --git a/src/psd_tools/api/layers.py b/src/psd_tools/api/layers.py
index 698fee5..15cf02d 100644
--- a/src/psd_tools/api/layers.py
+++ b/src/psd_tools/api/layers.py
@@ -579,12 +579,12 @@ class Artboard(Group):
                 if key in self.tagged_blocks:
                     data = self.tagged_blocks.get_data(key)
             assert data is not None
-            rect = data.get('artboardRect')
+            rect = data.get(b'artboardRect')
             self._bbox = (
-                int(rect.get('Left')),
-                int(rect.get('Top ')),
-                int(rect.get('Rght')),
-                int(rect.get('Btom')),
+                int(rect.get(b'Left')),
+                int(rect.get(b'Top ')),
+                int(rect.get(b'Rght')),
+                int(rect.get(b'Btom')),
             )
         return self._bbox
 
diff --git a/src/psd_tools/constants.py b/src/psd_tools/constants.py
index 0266997..1f0b9c5 100644
--- a/src/psd_tools/constants.py
+++ b/src/psd_tools/constants.py
@@ -410,634 +410,6 @@ class OSType(Enum):
     NAME = b'name'
 
 
-class UnitFloatType(Enum):
-    """
-    Units the value is in (used in Unit float structure).
-    """
-    ANGLE = b'#Ang'  # base degrees
-    DENSITY = b'#Rsl'  # base per inch
-    DISTANCE = b'#Rlt'  # base 72ppi
-    NONE = b'#Nne'  # coerced
-    PERCENT = b'#Prc'  # unit value
-    PIXELS = b'#Pxl'  # tagged unit value
-    POINTS = b'#Pnt'  # points
-    MILLIMETERS = b'#Mlm'  # millimeters
-
-
-class DescriptorClassID(Enum):
-    """
-    Descriptor classID.
-    """
-    NULL = b'null'
-    NAME = b'Nm  '
-    IDENTIFIER = b'Idnt'
-    VERSION = b'Vrsn'
-
-    ALIAS = b'alis'
-
-    SHAPE = b'ShpC'
-    CURVE = b'Crv '
-    TOP = b'Top '
-    LEFT = b'Left'
-    BOTTOM = b'Btom'
-    RIGHT = b'Rght'
-
-    CNT = b'Cnt '  # ???
-    POINT = b'Pnt '
-    CONTROL_POINT = b'CrPt'
-    HORIZONTAL = b'Hrzn'
-    VERTICAL = b'Vrtc'
-
-    COLOR = b'Clr '
-    RGB = b'RGBC'
-    GRAYSCALE = b'Grsc'
-    CMYK = b'CMYC'
-
-    RED = b'Rd  '
-    GREEN = b'Grn '
-    BLUE = b'Bl  '
-    GRAY = b'Gry '
-    CYAN = b'Cyn '
-    MAGENTA = b'Mgnt'
-    YELLOW = b'Yllw'
-
-    PATTERN = b'Ptrn'
-
-    # Gradient configuration
-    GRADIENT = b'Grdn'
-    TYPE = b'Type'
-    CLRY = b'Clry'  # Color picker?
-    USRS = b'UsrS'  # User setting?
-    LOCATION = b'Lctn'
-    MID_POINT = b'Mdpn'
-    OPACITY = b'Opct'
-    STOP_COLOR = b'Clrt'
-    STOP_OPACITY = b'TrnS'
-
-    LINEAR = b'Lnr '
-    GRADIENT_RADIAL = b'Rdl '
-    GRADIENT_REFLECTED = b'Rflc'
-    GRADIENT_DIAMOND = b'DMND'
-    COLORS = b'Clrs'
-    INTERPOLATION = b'Intr'
-    TRANSPARENCY = b'Trns'
-
-    GRADIENT_CONFIG = b'Grad'
-    GRADIENT_TYPE = b'GrdT'
-
-    GRADIENT_TYPE_VALUE = b'GrdF'
-    GRADIENT_TYPE_SOLID = b'CstS'
-
-    # Noise gradient
-    SH_TRANSPARENCY = b'ShTr'
-    RESTRICT_COLORS = b'VctC'
-    RANDOM_SEED = b'RndS'
-    SMOOTHNESS = b'Smth'
-    MINIMUM = b'Mnm '
-    MAXIMUM = b'Mxm '
-
-    # BrightnessContrast adjustment
-    BC_BRIGHTNESS = b'Brgh'
-    BC_CONTRAST = b'Cntr'
-    BC_LAB = b'Lab '
-    BC_AUTO = b'Auto'
-
-    # Vibrance adjustment
-    SATURATION = b'Strt'
-
-    ENABLED = b'enab'
-    ANGLE = b'Angl'
-    MODE = b'Md  '
-    CHOKE = b'Ckmt'
-    BLUR = b'blur'
-    NOISE = b'Nose'
-    ANTI_ALIASED = b'AntA'
-    DITHERED = b'Dthr'
-
-    LAYER_I = b'LyrI'  # ???
-    OFFSET = b'Ofst'
-    LAYER_FONT_EFFECT = b'Lefx'  # ???
-    SCALE = b'Scl '
-    # LAYER_EFFECT = b'FrFX'
-    FSTL = b'FStl'  # ???
-
-    FR_FILL = b'FrFl'  # ???
-    SOLID_COLOR = b'SClr'
-
-    # Stroke
-    STROKE_STYLE = b'Styl'
-    STROKE_INNER = b'InsF'
-    STROKE_OUTER = b'OutF'
-    STROKE_CENTER = b'CtrF'
-    FILL_TYPE = b'PntT'
-
-    REVERSED = b'Rvrs'
-    ALIGNED = b'Algn'
-
-    # Blend mode
-    BLEND_MODE = b'BlnM'
-    BLEND_NORMAL = b'Nrml'
-    BLEND_DISSOLVE = b'Dslv'
-    BLEND_DARKEN = b'Drkn'
-    BLEND_MULTIPLY = b'Mltp'
-    BLEND_COLOR_BURN = b'CBrn'
-    BLEND_LINEAR_BURN = b'linearBurn'
-    BLEND_DARKER_COLOR = b'darkerColor'
-    BLEND_LIGHTEN = b'Lghn'
-    BLEND_SCREEN = b'Scrn'
-    BLEND_COLOR_DODGE = b'CDdg'
-    BLEND_LINEAR_DODGE = b'linearDodge'
-    BLEND_LIGHTER_COLOR = b'lighterColor'
-    BLEND_OVERLAY = b'Ovrl'
-    BLEND_SOFT_LIGHT = b'SftL'
-    BLEND_HARD_LIGHT = b'HrdL'
-    BLEND_VIVID_LIGHT = b'vividLight'
-    BLEND_LINEAR_LIGHT = b'linearLight'
-    BLEND_PIN_LIGHT = b'pinLight'
-    BLEND_HARD_MIX = b'hardMix'
-    BLEND_DIFFERENCE = b'Dfrn'
-    BLEND_EXCLUSION = b'Xclu'
-    BLEND_SUBTRACT = b'blendSubtraction'
-    BLEND_DIVIDE = b'blendDivide'
-    BLEND_HUE = b'H   '
-    BLEND_SATURATION = b'Strt'
-    BLEND_COLOR = b'Clr '
-    BLEND_LUMINOSITY = b'Lmns'
-
-    DISTANCE = b'Dstn'
-    SIZE = b'Sz  '
-
-    # Effect types
-    DROP_SHADOW = b'DrSh'
-    INNER_SHADOW = b'IrSh'
-    OUTER_GLOW = b'OrGl'
-    COLOR_OVERLAY = b'SoFi'
-    GRADIENT_OVERLAY = b'GrFl'
-    STROKE = b'FrFX'
-    INNER_GLOW = b'IrGl'
-    BEVEL_EMBOSS = b'ebbl'
-    SATIN = b'ChFX'
-
-    USE_GLOBAL_LIGHT = b'uglg'
-
-    # Grow
-    GLOW_TYPE = b'GlwT'
-    GLOW_SOURCE = b'glwS'
-
-    GLOW_SOURCE_TYPE = b'IGSr'
-    GLOW_SOURCE_EDGE = b'SrcE'
-    GLOW_SOURCE_CENTER = b'SrcC'
-
-    INVERTED = b'Invr'
-    QUARYTY_RANGE = b'Inpr'
-    QUALITY_JITTER = b'ShdN'
-
-    # BevelEmboss
-    BEVEL_TYPE = b'bvlT'
-    BEVEL_SMOOTH = b'SfBL'
-    BEVEL_CHIESEL_HARD = b'PrBL'
-    BEVEL_CHIESEL_SOFT = b'Slmt'
-
-    BEVEL_STYLE = b'bvlS'
-    BEVEL_STYLE_TYPE = b'BESl'
-    BEVEL_STYLE_OUTER = b'OtrB'
-    BEVEL_STYLE_INNER = b'InrB'
-    BEVEL_STYLE_EMBOSS = b'Embs'
-    BEVEL_STYLE_PILLOW_EMBOSS = b'PlEb'
-    # BEVEL_STROKE_EMBOSS = b'strokeEmboss'
-
-    BEVEL_DIRECTION = b'bvlD'
-    BEVEL_DIRECTION_TYPE = b'BESs'  # ???
-    IN = b'In  '
-    OUT = b'Out '
-
-    GLOW_TYPE_VALUE = b'BETE'  # ???
-
-    HIGHLIGHT_MODE = b'hglM'
-    HIGHLIGHT_COLOR = b'hglC'
-    HIGHLIGHT_OPACITY = b'hglO'
-
-    SHADOW_MODE = b'sdwM'
-    SHADOW_COLOR = b'sdwC'
-    SHADOW_OPACITY = b'sdwO'
-
-    ALTITUDE = b'Lald'
-    DEPTH = b'srgR'
-    SOFTEN = b'Sftn'
-
-    # Satin
-    LIGHT_ANGLE = b'lagl'
-    SATIN_CONTOUR = b'MpgS'
-
-    # Text property
-    TEXT_LAYER = b'TxLr'
-    TEXT = b'Txt '
-    NONE = b'None'
-    ORIENTATION = b'Ornt'
-    NOISE_GRADIENT = b'ClNs'
-    COLORS2 = b'ClrS'
-
-    # Smart object
-    PAGE_NUMBER = b'PgNm'
-    TRANSFORM = b'Trnf'
-    RECTANGLE = b'Rctn'
-    CROP = b'Crop'
-    WIDTH = b'Wdth'
-    HEIGHT = b'Hght'
-    RESOLUTION = b'Rslt'
-    COMP = b'comp'
-
-    ANTI_ALIASE = b'Annt'  # ???
-
-    ANCR = b'AnCr'
-    ANSM = b'AnSm'
-    ANST = b'AnSt'
-    name = b'name'
-    Rnd  = b'Rnd '
-    FrgC = b'FrgC'
-    BckC = b'BckC'
-    Fltr = b'Fltr'
-    UnsM = b'UnsM'
-    Amnt = b'Amnt'
-    Rds  = b'Rds '
-    Thsh = b'Thsh'
-    X    = b'X   '
-    Y    = b'Y   '
-    Z    = b'Z   '
-    VrsM = b'VrsM'
-    VrsN = b'VrsN'
-    VrsF = b'VrsF'
-    GamR = b'GamR'
-    GamG = b'GamG'
-    GamB = b'GamB'
-    Glnr = b'Glnr'
-    Glbi = b'Glbi'
-    Gunt = b'Gunt'
-    Gast = b'Gast'
-    Gfrm = b'Gfrm'
-    Gfps = b'Gfps'
-    rrwp = b'rrwp'
-    rrmu = b'rrmu'
-    mhtp = b'mhtp'
-    Flip = b'Flip'
-    GRNm = b'GRNm'
-    lite = b'lite'
-    hots = b'hots'
-    FlOf = b'FlOf'
-    shdw = b'shdw'
-    attn = b'attn'
-    attt = b'attt'
-    atta = b'atta'
-    attb = b'attb'
-    attc = b'attc'
-    orad = b'orad'
-    irad = b'irad'
-    mult = b'mult'
-    ison = b'ison'
-    ssml = b'ssml'
-    afon = b'afon'
-    afpw = b'afpw'
-    tarx = b'tarx'
-    tary = b'tary'
-    tarz = b'tarz'
-    caml = b'caml'
-    camc = b'camc'
-    bank = b'bank'
-    Lns  = b'Lns '
-    orth = b'orth'
-    aspr = b'aspr'
-    zmfc = b'zmfc'
-    mshl = b'mshl'
-    msho = b'msho'
-    verl = b'verl'
-    nrml = b'nrml'
-    uvl  = b'uvl '
-    faci = b'faci'
-    plyc = b'plyc'
-    Mtrx = b'Mtrx'
-    flag = b'flag'
-    hidn = b'hidn'
-    hmat = b'hmat'
-    hsmt = b'hsmt'
-    misv = b'misv'
-    misc = b'misc'
-    miss = b'miss'
-    misi = b'misi'
-    Grup = b'Grup'
-    OrgH = b'OrgH'
-    tRiD = b'tRiD'
-    Ang1 = b'Ang1'
-    Ang2 = b'Ang2'
-    FTcs = b'FTcs'
-    QCSt = b'QCSt'
-    QCsa = b'QCsa'
-    Qlty = b'Qlty'
-    Nw   = b'Nw  '
-    PrnS = b'PrnS'
-    Rct1 = b'Rct1'
-    Bckg = b'Bckg'
-    Clsp = b'Clsp'
-    Qcsa = b'Qcsa'
-    Mtrl = b'Mtrl'
-    mtID = b'mtID'
-    Srce = b'Srce'
-    Dfs  = b'Dfs '
-    mtll = b'mtll'
-    mtlo = b'mtlo'
-    ared = b'ared'
-    agrn = b'agrn'
-    ablu = b'ablu'
-    dred = b'dred'
-    dgrn = b'dgrn'
-    dblu = b'dblu'
-    sred = b'sred'
-    sgrn = b'sgrn'
-    sblu = b'sblu'
-    ered = b'ered'
-    egrn = b'egrn'
-    eblu = b'eblu'
-    shin = b'shin'
-    shi2 = b'shi2'
-    rtgh = b'rtgh'
-    rogh = b'rogh'
-    refl = b'refl'
-    self = b'self'
-    shad = b'shad'
-    twos = b'twos'
-    wire = b'wire'
-    decl = b'decl'
-    wfsz = b'wfsz'
-    RfAc = b'RfAc'
-    gMtr = b'gMtr'
-    mIHd = b'mIHd'
-    mapl = b'mapl'
-    mapo = b'mapo'
-    srgh = b'srgh'
-    uscl = b'uscl'
-    vscl = b'vscl'
-    uoff = b'uoff'
-    voff = b'voff'
-    msty = b'msty'
-    sTyP = b'sTyP'
-    sWrU = b'sWrU'
-    sWrV = b'sWrV'
-    sWrW = b'sWrW'
-    sMin = b'sMin'
-    sMag = b'sMag'
-    sMip = b'sMip'
-    sUsE = b'sUsE'
-    KeFL = b'KeFL'
-    KeCS = b'KeCS'
-    InsN = b'InsN'
-    flgO = b'flgO'
-    flgT = b'flgT'
-    NoID = b'NoID'
-    PrID = b'PrID'
-    tYpE = b'tYpE'
-    tYgE = b'tYgE'
-    PvLs = b'PvLs'
-    PvtO = b'PvtO'
-    PvtX = b'PvtX'
-    PvtY = b'PvtY'
-    PvtZ = b'PvtZ'
-    ExLs = b'ExLs'
-    ExOb = b'ExOb'
-    PvPr = b'PvPr'
-    LcMt = b'LcMt'
-    RgBl = b'RgBl'
-    sTCl = b'sTCl'
-    repo = b'repo'
-    GsnB = b'GsnB'
-    HSBC = b'HSBC'
-    Lyr  = b'Lyr '
-    Ordn = b'Ordn'
-    Trgt = b'Trgt'
-    Ylw  = b'Ylw '
-    Blck = b'Blck'
-    LghS = b'LghS'
-    On   = b'On  '
-    Fcs  = b'Fcs '
-    Intn = b'Intn'
-    LghT = b'LghT'
-    Pstn = b'Pstn'
-    Vct0 = b'Vct0'
-    Vct1 = b'Vct1'
-    CrnL = b'CrnL'
-    Glos = b'Glos'
-    Exps = b'Exps'
-    AmbB = b'AmbB'
-    AmbC = b'AmbC'
-    FrmW = b'FrmW'
-    BmpA = b'BmpA'
-    BmpC = b'BmpC'
-    LnsF = b'LnsF'
-    FlrC = b'FlrC'
-    Nkn1 = b'Nkn1'
-    Dspl = b'Dspl'
-    HrzS = b'HrzS'
-    VrtS = b'VrtS'
-    DspM = b'DspM'
-    StrF = b'StrF'
-    UndA = b'UndA'
-    RptE = b'RptE'
-    DspF = b'DspF'
-    HStr = b'HStr'
-    Clrz = b'Clrz'
-    Adjs = b'Adjs'
-    Hst2 = b'Hst2'
-    Lght = b'Lght'
-    LaID = b'LaID'
-    LaSt = b'LaSt'
-    FrLs = b'FrLs'
-    CMod = b'CMod'
-    Sett = b'Sett'
-    Cst  = b'Cst '
-    WBal = b'WBal'
-    AsSh = b'AsSh'
-    Temp = b'Temp'
-    Tint = b'Tint'
-    CtoG = b'CtoG'
-    Shrp = b'Shrp'
-    LNR  = b'LNR '
-    CNR  = b'CNR '
-    VigA = b'VigA'
-    BlkB = b'BlkB'
-    RHue = b'RHue'
-    RSat = b'RSat'
-    GHue = b'GHue'
-    GSat = b'GSat'
-    BHue = b'BHue'
-    BSat = b'BSat'
-    Vibr = b'Vibr'
-    HA_R = b'HA_R'
-    HA_O = b'HA_O'
-    HA_Y = b'HA_Y'
-    HA_G = b'HA_G'
-    HA_A = b'HA_A'
-    HA_B = b'HA_B'
-    HA_P = b'HA_P'
-    HA_M = b'HA_M'
-    SA_R = b'SA_R'
-    SA_O = b'SA_O'
-    SA_Y = b'SA_Y'
-    SA_G = b'SA_G'
-    SA_A = b'SA_A'
-    SA_B = b'SA_B'
-    SA_P = b'SA_P'
-    SA_M = b'SA_M'
-    LA_R = b'LA_R'
-    LA_O = b'LA_O'
-    LA_Y = b'LA_Y'
-    LA_G = b'LA_G'
-    LA_A = b'LA_A'
-    LA_B = b'LA_B'
-    LA_P = b'LA_P'
-    LA_M = b'LA_M'
-    STSH = b'STSH'
-    STSS = b'STSS'
-    STHH = b'STHH'
-    STHS = b'STHS'
-    STB  = b'STB '
-    PC_S = b'PC_S'
-    PC_D = b'PC_D'
-    PC_L = b'PC_L'
-    PC_H = b'PC_H'
-    PC_1 = b'PC_1'
-    PC_2 = b'PC_2'
-    PC_3 = b'PC_3'
-    ShpR = b'ShpR'
-    ShpD = b'ShpD'
-    ShpM = b'ShpM'
-    PCVA = b'PCVA'
-    GRNA = b'GRNA'
-    LPEn = b'LPEn'
-    MDis = b'MDis'
-    PerV = b'PerV'
-    PerH = b'PerH'
-    PerR = b'PerR'
-    PerS = b'PerS'
-    PerA = b'PerA'
-    PerU = b'PerU'
-    PerX = b'PerX'
-    PerY = b'PerY'
-    AuCA = b'AuCA'
-    Ex12 = b'Ex12'
-    Cr12 = b'Cr12'
-    Hi12 = b'Hi12'
-    Sh12 = b'Sh12'
-    Wh12 = b'Wh12'
-    Bk12 = b'Bk12'
-    Cl12 = b'Cl12'
-    DfPA = b'DfPA'
-    DPHL = b'DPHL'
-    DPHH = b'DPHH'
-    DfGA = b'DfGA'
-    DPGL = b'DPGL'
-    DPGH = b'DPGH'
-    Dhze = b'Dhze'
-    CrvR = b'CrvR'
-    CrvG = b'CrvG'
-    CrvB = b'CrvB'
-    CamP = b'CamP'
-    CP_D = b'CP_D'
-    PrVe = b'PrVe'
-    Rtch = b'Rtch'
-    REye = b'REye'
-    LCs  = b'LCs '
-    Upri = b'Upri'
-    GuUr = b'GuUr'
-    FXRf = b'FXRf'
-    IMsk = b'IMsk'
-    Lvls = b'Lvls'
-    LvlA = b'LvlA'
-    Chnl = b'Chnl'
-    Cmps = b'Cmps'
-    Inpt = b'Inpt'
-    Gmm  = b'Gmm '
-    AWBV = b'AWBV'
-    TMMs = b'TMMs'
-    LNRD = b'LNRD'
-    CNRD = b'CNRD'
-    LNRC = b'LNRC'
-    CNRS = b'CNRS'
-    Nkn  = b'Nkn '
-    HghP = b'HghP'
-    LbCl = b'LbCl'
-    Lmnc = b'Lmnc'
-    A    = b'A   '
-    B    = b'B   '
-    Expn = b'Expn'
-    PstS = b'PstS'
-    Inte = b'Inte'
-    Img  = b'Img '
-    Bltn = b'Bltn'
-    Cptn = b'Cptn'
-    Clbr = b'Clbr'
-    RgsM = b'RgsM'
-    CrnC = b'CrnC'
-    CntC = b'CntC'
-    Lbls = b'Lbls'
-    Ngtv = b'Ngtv'
-    EmlD = b'EmlD'
-    BrdT = b'BrdT'
-    Bld  = b'Bld '
-    PgPs = b'PgPs'
-    PgPC = b'PgPC'
-    Clrm = b'Clrm'
-    MpBl = b'MpBl'
-    Spcn = b'Spcn'
-    LCnt = b'LCnt'
-    Msge = b'Msge'
-    Path = b'Path'
-    AdNs = b'AdNs'
-    Dstr = b'Dstr'
-    Unfr = b'Unfr'
-    Mnch = b'Mnch'
-    FlRs = b'FlRs'
-    PaCm = b'PaCm'
-    SbpL = b'SbpL'
-    Sbpl = b'Sbpl'
-    Pts  = b'Pts '
-    Pthp = b'Pthp'
-    Anch = b'Anch'
-    Fwd  = b'Fwd '
-    Bwd  = b'Bwd '
-    Smoo = b'Smoo'
-    InvT = b'InvT'
-    BanW = b'BanW'
-    SlcC = b'SlcC'
-    Mthd = b'Mthd'
-    CrcM = b'CrcM'
-    Rltv = b'Rltv'
-    ClrC = b'ClrC'
-    Ylws = b'Ylws'
-    Whts = b'Whts'
-    Ntrl = b'Ntrl'
-    Blks = b'Blks'
-    Bk   = b'Bk  '
-    MtnB = b'MtnB'
-    RdlB = b'RdlB'
-    BlrM = b'BlrM'
-    Zm   = b'Zm  '
-    BlrQ = b'BlrQ'
-    Gd   = b'Gd  '
-    GEfc = b'GEfc'
-    GEfs = b'GEfs'
-    GEfk = b'GEfk'
-    GEft = b'GEft'
-    GELv = b'GELv'
-    GraP = b'GraP'
-    StrL = b'StrL'
-    LgDr = b'LgDr'
-    SDir = b'SDir'
-    StrD = b'StrD'
-    SDRD = b'SDRD'
-    PltK = b'PltK'
-    StrS = b'StrS'
-    StDt = b'StDt'
-    Engn = b'Engn'
-
-
 class EffectOSType(Enum):
     """
     OS Type keys for Layer Effects.
@@ -1086,50 +458,3 @@ class SheetColorType(IntEnum):
     BLUE = 5
     VIOLET = 6
     GRAY = 7
-
-
-# class DisplayResolutionUnit(Enum):
-#     PIXELS_PER_INCH = 1
-#     PIXELS_PER_CM = 2
-
-
-# class DimensionUnit(Enum):
-#     INCH = 1
-#     CM = 2
-#     POINT = 3  # 72 points == 1 inch
-#     PICA = 4   # 6 pica == 1 inch
-#     COLUMN = 5
-
-
-# class PlacedLayerProperty(Enum):
-#     TRANSFORM = b'Trnf'
-#     SIZE = b'Sz  '
-#     ID = b'Idnt'
-
-
-# class TextProperty(Enum):
-#     TXT = b'Txt '
-#     ORIENTATION = b'Ornt'
-
-
-# class TextOrientation(Enum):
-#     HORIZONTAL = b'Hrzn'
-
-
-# class ObjectBasedEffects(Enum):
-#     """Type of the object-based effects."""
-#     DROP_SHADOW_MULTI = b'dropShadowMulti'
-#     DROP_SHADOW = b'DrSh'
-#     INNER_SHADOW_MULTI = b'innerShadowMulti'
-#     INNER_SHADOW = b'IrSh'
-#     OUTER_GLOW = b'OrGl'
-#     COLOR_OVERLAY_MULTI = b'solidFillMulti'
-#     COLOR_OVERLAY = b'SoFi'
-#     GRADIENT_OVERLAY_MULTI = b'gradientFillMulti'
-#     GRADIENT_OVERLAY = b'GrFl'
-#     PATTERN_OVERLAY = b'patternFill'
-#     STROKE_MULTI = b'frameFXMulti'
-#     STROKE = b'FrFX'
-#     INNER_GLOW = b'IrGl'
-#     BEVEL_EMBOSS = b'ebbl'
-#     SATIN = b'ChFX'
diff --git a/src/psd_tools/psd/descriptor.py b/src/psd_tools/psd/descriptor.py
index f24d86c..17eeac9 100644
--- a/src/psd_tools/psd/descriptor.py
+++ b/src/psd_tools/psd/descriptor.py
@@ -3,12 +3,12 @@ Descriptor data structure.
 
 Descriptors are basic data structure used throughout PSD files. Descriptor is
 one kind of serialization protocol for data objects, and
-:py:class:`~psd_tools.constants.DescriptorClassID` indicates what kind of
-descriptor it is.
+enum classes in :py:mod:`psd_tools.terminology` or bytes indicates what kind
+of descriptor it is.
 
-:py:class:`~psd_tools.constants.DescriptorClassID` can be pre-defined enum if
-the tag is 4-byte length or plain bytes if the length is arbitrary. It seems
-this depends on the internal of Adobe Photoshop but it is unknown.
+The class ID can be pre-defined enum if the tag is 4-byte length or plain
+bytes if the length is arbitrary. They depend on the internal version of
+Adobe Photoshop but the detail is unknown.
 
 Pretty printing is the best approach to check the descriptor content::
 
@@ -25,7 +25,8 @@ from psd_tools.psd.base import (
     BaseElement, BooleanElement, DictElement, IntegerElement, ListElement,
     NumericElement, StringElement,
 )
-from psd_tools.constants import OSType, UnitFloatType, DescriptorClassID
+from psd_tools.constants import OSType
+from psd_tools.terminology import Klass, Enum, Event, Form, Key, Type, Unit
 from psd_tools.validators import in_
 from psd_tools.utils import (
     read_fmt, write_fmt, read_unicode_string, write_unicode_string,
@@ -38,48 +39,41 @@ logger = logging.getLogger(__name__)
 
 TYPES, register = new_registry(attribute='ostype')
 
-_UNKNOWN_CLASS_ID = set()
+_TERMS = set(
+    item.value for kls in (Klass, Enum, Event, Form, Key, Type, Unit)
+    for item in kls
+)
 
 
 def read_length_and_key(fp):
     """
-    Helper to write descriptor classID and key.
+    Helper to read descriptor key.
     """
     length = read_fmt('I', fp)[0]
     key = fp.read(length or 4)
-    if length == 0:
-        try:
-            return DescriptorClassID(key)
-        except ValueError:
-            if key == b'\x00\x00\x00\x00':
-                raise
-            message = ('Unknown classID: %r' % (key))
-            warn(message)
-            logger.debug(message)
-            _UNKNOWN_CLASS_ID.add(key)
-
-    return key  # Fallback.
+    if length == 0 and key not in _TERMS:
+        logger.debug('Unknown term: %r' % (key))
+        _TERMS.add(key)
+    return key
 
 
 def write_length_and_key(fp, value):
     """
-    Helper to write descriptor classID and key.
-    """
-    if isinstance(value, DescriptorClassID):
-        length = (len(value.value) != 4) * len(value.value)
-        written = write_fmt(fp, 'I', length)
-        written += write_bytes(fp, value.value)
-    elif value in _UNKNOWN_CLASS_ID:
-        written = write_fmt(fp, 'I', 0)
-        written += write_bytes(fp, value)
-    else:
-        written = write_fmt(fp, 'I', len(value))
-        written += write_bytes(fp, value)
+    Helper to write descriptor key.
+    """
+    written = write_fmt(fp, 'I', 0 if value in _TERMS else len(value))
+    written += write_bytes(fp, value)
     return written
 
 
 class _DescriptorMixin(DictElement):
-    enum = DescriptorClassID
+    @classmethod
+    def _key_converter(cls, key):
+        if hasattr(key, 'value'):
+            return key.value
+        elif hasattr(key, 'encode'):
+            return key.encode('ascii')
+        return key
 
     @classmethod
     def _read_body(cls, fp):
@@ -129,22 +123,6 @@ class _DescriptorMixin(DictElement):
                     p.pretty(value)
             p.breakable('')
 
-    @classmethod
-    def _convert_enum(cls, enum, key):
-        """Descriptor class ID is case-sensitive for now."""
-        if isinstance(key, enum):
-            return key
-        key = key.encode('ascii') if hasattr(key, 'encode') else key
-        key_str = key.decode('ascii')
-        if isinstance(key, bytes) and hasattr(enum, key_str):
-            key = getattr(enum, key_str)
-        else:
-            try:
-                key = enum(key)
-            except ValueError:
-                pass
-        return key
-
 
 @register(OSType.DESCRIPTOR)
 @attr.s
@@ -152,8 +130,16 @@ class Descriptor(_DescriptorMixin):
     """
     Dict-like descriptor structure.
 
+    Key values can be 4-character `bytes` in
+    :py:class:`~psd_tools.terminology.Key` or arbitrary length `bytes`.
+    Supports direct access by :py:class:`~psd_tools.terminology.Key`.
+
     Example::
 
+        from psd_tools.terminology import Key
+
+        descriptor[Key.Enabled]
+
         for key in descriptor:
             print(descriptor[key])
 
@@ -163,10 +149,10 @@ class Descriptor(_DescriptorMixin):
 
     .. py:attribute:: classID
 
-        :py:class:`psd_tools.constants.DescriptorClassID` enum
+        bytes in :py:class:`~psd_tools.terminology.Klass`
     """
     name = attr.ib(default='', type=str)
-    classID = attr.ib(default=DescriptorClassID.NULL)
+    classID = attr.ib(default=Klass.Null.value)
 
     @classmethod
     def read(cls, fp):
@@ -193,11 +179,11 @@ class ObjectArray(_DescriptorMixin):
 
     .. py:attribute:: classID
 
-        :py:class:`DescriptorClassID`
+        bytes in :py:class:`~psd_tools.terminology.Klass`
     """
     items_count = attr.ib(default=0, type=int)
     name = attr.ib(default='', type=str)
-    classID = attr.ib(default=DescriptorClassID.NULL)
+    classID = attr.ib(default=Klass.Null.value)
 
     @classmethod
     def read(cls, fp):
@@ -252,11 +238,11 @@ class Property(BaseElement):
 
     .. py:attribute:: classID
 
-        :py:class:`DescriptorClassID`
+        bytes in :py:class:`~psd_tools.terminology.Klass`
 
     .. py:attribute:: keyID
 
-        :py:class:`DescriptorClassID`
+        bytes in :py:class:`~psd_tools.terminology.Key`
     """
     name = attr.ib(default='', type=str)
     classID = attr.ib(default=b'\x00\x00\x00\x00', type=bytes)
@@ -284,20 +270,19 @@ class UnitFloat(NumericElement):
 
     .. py:attribute:: unit
 
-        unit of the value in :py:class:`UnitFloatType`
+        unit of the value in :py:class:`Unit`
 
     .. py:attribute:: value
 
         `float` value
     """
     value = attr.ib(default=0.0, type=float)
-    unit = attr.ib(default=UnitFloatType.NONE, converter=UnitFloatType,
-                   validator=in_(UnitFloatType))
+    unit = attr.ib(default=Unit._None, converter=Unit, validator=in_(Unit))
 
     @classmethod
     def read(cls, fp):
         unit, value = read_fmt('4sd', fp)
-        return cls(unit=UnitFloatType(unit), value=value)
+        return cls(unit=Unit(unit), value=value)
 
     def write(self, fp):
         return write_fmt(fp, '4sd', self.unit.value, self.value)
@@ -318,14 +303,13 @@ class UnitFloats(BaseElement):
 
     .. py:attribute:: unit
 
-        unit of the value in :py:class:`UnitFloatType`
+        unit of the value in :py:class:`Unit`
 
     .. py:attribute:: values
 
         List of `float` values
     """
-    unit = attr.ib(default=UnitFloatType.NONE, converter=UnitFloatType,
-                   validator=in_(UnitFloatType))
+    unit = attr.ib(default=Unit._None, converter=Unit, validator=in_(Unit))
     values = attr.ib(factory=list)
 
     @classmethod
@@ -377,7 +361,7 @@ class Class(BaseElement):
 
     .. py:attribute:: classID
 
-        :py:class:`DescriptorClassID`
+        bytes in :py:class:`~psd_tools.terminology.Klass`
     """
     name = attr.ib(default='', type=str)
     classID = attr.ib(default=b'\x00\x00\x00\x00', type=bytes)
@@ -418,15 +402,15 @@ class EnumeratedReference(BaseElement):
 
     .. py:attribute:: classID
 
-        :py:class:`DescriptorClassID`
+        bytes in :py:class:`~psd_tools.terminology.Klass`
 
     .. py:attribute:: typeID
 
-        :py:class:`DescriptorClassID`
+        bytes in :py:class:`~psd_tools.terminology.Type`
 
     .. py:attribute:: enum
 
-        :py:class:`DescriptorClassID`
+        bytes in :py:class:`~psd_tools.terminology.Enum`
     """
     name = attr.ib(default='', type=str)
     classID = attr.ib(default=b'\x00\x00\x00\x00', type=bytes)
@@ -461,7 +445,7 @@ class Offset(BaseElement):
 
     .. py:attribute:: classID
 
-        :py:class:`DescriptorClassID`
+        bytes in :py:class:`~psd_tools.terminology.Klass`
 
     .. py:attribute:: value
 
@@ -538,17 +522,17 @@ class Integer(IntegerElement):
 
 @register(OSType.ENUMERATED)
 @attr.s
-class Enum(BaseElement):
+class Enumerated(BaseElement):
     """
     Enum structure.
 
     .. py:attribute:: typeID
 
-        :py:class:`DescriptorClassID`
+        bytes in :py:class:`~psd_tools.terminology.Type`
 
     .. py:attribute:: enum
 
-        :py:class:`DescriptorClassID`
+        bytes in :py:class:`~psd_tools.terminology.Enum`
     """
     typeID = attr.ib(default=b'\x00\x00\x00\x00', type=bytes)
     enum = attr.ib(default=b'\x00\x00\x00\x00', type=bytes)
@@ -573,6 +557,15 @@ class Enum(BaseElement):
         p.pretty(getattr(self.enum, 'name', self.enum))
         p.text(')')
 
+    def get_name(self):
+        """Get enum name."""
+        if len(self.enum) == 4:
+            try:
+                return Enum(self.enum).name
+            except ValueError:
+                pass
+        return str(self.enum)
+
 
 @register(OSType.RAW_DATA)
 @attr.s
@@ -686,9 +679,15 @@ class Name(BaseElement):
 
     .. py:attribute:: name
 
+        str
+
     .. py:attribute:: classID
 
+        bytes in :py:class:`~psd_tools.terminology.Klass`
+
     .. py:attribute:: value
+
+        str
     """
     name = attr.ib(default='', type=str)
     classID = attr.ib(default=b'\x00\x00\x00\x00', type=bytes)
diff --git a/src/psd_tools/psd/engine_data.py b/src/psd_tools/psd/engine_data.py
index cae31ec..1fecd7c 100644
--- a/src/psd_tools/psd/engine_data.py
+++ b/src/psd_tools/psd/engine_data.py
@@ -85,33 +85,36 @@ class Tokenizer(object):
 
     def __init__(self, data):
         self.data = data
+        self.index = 0
 
     def __iter__(self):
         return self
 
     def __len__(self):
-        return len(self.data)
+        return len(self.data) - self.index
 
     def next(self):
         return self.__next__()
 
     def __next__(self):
-        if len(self.data) == 0:
+        if len(self) == 0:
             raise StopIteration
 
-        if self.data.startswith(self.UTF16_START):
-            match = self.UTF16_END.search(self.data)
+        index = self.index
+        if self.data[index:].startswith(self.UTF16_START):
+            match = self.UTF16_END.search(self.data[index:])
             if match is None:
-                raise ValueError('Invalid token: %r' % (self.data))
-            token = self.data[:match.end()]
-            self.data = self.data[match.end() + 1:]
+                raise ValueError('Invalid token: %r' % (self.data[index:]))
+            token = self.data[index:index + match.end()]
+            self.index += match.end()
         else:
-            match = self.DIVIDER.search(self.data)
+            match = self.DIVIDER.search(self.data[index:])
             if match is None:
-                token, self.data = self.data, b''
+                token = self.data[index:]
+                self.index = len(self.data)
             else:
-                token = self.data[:match.start()]
-                self.data = self.data[match.end():]
+                token = self.data[index:index + match.start()]
+                self.index += match.end()
                 if token == b'':
                     return self.__next__()
 
diff --git a/src/psd_tools/psd/layer_and_mask.py b/src/psd_tools/psd/layer_and_mask.py
index 672ebf3..e1739b8 100644
--- a/src/psd_tools/psd/layer_and_mask.py
+++ b/src/psd_tools/psd/layer_and_mask.py
@@ -47,26 +47,31 @@ class LayerAndMaskInformation(BaseElement):
     @classmethod
     def read(cls, fp, encoding='macroman', version=1):
         start_pos = fp.tell()
-        data = read_length_block(fp, fmt=('I', 'Q')[version - 1])
+        length = read_fmt(('I', 'Q')[version - 1], fp)[0]
+        end_pos = fp.tell() + length
         logger.debug('reading layer and mask info, len=%d, offset=%d' % (
-            len(data), start_pos
+            length, start_pos
         ))
-        if len(data) == 0:
-            return cls()
-        with io.BytesIO(data) as f:
-            return cls._read_body(f, encoding, version)
+        if length == 0:
+            self = cls()
+        else:
+            self = cls._read_body(fp, end_pos, encoding, version)
+        assert fp.tell() <= end_pos
+        fp.seek(end_pos, 0)
+        return self
 
     @classmethod
-    def _read_body(cls, fp, encoding, version):
+    def _read_body(cls, fp, end_pos, encoding, version):
         layer_info = LayerInfo.read(fp, encoding, version)
         global_layer_mask_info = None
-        if is_readable(fp):
+        if is_readable(fp) and fp.tell() < end_pos:
             global_layer_mask_info = GlobalLayerMaskInfo.read(fp)
 
         tagged_blocks = None
         if is_readable(fp):
             # For some reason, global tagged blocks aligns 4 byte
-            tagged_blocks = TaggedBlocks.read(fp, version=version, padding=4)
+            tagged_blocks = TaggedBlocks.read(fp, version=version, padding=4,
+                                              end_pos=end_pos)
 
         return cls(layer_info, global_layer_mask_info, tagged_blocks)
 
@@ -116,13 +121,16 @@ class LayerInfo(BaseElement):
 
     @classmethod
     def read(cls, fp, encoding='macroman', version=1):
-        data = read_length_block(fp, fmt=('I', 'Q')[version - 1])
-        logger.debug('reading layer info, len=%d' % (len(data)))
-        if len(data) == 0:
-            return LayerInfo()
-
-        with io.BytesIO(data) as f:
-            return cls._read_body(f, encoding, version)
+        length = read_fmt(('I', 'Q')[version - 1], fp)[0]
+        logger.debug('reading layer info, len=%d' % length)
+        end_pos = fp.tell() + length
+        if length == 0:
+            self = LayerInfo()
+        else:
+            self = cls._read_body(fp, encoding, version)
+        assert fp.tell() <= end_pos
+        fp.seek(end_pos, 0)
+        return self
 
     @classmethod
     def _read_body(cls, fp, encoding, version):
@@ -803,14 +811,10 @@ class ChannelDataList(ListElement):
     See :py:class:`.ChannelData`.
     """
     @classmethod
-    def read(cls, fp, channel_info):
+    def read(cls, fp, channel_info, **kwargs):
         items = []
         for c in channel_info:
-            data = fp.read(c.length)
-            assert len(data) == c.length, '(%d, %d)' % (len(data), c.length)
-            # Seems no padding here.
-            with io.BytesIO(data) as f:
-                items.append(ChannelData.read(f))
+            items.append(ChannelData.read(fp, c.length - 2, **kwargs))
         return cls(items)
 
     @property
@@ -837,9 +841,9 @@ class ChannelData(BaseElement):
     data = attr.ib(default=b'', type=bytes, repr=False)
 
     @classmethod
-    def read(cls, fp):
+    def read(cls, fp, length=0, **kwargs):
         compression = Compression(read_fmt('H', fp)[0])
-        data = fp.read()
+        data = fp.read(length)
         return cls(compression, data)
 
     def write(self, fp, **kwargs):
@@ -911,7 +915,7 @@ class GlobalLayerMaskInfo(BaseElement):
 
     @classmethod
     def read(cls, fp):
-        data = read_length_block(fp)
+        data = read_length_block(fp)  # fmt?
         logger.debug('reading global layer mask info, len=%d' % (len(data)))
         if len(data) == 0:
             return cls(overlay_color=None)
diff --git a/src/psd_tools/psd/tagged_blocks.py b/src/psd_tools/psd/tagged_blocks.py
index fa5c479..cf98553 100644
--- a/src/psd_tools/psd/tagged_blocks.py
+++ b/src/psd_tools/psd/tagged_blocks.py
@@ -145,9 +145,11 @@ class TaggedBlocks(DictElement):
         self[key] = TaggedBlock(key=key, data=kls(*args, **kwargs))
 
     @classmethod
-    def read(cls, fp, version=1, padding=1):
+    def read(cls, fp, version=1, padding=1, end_pos=None):
         items = []
         while is_readable(fp, 8):  # len(signature) + len(key) = 8
+            if end_pos is not None and fp.tell() >= end_pos:
+                break
             block = TaggedBlock.read(fp, version, padding)
             if block is None:
                 break
diff --git a/src/psd_tools/terminology.py b/src/psd_tools/terminology.py
new file mode 100644
index 0000000..7b97272
--- /dev/null
+++ b/src/psd_tools/terminology.py
@@ -0,0 +1,2078 @@
+"""
+Constants for descriptor.
+
+This file is automaticaly generated by tools/extract_terminology.py
+"""
+from enum import Enum as _Enum
+
+
+class Klass(_Enum):
+    """
+    Klass definitions extracted from PITerminology.h.
+
+    See https://www.adobe.com/devnet/photoshop/sdk.html
+    """
+    Action = b'Actn'
+    ActionSet = b'ASet'
+    Adjustment = b'Adjs'
+    AdjustmentLayer = b'AdjL'
+    AirbrushTool = b'AbTl'
+    AlphaChannelOptions = b'AChl'
+    AntiAliasedPICTAcquire = b'AntA'
+    Application = b'capp'
+    Arrowhead = b'cArw'
+    Assert = b'Asrt'
+    AssumedProfile = b'AssP'
+    BMPFormat = b'BMPF'
+    BackgroundLayer = b'BckL'
+    BevelEmboss = b'ebbl'
+    BitmapMode = b'BtmM'
+    BlendRange = b'Blnd'
+    BlurTool = b'BlTl'
+    BookColor = b'BkCl'
+    BrightnessContrast = b'BrgC'
+    Brush = b'Brsh'
+    BurnInTool = b'BrTl'
+    CachePrefs = b'CchP'
+    CMYKColor = b'CMYC'
+    CMYKColorMode = b'CMYM'
+    CMYKSetup = b'CMYS'
+    Calculation = b'Clcl'
+    Channel = b'Chnl'
+    ChannelMatrix = b'ChMx'
+    ChannelMixer = b'ChnM'
+    CineonFormat = b'SDPX'
+    ClippingInfo = b'Clpo'
+    ClippingPath = b'ClpP'
+    CloneStampTool = b'ClTl'
+    Color = b'Clr '
+    ColorBalance = b'ClrB'
+    ColorCorrection = b'ClrC'
+    ColorPickerPrefs = b'Clrk'
+    ColorSampler = b'ClSm'
+    ColorStop = b'Clrt'
+    Command = b'Cmnd'
+    Curves = b'Crvs'
+    CurvePoint = b'CrPt'
+    CustomPalette = b'Cstl'
+    CurvesAdjustment = b'CrvA'
+    CustomPhosphors = b'CstP'
+    CustomWhitePoint = b'CstW'
+    DicomFormat = b'Dicm'
+    DisplayPrefs = b'DspP'
+    Document = b'Dcmn'
+    DodgeTool = b'DdTl'
+    DropShadow = b'DrSh'
+    DuotoneInk = b'DtnI'
+    DuotoneMode = b'DtnM'
+    EPSGenericFormat = b'EPSG'
+    EPSPICTPreview = b'EPSC'
+    EPSTIFFPreview = b'EPST'
+    Element = b'Elmn'
+    Ellipse = b'Elps'
+    EraserTool = b'ErTl'
+    Export = b'Expr'
+    FileInfo = b'FlIn'
+    FileSavePrefs = b'FlSv'
+    FlashPixFormat = b'FlsP'
+    FontDesignAxes = b'FntD'
+    Format = b'Fmt '
+    FrameFX = b'FrFX'
+    Contour = b'FxSc'
+    GeneralPrefs = b'GnrP'
+    GIF89aExport = b'GF89'
+    GIFFormat = b'GFFr'
+    GlobalAngle = b'gblA'
+    Gradient = b'Grdn'
+    GradientFill = b'Grdf'
+    GradientMap = b'GdMp'
+    GradientTool = b'GrTl'
+    GraySetup = b'GrSt'
+    Grayscale = b'Grsc'
+    GrayscaleMode = b'Grys'
+    Guide = b'Gd  '
+    GuidesPrefs = b'GdPr'
+    HalftoneScreen = b'HlfS'
+    HalftoneSpec = b'Hlfp'
+    HSBColor = b'HSBC'
+    HSBColorMode = b'HSBM'
+    HistoryBrushTool = b'HBTl'
+    HistoryPrefs = b'CHsP'
+    HistoryState = b'HstS'
+    HueSatAdjustment = b'HStA'
+    HueSatAdjustmentV2 = b'Hst2'
+    HueSaturation = b'HStr'
+    IFFFormat = b'IFFF'
+    IllustratorPathsExport = b'IlsP'
+    ImagePoint = b'ImgP'
+    Import = b'Impr'
+    IndexedColorMode = b'IndC'
+    InkTransfer = b'InkT'
+    InnerGlow = b'IrGl'
+    InnerShadow = b'IrSh'
+    InterfaceColor = b'IClr'
+    Invert = b'Invr'
+    JPEGFormat = b'JPEG'
+    LabColor = b'LbCl'
+    LabColorMode = b'LbCM'
+    Layer = b'Lyr '
+    LayerEffects = b'Lefx'
+    LayerFXVisible = b'lfxv'
+    Levels = b'Lvls'
+    LevelsAdjustment = b'LvlA'
+    LightSource = b'LghS'
+    Line = b'Ln  '
+    MacPaintFormat = b'McPn'
+    MagicEraserTool = b'MgEr'
+    MagicPoint = b'Mgcp'
+    Mask = b'Msk '
+    MenuItem = b'Mn  '
+    Mode = b'Md  '
+    MultichannelMode = b'MltC'
+    ObsoleteTextLayer = b'TxLy'
+    Null = b'null'
+    Offset = b'Ofst'
+    Opacity = b'Opac'
+    OuterGlow = b'OrGl'
+    PDFGenericFormat = b'PDFG'
+    PICTFileFormat = b'PICF'
+    PICTResourceFormat = b'PICR'
+    PNGFormat = b'PNGF'
+    PageSetup = b'PgSt'
+    PaintbrushTool = b'PbTl'
+    Path = b'Path'
+    PathComponent = b'PaCm'
+    PathPoint = b'Pthp'
+    Pattern = b'PttR'
+    PatternStampTool = b'PaTl'
+    PencilTool = b'PcTl'
+    Photoshop20Format = b'Pht2'
+    Photoshop35Format = b'Pht3'
+    PhotoshopDCS2Format = b'PhD2'
+    PhotoshopDCSFormat = b'PhD1'
+    PhotoshopEPSFormat = b'PhtE'
+    PhotoshopPDFFormat = b'PhtP'
+    Pixel = b'Pxel'
+    PixelPaintFormat = b'PxlP'
+    PluginPrefs = b'PlgP'
+    Point = b'Pnt '
+    Point16 = b'Pnt1'
+    Polygon = b'Plgn'
+    Posterize = b'Pstr'
+    Preferences = b'GnrP'
+    ProfileSetup = b'PrfS'
+    Property = b'Prpr'
+    Range = b'Rang'
+    Rect16 = b'Rct1'
+    RGBColor = b'RGBC'
+    RGBColorMode = b'RGBM'
+    RGBSetup = b'RGBt'
+    RawFormat = b'Rw  '
+    Rectangle = b'Rctn'
+    SaturationTool = b'SrTl'
+    ScitexCTFormat = b'Sctx'
+    Selection = b'csel'
+    SelectiveColor = b'SlcC'
+    ShapingCurve = b'ShpC'
+    SharpenTool = b'ShTl'
+    SingleColumn = b'Sngc'
+    SingleRow = b'Sngr'
+    BackgroundEraserTool = b'SETl'
+    SolidFill = b'SoFi'
+    ArtHistoryBrushTool = b'ABTl'
+    SmudgeTool = b'SmTl'
+    Snapshot = b'SnpS'
+    SpotColorChannel = b'SCch'
+    Style = b'StyC'
+    SubPath = b'Sbpl'
+    TIFFFormat = b'TIFF'
+    TargaFormat = b'TrgF'
+    TextLayer = b'TxLr'
+    TextStyle = b'TxtS'
+    TextStyleRange = b'Txtt'
+    Threshold = b'Thrs'
+    Tool = b'Tool'
+    TransferSpec = b'Trfp'
+    TransferPoint = b'DtnP'
+    TransparencyPrefs = b'TrnP'
+    TransparencyStop = b'TrnS'
+    UnitsPrefs = b'UntP'
+    UnspecifiedColor = b'UnsC'
+    Version = b'Vrsn'
+    WebdavPrefs = b'Wdbv'
+    XYYColor = b'XYYC'
+    ChromeFX = b'ChFX'
+    BackLight = b'BakL'
+    FillFlash = b'FilF'
+    ColorCast = b'ColC'
+
+
+class Enum(_Enum):
+    """
+    Enum definitions extracted from PITerminology.h.
+
+    See https://www.adobe.com/devnet/photoshop/sdk.html
+    """
+    Add = b'Add '
+    AmountHigh = b'amHi'
+    AmountLow = b'amLo'
+    AmountMedium = b'amMd'
+    AntiAliasNone = b'Anno'
+    AntiAliasLow = b'AnLo'
+    AntiAliasMedium = b'AnMd'
+    AntiAliasHigh = b'AnHi'
+    AntiAliasCrisp = b'AnCr'
+    AntiAliasStrong = b'AnSt'
+    AntiAliasSmooth = b'AnSm'
+    AppleRGB = b'AppR'
+    ASCII = b'ASCI'
+    AskWhenOpening = b'AskW'
+    Bicubic = b'Bcbc'
+    Binary = b'Bnry'
+    MonitorSetup = b'MntS'
+    _16BitsPerPixel = b'16Bt'
+    _1BitPerPixel = b'OnBt'
+    _2BitsPerPixel = b'2Bts'
+    _32BitsPerPixel = b'32Bt'
+    _4BitsPerPixel = b'4Bts'
+    _5000 = b'5000'
+    _5500 = b'5500'
+    _6500 = b'6500'
+    _72Color = b'72Cl'
+    _72Gray = b'72Gr'
+    _7500 = b'7500'
+    _8BitsPerPixel = b'EghB'
+    _9300 = b'9300'
+    A = b'A   '
+    AbsColorimetric = b'AClr'
+    ADSBottoms = b'AdBt'
+    ADSCentersH = b'AdCH'
+    ADSCentersV = b'AdCV'
+    ADSHorizontal = b'AdHr'
+    ADSLefts = b'AdLf'
+    ADSRights = b'AdRg'
+    ADSTops = b'AdTp'
+    ADSVertical = b'AdVr'
+    AboutApp = b'AbAp'
+    Absolute = b'Absl'
+    ActualPixels = b'ActP'
+    Adaptive = b'Adpt'
+    AdjustmentOptions = b'AdjO'
+    AirbrushEraser = b'Arbs'
+    All = b'Al  '
+    Amiga = b'Amga'
+    Angle = b'Angl'
+    Any = b'Any '
+    ApplyImage = b'AplI'
+    AroundCenter = b'ArnC'
+    Arrange = b'Arng'
+    Ask = b'Ask '
+    B = b'B   '
+    Back = b'Back'
+    Background = b'Bckg'
+    BackgroundColor = b'BckC'
+    Backward = b'Bckw'
+    Behind = b'Bhnd'
+    Best = b'Bst '
+    Better = b'Dthb'
+    Bilinear = b'Blnr'
+    BitDepth1 = b'BD1 '
+    BitDepth16 = b'BD16'
+    BitDepth24 = b'BD24'
+    BitDepth32 = b'BD32'
+    BitDepth4 = b'BD4 '
+    BitDepth8 = b'BD8 '
+    BitDepthA1R5G5B5 = b'1565'
+    BitDepthR5G6B5 = b'x565'
+    BitDepthX4R4G4B4 = b'x444'
+    BitDepthA4R4G4B4 = b'4444'
+    BitDepthX8R8G8B8 = b'x888'
+    Bitmap = b'Btmp'
+    Black = b'Blck'
+    BlackAndWhite = b'BanW'
+    BlackBody = b'BlcB'
+    Blacks = b'Blks'
+    BlockEraser = b'Blk '
+    Blast = b'Blst'
+    Blocks = b'Blks'
+    Blue = b'Bl  '
+    Blues = b'Bls '
+    Bottom = b'Bttm'
+    BrushDarkRough = b'BrDR'
+    BrushesAppend = b'BrsA'
+    BrushesDefine = b'BrsD'
+    BrushesDelete = b'Brsf'
+    BrushesLoad = b'Brsd'
+    BrushesNew = b'BrsN'
+    BrushesOptions = b'BrsO'
+    BrushesReset = b'BrsR'
+    BrushesSave = b'Brsv'
+    BrushLightRough = b'BrsL'
+    BrushSimple = b'BrSm'
+    BrushSize = b'BrsS'
+    BrushSparkle = b'BrSp'
+    BrushWideBlurry = b'BrbW'
+    BrushWideSharp = b'BrsW'
+    Builtin = b'Bltn'
+    BurnInH = b'BrnH'
+    BurnInM = b'BrnM'
+    BurnInS = b'BrnS'
+    ButtonMode = b'BtnM'
+    CIERGB = b'CRGB'
+    WidePhosphors = b'Wide'
+    WideGamutRGB = b'WRGB'
+    CMYK = b'CMYK'
+    CMYK64 = b'CMSF'
+    CMYKColor = b'ECMY'
+    Calculations = b'Clcl'
+    Cascade = b'Cscd'
+    Center = b'Cntr'
+    CenterGlow = b'SrcC'
+    CenteredFrame = b'CtrF'
+    ChannelOptions = b'ChnO'
+    ChannelsPaletteOptions = b'ChnP'
+    CheckerboardNone = b'ChcN'
+    CheckerboardSmall = b'ChcS'
+    CheckerboardMedium = b'ChcM'
+    CheckerboardLarge = b'ChcL'
+    Clear = b'Clar'
+    ClearGuides = b'ClrG'
+    Clipboard = b'Clpb'
+    ClippingPath = b'ClpP'
+    CloseAll = b'ClsA'
+    CoarseDots = b'CrsD'
+    Color = b'Clr '
+    ColorBurn = b'CBrn'
+    ColorDodge = b'CDdg'
+    ColorMatch = b'ClMt'
+    ColorNoise = b'ClNs'
+    Colorimetric = b'Clrm'
+    Composite = b'Cmps'
+    ConvertToCMYK = b'CnvC'
+    ConvertToGray = b'CnvG'
+    ConvertToLab = b'CnvL'
+    ConvertToRGB = b'CnvR'
+    CreateDuplicate = b'CrtD'
+    CreateInterpolation = b'CrtI'
+    Cross = b'Crs '
+    CurrentLayer = b'CrrL'
+    Custom = b'Cst '
+    CustomPattern = b'Cstm'
+    CustomStops = b'CstS'
+    Cyan = b'Cyn '
+    Cyans = b'Cyns'
+    Dark = b'Drk '
+    Darken = b'Drkn'
+    DarkenOnly = b'DrkO'
+    DashedLines = b'DshL'
+    Desaturate = b'Dstt'
+    Diamond = b'Dmnd'
+    Difference = b'Dfrn'
+    Diffusion = b'Dfsn'
+    DiffusionDither = b'DfnD'
+    DisplayCursorsPreferences = b'DspC'
+    Dissolve = b'Dslv'
+    Distort = b'Dstr'
+    DodgeH = b'DdgH'
+    DodgeM = b'DdgM'
+    DodgeS = b'DdgS'
+    Dots = b'Dts '
+    Draft = b'Drft'
+    Duotone = b'Dtn '
+    EBUITU = b'EBT '
+    EdgeGlow = b'SrcE'
+    EliminateEvenFields = b'ElmE'
+    EliminateOddFields = b'ElmO'
+    Ellipse = b'Elps'
+    Emboss = b'Embs'
+    Exact = b'Exct'
+    Exclusion = b'Xclu'
+    FPXCompressLossyJPEG = b'FxJP'
+    FPXCompressNone = b'FxNo'
+    Faster = b'Dthf'
+    File = b'Fle '
+    FileInfo = b'FlIn'
+    FillBack = b'FlBc'
+    FillFore = b'FlFr'
+    FillInverse = b'FlIn'
+    FillSame = b'FlSm'
+    FineDots = b'FnDt'
+    First = b'Frst'
+    FirstIdle = b'FrId'
+    FitOnScreen = b'FtOn'
+    ForegroundColor = b'FrgC'
+    Forward = b'Frwr'
+    FreeTransform = b'FrTr'
+    Front = b'Frnt'
+    FullDocument = b'FllD'
+    FullSize = b'FlSz'
+    GaussianDistribution = b'Gsn '
+    GIFColorFileColorTable = b'GFCT'
+    GIFColorFileColors = b'GFCF'
+    GIFColorFileMicrosoftPalette = b'GFMS'
+    GIFPaletteAdaptive = b'GFPA'
+    GIFPaletteExact = b'GFPE'
+    GIFPaletteOther = b'GFPO'
+    GIFPaletteSystem = b'GFPS'
+    GIFRequiredColorSpaceIndexed = b'GFCI'
+    GIFRequiredColorSpaceRGB = b'GFRG'
+    GIFRowOrderInterlaced = b'GFIN'
+    GIFRowOrderNormal = b'GFNI'
+    GeneralPreferences = b'GnrP'
+    Good = b'Gd  '
+    GradientFill = b'GrFl'
+    GrainClumped = b'GrnC'
+    GrainContrasty = b'GrCn'
+    GrainEnlarged = b'GrnE'
+    GrainHorizontal = b'GrnH'
+    GrainRegular = b'GrnR'
+    GrainSoft = b'GrSf'
+    GrainSpeckle = b'GrSp'
+    GrainSprinkles = b'GrSr'
+    GrainStippled = b'GrSt'
+    GrainVertical = b'GrnV'
+    GrainyDots = b'GrnD'
+    Graphics = b'Grp '
+    Gray = b'Gry '
+    Gray16 = b'GryX'
+    Gray18 = b'Gr18'
+    Gray22 = b'Gr22'
+    Gray50 = b'Gr50'
+    GrayScale = b'Gryc'
+    Grayscale = b'Grys'
+    Green = b'Grn '
+    Greens = b'Grns'
+    GuidesGridPreferences = b'GudG'
+    HDTV = b'HDTV'
+    HSBColor = b'HSBl'
+    HSLColor = b'HSLC'
+    HalftoneFile = b'HlfF'
+    HalftoneScreen = b'HlfS'
+    HardLight = b'HrdL'
+    Heavy = b'Hvy '
+    HideAll = b'HdAl'
+    HideSelection = b'HdSl'
+    High = b'High'
+    HighQuality = b'Hgh '
+    Highlights = b'Hghl'
+    Histogram = b'Hstg'
+    History = b'Hsty'
+    HistoryPaletteOptions = b'HstO'
+    HistoryPreferences = b'HstP'
+    Horizontal = b'Hrzn'
+    HorizontalOnly = b'HrzO'
+    Hue = b'H   '
+    IBMPC = b'IBMP'
+    ICC = b'ICC '
+    Icon = b'Icn '
+    IdleVM = b'IdVM'
+    Ignore = b'Ignr'
+    Image = b'Img '
+    ImageCachePreferences = b'ImgP'
+    IndexedColor = b'Indl'
+    InfoPaletteOptions = b'InfP'
+    InfoPaletteToggleSamplers = b'InfT'
+    InnerBevel = b'InrB'
+    InsetFrame = b'InsF'
+    Inside = b'Insd'
+    JPEG = b'JPEG'
+    JustifyAll = b'JstA'
+    JustifyFull = b'JstF'
+    KeepProfile = b'KPro'
+    KeyboardPreferences = b'KybP'
+    Lab = b'Lab '
+    Lab48 = b'LbCF'
+    LabColor = b'LbCl'
+    Large = b'Lrg '
+    Last = b'Lst '
+    LastFilter = b'LstF'
+    LayerOptions = b'LyrO'
+    LayersPaletteOptions = b'LyrP'
+    Left = b'Left'
+    Left_PLUGIN = b'Lft '
+    LevelBased = b'LvlB'
+    Light = b'Lgt '
+    LightBlue = b'LgtB'
+    LightDirBottom = b'LDBt'
+    LightDirBottomLeft = b'LDBL'
+    LightDirBottomRight = b'LDBR'
+    LightDirLeft = b'LDLf'
+    LightDirRight = b'LDRg'
+    LightDirTop = b'LDTp'
+    LightDirTopLeft = b'LDTL'
+    LightDirTopRight = b'LDTR'
+    LightGray = b'LgtG'
+    LightDirectional = b'LghD'
+    LightenOnly = b'LghO'
+    LightOmni = b'LghO'
+    LightPosBottom = b'LPBt'
+    LightPosBottomLeft = b'LPBL'
+    LightPosBottomRight = b'LPBr'
+    LightPosLeft = b'LPLf'
+    LightPosRight = b'LPRg'
+    LightPosTop = b'LPTp'
+    LightPosTopLeft = b'LPTL'
+    LightPosTopRight = b'LPTR'
+    LightRed = b'LgtR'
+    LightSpot = b'LghS'
+    Lighten = b'Lghn'
+    Lightness = b'Lght'
+    Line = b'Ln  '
+    Lines = b'Lns '
+    Linear = b'Lnr '
+    Linked = b'Lnkd'
+    LongLines = b'LngL'
+    LongStrokes = b'LngS'
+    Low = b'Low '
+    Lower = b'Lwr '
+    LowQuality = b'Lw  '
+    Luminosity = b'Lmns'
+    Maya = b'Maya'
+    MacThumbnail = b'McTh'
+    Macintosh = b'Mcnt'
+    MacintoshSystem = b'McnS'
+    Magenta = b'Mgnt'
+    Magentas = b'Mgnt'
+    Mask = b'Msk '
+    MaskedAreas = b'MskA'
+    MasterAdaptive = b'MAdp'
+    MasterPerceptual = b'MPer'
+    MasterSelective = b'MSel'
+    Maximum = b'Mxmm'
+    MaximumQuality = b'Mxm '
+    Medium = b'Mdim'
+    MediumBlue = b'MdmB'
+    MediumQuality = b'Mdm '
+    MediumDots = b'MdmD'
+    MediumLines = b'MdmL'
+    MediumStrokes = b'MdmS'
+    MemoryPreferences = b'MmrP'
+    MergeChannels = b'MrgC'
+    Merged = b'Mrgd'
+    MergedLayers = b'Mrg2'
+    MergedLayersOld = b'MrgL'
+    Middle = b'Mddl'
+    Midtones = b'Mdtn'
+    ModeGray = b'MdGr'
+    ModeRGB = b'MdRG'
+    Monitor = b'Moni'
+    Monotone = b'Mntn'
+    Multi72Color = b'72CM'
+    Multi72Gray = b'72GM'
+    Multichannel = b'Mlth'
+    MultiNoCompositePS = b'NCmM'
+    Multiply = b'Mltp'
+    NavigatorPaletteOptions = b'NvgP'
+    NearestNeighbor = b'Nrst'
+    NetscapeGray = b'NsGr'
+    Neutrals = b'Ntrl'
+    NewView = b'NwVw'
+    Next = b'Nxt '
+    Nikon = b'Nkn '
+    Nikon105 = b'Nkn1'
+    No = b'N   '
+    NoCompositePS = b'NCmp'
+    _None = b'None'
+    Normal = b'Nrml'
+    NormalPath = b'NrmP'
+    NTSC = b'NTSC'
+    Null = b'null'
+    OS2 = b'OS2 '
+    Off = b'Off '
+    On = b'On  '
+    OpenAs = b'OpAs'
+    Orange = b'Orng'
+    OutFromCenter = b'OtFr'
+    OutOfGamut = b'OtOf'
+    OuterBevel = b'OtrB'
+    Outside = b'Otsd'
+    OutsetFrame = b'OutF'
+    Overlay = b'Ovrl'
+    PaintbrushEraser = b'Pntb'
+    PencilEraser = b'Pncl'
+    P22EBU = b'P22B'
+    PNGFilterAdaptive = b'PGAd'
+    PNGFilterAverage = b'PGAv'
+    PNGFilterNone = b'PGNo'
+    PNGFilterPaeth = b'PGPt'
+    PNGFilterSub = b'PGSb'
+    PNGFilterUp = b'PGUp'
+    PNGInterlaceAdam7 = b'PGIA'
+    PNGInterlaceNone = b'PGIN'
+    PagePosCentered = b'PgPC'
+    PagePosTopLeft = b'PgTL'
+    PageSetup = b'PgSt'
+    PalSecam = b'PlSc'
+    PanaVision = b'PnVs'
+    PathsPaletteOptions = b'PthP'
+    Pattern = b'Ptrn'
+    PatternDither = b'PtnD'
+    Perceptual = b'Perc'
+    Perspective = b'Prsp'
+    PhotoshopPicker = b'Phtk'
+    PickCMYK = b'PckC'
+    PickGray = b'PckG'
+    PickHSB = b'PckH'
+    PickLab = b'PckL'
+    PickOptions = b'PckO'
+    PickRGB = b'PckR'
+    PillowEmboss = b'PlEb'
+    PixelPaintSize1 = b'PxS1'
+    PixelPaintSize2 = b'PxS2'
+    PixelPaintSize3 = b'PxS3'
+    PixelPaintSize4 = b'PxS4'
+    Place = b'Plce'
+    PlaybackOptions = b'PbkO'
+    PluginPicker = b'PlgP'
+    PluginsScratchDiskPreferences = b'PlgS'
+    PolarToRect = b'PlrR'
+    PondRipples = b'PndR'
+    Precise = b'Prc '
+    PreciseMatte = b'PrBL'
+    PreviewOff = b'PrvO'
+    PreviewCMYK = b'PrvC'
+    PreviewCyan = b'Prvy'
+    PreviewMagenta = b'PrvM'
+    PreviewYellow = b'PrvY'
+    PreviewBlack = b'PrvB'
+    PreviewCMY = b'PrvN'
+    Previous = b'Prvs'
+    Primaries = b'Prim'
+    PrintSize = b'PrnS'
+    PrintingInksSetup = b'PrnI'
+    Purple = b'Prp '
+    Pyramids = b'Pyrm'
+    QCSAverage = b'Qcsa'
+    QCSCorner0 = b'Qcs0'
+    QCSCorner1 = b'Qcs1'
+    QCSCorner2 = b'Qcs2'
+    QCSCorner3 = b'Qcs3'
+    QCSIndependent = b'Qcsi'
+    QCSSide0 = b'Qcs4'
+    QCSSide1 = b'Qcs5'
+    QCSSide2 = b'Qcs6'
+    QCSSide3 = b'Qcs7'
+    Quadtone = b'Qdtn'
+    QueryAlways = b'QurA'
+    QueryAsk = b'Qurl'
+    QueryNever = b'QurN'
+    Repeat = b'Rpt '
+    RGB = b'RGB '
+    RGB48 = b'RGBF'
+    RGBColor = b'RGBC'
+    Radial = b'Rdl '
+    Random = b'Rndm'
+    RectToPolar = b'RctP'
+    Red = b'Rd  '
+    RedrawComplete = b'RdCm'
+    Reds = b'Rds '
+    Reflected = b'Rflc'
+    Relative = b'Rltv'
+    RepeatEdgePixels = b'RptE'
+    RevealAll = b'RvlA'
+    RevealSelection = b'RvlS'
+    Revert = b'Rvrt'
+    Right = b'Rght'
+    Rotate = b'Rtte'
+    RotoscopingPreferences = b'RtsP'
+    Round = b'Rnd '
+    RulerCm = b'RrCm'
+    RulerInches = b'RrIn'
+    RulerPercent = b'RrPr'
+    RulerPicas = b'RrPi'
+    RulerPixels = b'RrPx'
+    RulerPoints = b'RrPt'
+    AdobeRGB1998 = b'SMPT'
+    SMPTEC = b'SMPC'
+    SRGB = b'SRGB'
+    Sample3x3 = b'Smp3'
+    Sample5x5 = b'Smp5'
+    SamplePoint = b'SmpP'
+    Saturate = b'Str '
+    Saturation = b'Strt'
+    Saved = b'Sved'
+    SaveForWeb = b'Svfw'
+    SavingFilesPreferences = b'SvnF'
+    Scale = b'Scl '
+    Screen = b'Scrn'
+    ScreenCircle = b'ScrC'
+    ScreenDot = b'ScrD'
+    ScreenLine = b'ScrL'
+    SelectedAreas = b'SlcA'
+    Selection = b'Slct'
+    Selective = b'Sele'
+    SeparationSetup = b'SprS'
+    SeparationTables = b'SprT'
+    Shadows = b'Shdw'
+    ContourLinear = b'sp01'
+    ContourGaussian = b'sp02'
+    ContourSingle = b'sp03'
+    ContourDouble = b'sp04'
+    ContourTriple = b'sp05'
+    ContourCustom = b'sp06'
+    ShortLines = b'ShrL'
+    ShortStrokes = b'ShSt'
+    Single72Color = b'72CS'
+    Single72Gray = b'72GS'
+    SingleNoCompositePS = b'NCmS'
+    Skew = b'Skew'
+    SlopeLimitMatte = b'Slmt'
+    Small = b'Sml '
+    SmartBlurModeEdgeOnly = b'SBME'
+    SmartBlurModeNormal = b'SBMN'
+    SmartBlurModeOverlayEdge = b'SBMO'
+    SmartBlurQualityHigh = b'SBQH'
+    SmartBlurQualityLow = b'SBQL'
+    SmartBlurQualityMedium = b'SBQM'
+    Snapshot = b'Snps'
+    SolidColor = b'SClr'
+    SoftLight = b'SftL'
+    SoftMatte = b'SfBL'
+    Spectrum = b'Spct'
+    Spin = b'Spn '
+    SpotColor = b'Spot'
+    Square = b'Sqr '
+    Stagger = b'Stgr'
+    StampIn = b'In  '
+    StampOut = b'Out '
+    Standard = b'Std '
+    StdA = b'StdA'
+    StdB = b'StdB'
+    StdC = b'StdC'
+    StdE = b'StdE'
+    StretchToFit = b'StrF'
+    StrokeDirHorizontal = b'SDHz'
+    StrokeDirLeftDiag = b'SDLD'
+    StrokeDirRightDiag = b'SDRD'
+    StrokeDirVertical = b'SDVt'
+    StylesAppend = b'SlsA'
+    StylesDelete = b'Slsf'
+    StylesLoad = b'Slsd'
+    StylesNew = b'SlsN'
+    StylesReset = b'SlsR'
+    StylesSave = b'Slsv'
+    Subtract = b'Sbtr'
+    SwatchesAppend = b'SwtA'
+    SwatchesReplace = b'Swtp'
+    SwatchesReset = b'SwtR'
+    SwatchesSave = b'SwtS'
+    SystemPicker = b'SysP'
+    Tables = b'Tbl '
+    Target = b'Trgt'
+    TargetPath = b'Trgp'
+    TexTypeBlocks = b'TxBl'
+    TexTypeBrick = b'TxBr'
+    TexTypeBurlap = b'TxBu'
+    TexTypeCanvas = b'TxCa'
+    TexTypeFrosted = b'TxFr'
+    TexTypeSandstone = b'TxSt'
+    TexTypeTinyLens = b'TxTL'
+    Threshold = b'Thrh'
+    Thumbnail = b'Thmb'
+    TIFF = b'TIFF'
+    Tile = b'Tile'
+    Tile_PLUGIN = b'Tl  '
+    ToggleActionsPalette = b'TglA'
+    ToggleBlackPreview = b'TgBP'
+    ToggleBrushesPalette = b'TglB'
+    ToggleCMYKPreview = b'TglC'
+    ToggleCMYPreview = b'TgCM'
+    ToggleChannelsPalette = b'Tglh'
+    ToggleColorPalette = b'Tglc'
+    ToggleCyanPreview = b'TgCP'
+    ToggleEdges = b'TglE'
+    ToggleGamutWarning = b'TglG'
+    ToggleGrid = b'TgGr'
+    ToggleGuides = b'Tgld'
+    ToggleHistoryPalette = b'TglH'
+    ToggleInfoPalette = b'TglI'
+    ToggleLayerMask = b'TglM'
+    ToggleLayersPalette = b'Tgly'
+    ToggleLockGuides = b'TglL'
+    ToggleMagentaPreview = b'TgMP'
+    ToggleNavigatorPalette = b'TglN'
+    ToggleOptionsPalette = b'TglO'
+    TogglePaths = b'TglP'
+    TogglePathsPalette = b'Tglt'
+    ToggleRGBMacPreview = b'TrMp'
+    ToggleRGBWindowsPreview = b'TrWp'
+    ToggleRGBUncompensatedPreview = b'TrUp'
+    ToggleRulers = b'TglR'
+    ToggleSnapToGrid = b'TgSn'
+    ToggleSnapToGuides = b'TglS'
+    ToggleStatusBar = b'Tgls'
+    ToggleStylesPalette = b'TgSl'
+    ToggleSwatchesPalette = b'Tglw'
+    ToggleToolsPalette = b'TglT'
+    ToggleYellowPreview = b'TgYP'
+    ToggleDocumentPalette = b'TgDc'
+    Top = b'Top '
+    Transparency = b'Trsp'
+    TransparencyGamutPreferences = b'TrnG'
+    Transparent = b'Trns'
+    Trinitron = b'Trnt'
+    Tritone = b'Trtn'
+    UIBitmap = b'UBtm'
+    UICMYK = b'UCMY'
+    UIDuotone = b'UDtn'
+    UIGrayscale = b'UGry'
+    UIIndexed = b'UInd'
+    UILab = b'ULab'
+    UIMultichannel = b'UMlt'
+    UIRGB = b'URGB'
+    Undo = b'Und '
+    Uniform = b'Unfm'
+    UniformDistribution = b'Unfr'
+    UnitsRulersPreferences = b'UntR'
+    Upper = b'Upr '
+    UserStop = b'UsrS'
+    VMPreferences = b'VMPr'
+    Vertical = b'Vrtc'
+    VerticalOnly = b'VrtO'
+    Violet = b'Vlt '
+    WaveSine = b'WvSn'
+    WaveSquare = b'WvSq'
+    WaveTriangle = b'WvTr'
+    Web = b'Web '
+    White = b'Wht '
+    Whites = b'Whts'
+    WinThumbnail = b'WnTh'
+    Wind = b'Wnd '
+    Windows = b'Win '
+    WindowsSystem = b'WndS'
+    Wrap = b'Wrp '
+    WrapAround = b'WrpA'
+    WorkPath = b'WrkP'
+    Yellow = b'Yllw'
+    YellowColor = b'Ylw '
+    Yellows = b'Ylws'
+    Yes = b'Ys  '
+    Zip = b'ZpEn'
+    Zoom = b'Zm  '
+    ZoomIn = b'ZmIn'
+    ZoomOut = b'ZmOt'
+
+
+class Event(_Enum):
+    """
+    Event definitions extracted from PITerminology.h.
+
+    See https://www.adobe.com/devnet/photoshop/sdk.html
+    """
+    _3DTransform = b'TdT '
+    Average = b'Avrg'
+    ApplyStyle = b'ASty'
+    Assert = b'Asrt'
+    AccentedEdges = b'AccE'
+    Add = b'Add '
+    AddNoise = b'AdNs'
+    AddTo = b'AddT'
+    Align = b'Algn'
+    All = b'All '
+    AngledStrokes = b'AngS'
+    ApplyImage = b'AppI'
+    BasRelief = b'BsRl'
+    Batch = b'Btch'
+    BatchFromDroplet = b'BtcF'
+    Blur = b'Blr '
+    BlurMore = b'BlrM'
+    Border = b'Brdr'
+    Brightness = b'BrgC'
+    CanvasSize = b'CnvS'
+    ChalkCharcoal = b'ChlC'
+    ChannelMixer = b'ChnM'
+    Charcoal = b'Chrc'
+    Chrome = b'Chrm'
+    Clear = b'Cler'
+    Close = b'Cls '
+    Clouds = b'Clds'
+    ColorBalance = b'ClrB'
+    ColorHalftone = b'ClrH'
+    ColorRange = b'ClrR'
+    ColoredPencil = b'ClrP'
+    ConteCrayon = b'CntC'
+    Contract = b'Cntc'
+    ConvertMode = b'CnvM'
+    Copy = b'copy'
+    CopyEffects = b'CpFX'
+    CopyMerged = b'CpyM'
+    CopyToLayer = b'CpTL'
+    Craquelure = b'Crql'
+    CreateDroplet = b'CrtD'
+    Crop = b'Crop'
+    Crosshatch = b'Crsh'
+    Crystallize = b'Crst'
+    Curves = b'Crvs'
+    Custom = b'Cstm'
+    Cut = b'cut '
+    CutToLayer = b'CtTL'
+    Cutout = b'Ct  '
+    DarkStrokes = b'DrkS'
+    DeInterlace = b'Dntr'
+    DefinePattern = b'DfnP'
+    Defringe = b'Dfrg'
+    Delete = b'Dlt '
+    Desaturate = b'Dstt'
+    Deselect = b'Dslc'
+    Despeckle = b'Dspc'
+    DifferenceClouds = b'DfrC'
+    Diffuse = b'Dfs '
+    DiffuseGlow = b'DfsG'
+    DisableLayerFX = b'dlfx'
+    Displace = b'Dspl'
+    Distribute = b'Dstr'
+    Draw = b'Draw'
+    DryBrush = b'DryB'
+    Duplicate = b'Dplc'
+    DustAndScratches = b'DstS'
+    Emboss = b'Embs'
+    Equalize = b'Eqlz'
+    Exchange = b'Exch'
+    Expand = b'Expn'
+    Export = b'Expr'
+    Extrude = b'Extr'
+    Facet = b'Fct '
+    Fade = b'Fade'
+    Feather = b'Fthr'
+    Fibers = b'Fbrs'
+    Fill = b'Fl  '
+    FilmGrain = b'FlmG'
+    Filter = b'Fltr'
+    FindEdges = b'FndE'
+    FlattenImage = b'FltI'
+    Flip = b'Flip'
+    Fragment = b'Frgm'
+    Fresco = b'Frsc'
+    GaussianBlur = b'GsnB'
+    Get = b'getd'
+    Glass = b'Gls '
+    GlowingEdges = b'GlwE'
+    Gradient = b'Grdn'
+    GradientMap = b'GrMp'
+    Grain = b'Grn '
+    GraphicPen = b'GraP'
+    Group = b'GrpL'
+    Grow = b'Grow'
+    HalftoneScreen = b'HlfS'
+    Hide = b'Hd  '
+    HighPass = b'HghP'
+    HSBHSL = b'HsbP'
+    HueSaturation = b'HStr'
+    ImageSize = b'ImgS'
+    Import = b'Impr'
+    InkOutlines = b'InkO'
+    Intersect = b'Intr'
+    IntersectWith = b'IntW'
+    Inverse = b'Invs'
+    Invert = b'Invr'
+    LensFlare = b'LnsF'
+    Levels = b'Lvls'
+    LightingEffects = b'LghE'
+    Link = b'Lnk '
+    Make = b'Mk  '
+    Maximum = b'Mxm '
+    Median = b'Mdn '
+    MergeLayers = b'Mrg2'
+    MergeLayersOld = b'MrgL'
+    MergeSpotChannel = b'MSpt'
+    MergeVisible = b'MrgV'
+    Mezzotint = b'Mztn'
+    Minimum = b'Mnm '
+    Mosaic = b'Msc '
+    Mosaic_PLUGIN = b'MscT'
+    MotionBlur = b'MtnB'
+    Move = b'move'
+    NTSCColors = b'NTSC'
+    NeonGlow = b'NGlw'
+    Next = b'Nxt '
+    NotePaper = b'NtPr'
+    Notify = b'Ntfy'
+    Null = b'null'
+    OceanRipple = b'OcnR'
+    Offset = b'Ofst'
+    Open = b'Opn '
+    PaintDaubs = b'PntD'
+    PaletteKnife = b'PltK'
+    Paste = b'past'
+    PasteEffects = b'PaFX'
+    PasteInto = b'PstI'
+    PasteOutside = b'PstO'
+    Patchwork = b'Ptch'
+    Photocopy = b'Phtc'
+    Pinch = b'Pnch'
+    Place = b'Plc '
+    Plaster = b'Plst'
+    PlasticWrap = b'PlsW'
+    Play = b'Ply '
+    Pointillize = b'Pntl'
+    Polar = b'Plr '
+    PosterEdges = b'PstE'
+    Posterize = b'Pstr'
+    Previous = b'Prvs'
+    Print = b'Prnt'
+    ProfileToProfile = b'PrfT'
+    Purge = b'Prge'
+    Quit = b'quit'
+    RadialBlur = b'RdlB'
+    Rasterize = b'Rstr'
+    RasterizeTypeSheet = b'RstT'
+    RemoveBlackMatte = b'RmvB'
+    RemoveLayerMask = b'RmvL'
+    RemoveWhiteMatte = b'RmvW'
+    Rename = b'Rnm '
+    ReplaceColor = b'RplC'
+    Reset = b'Rset'
+    Reticulation = b'Rtcl'
+    Revert = b'Rvrt'
+    Ripple = b'Rple'
+    Rotate = b'Rtte'
+    RoughPastels = b'RghP'
+    Save = b'save'
+    Select = b'slct'
+    SelectiveColor = b'SlcC'
+    Set = b'setd'
+    SharpenEdges = b'ShrE'
+    Sharpen = b'Shrp'
+    SharpenMore = b'ShrM'
+    Shear = b'Shr '
+    Show = b'Shw '
+    Similar = b'Smlr'
+    SmartBlur = b'SmrB'
+    Smooth = b'Smth'
+    SmudgeStick = b'SmdS'
+    Solarize = b'Slrz'
+    Spatter = b'Spt '
+    Spherize = b'Sphr'
+    SplitChannels = b'SplC'
+    Sponge = b'Spng'
+    SprayedStrokes = b'SprS'
+    StainedGlass = b'StnG'
+    Stamp = b'Stmp'
+    Stop = b'Stop'
+    Stroke = b'Strk'
+    Subtract = b'Sbtr'
+    SubtractFrom = b'SbtF'
+    Sumie = b'Smie'
+    TakeMergedSnapshot = b'TkMr'
+    TakeSnapshot = b'TkSn'
+    TextureFill = b'TxtF'
+    Texturizer = b'Txtz'
+    Threshold = b'Thrs'
+    Tiles = b'Tls '
+    TornEdges = b'TrnE'
+    TraceContour = b'TrcC'
+    Transform = b'Trnf'
+    Trap = b'Trap'
+    Twirl = b'Twrl'
+    Underpainting = b'Undr'
+    Undo = b'undo'
+    Ungroup = b'Ungr'
+    Unlink = b'Unlk'
+    UnsharpMask = b'UnsM'
+    Variations = b'Vrtn'
+    Wait = b'Wait'
+    WaterPaper = b'WtrP'
+    Watercolor = b'Wtrc'
+    Wave = b'Wave'
+    Wind = b'Wnd '
+    ZigZag = b'ZgZg'
+    BackLight = b'BacL'
+    ColorCast = b'ColE'
+    OpenUntitled = b'OpnU'
+
+
+class Form(_Enum):
+    """
+    Form definitions extracted from PITerminology.h.
+
+    See https://www.adobe.com/devnet/photoshop/sdk.html
+    """
+    Class = b'Clss'
+    Enumerated = b'Enmr'
+    Identifier = b'Idnt'
+    Index = b'indx'
+    Offset = b'rele'
+    Property = b'prop'
+
+
+class Key(_Enum):
+    """
+    Key definitions extracted from PITerminology.h.
+
+    See https://www.adobe.com/devnet/photoshop/sdk.html
+    """
+    _3DAntiAlias = b'Alis'
+    A = b'A   '
+    Adjustment = b'Adjs'
+    Aligned = b'Algd'
+    Alignment = b'Algn'
+    AllPS = b'All '
+    AllExcept = b'AllE'
+    AllToolOptions = b'AlTl'
+    AlphaChannelOptions = b'AChn'
+    AlphaChannels = b'AlpC'
+    AmbientBrightness = b'AmbB'
+    AmbientColor = b'AmbC'
+    Amount = b'Amnt'
+    AmplitudeMax = b'AmMx'
+    AmplitudeMin = b'AmMn'
+    Anchor = b'Anch'
+    Angle = b'Angl'
+    Angle1 = b'Ang1'
+    Angle2 = b'Ang2'
+    Angle3 = b'Ang3'
+    Angle4 = b'Ang4'
+    AntiAlias = b'AntA'
+    Append = b'Appe'
+    Apply = b'Aply'
+    Area = b'Ar  '
+    Arrowhead = b'Arrw'
+    As = b'As  '
+    AssetBin = b'Asst'
+    AssumedCMYK = b'AssC'
+    AssumedGray = b'AssG'
+    AssumedRGB = b'AssR'
+    At = b'At  '
+    Auto = b'Auto'
+    AutoContrast = b'AuCo'
+    AutoErase = b'Atrs'
+    AutoKern = b'AtKr'
+    AutoUpdate = b'AtUp'
+    ShowMenuColors = b'SwMC'
+    Axis = b'Axis'
+    B = b'B   '
+    Background = b'Bckg'
+    BackgroundColor = b'BckC'
+    BackgroundLevel = b'BckL'
+    Backward = b'Bwd '
+    Balance = b'Blnc'
+    BaselineShift = b'Bsln'
+    BeepWhenDone = b'BpWh'
+    BeginRamp = b'BgnR'
+    BeginSustain = b'BgnS'
+    BevelDirection = b'bvlD'
+    BevelEmboss = b'ebbl'
+    BevelStyle = b'bvlS'
+    BevelTechnique = b'bvlT'
+    BigNudgeH = b'BgNH'
+    BigNudgeV = b'BgNV'
+    BitDepth = b'BtDp'
+    Black = b'Blck'
+    BlackClip = b'BlcC'
+    BlackGeneration = b'Blcn'
+    BlackGenerationCurve = b'BlcG'
+    BlackIntensity = b'BlcI'
+    BlackLevel = b'BlcL'
+    BlackLimit = b'BlcL'
+    Bleed = b'Bld '
+    BlendRange = b'Blnd'
+    Blue = b'Bl  '
+    BlueBlackPoint = b'BlBl'
+    BlueGamma = b'BlGm'
+    BlueWhitePoint = b'BlWh'
+    BlueX = b'BlX '
+    BlueY = b'BlY '
+    Blur = b'blur'
+    BlurMethod = b'BlrM'
+    BlurQuality = b'BlrQ'
+    Book = b'Bk  '
+    BorderThickness = b'BrdT'
+    Bottom = b'Btom'
+    Brightness = b'Brgh'
+    BrushDetail = b'BrsD'
+    Brushes = b'Brsh'
+    BrushSize = b'BrsS'
+    BrushType = b'BrsT'
+    BumpAmplitude = b'BmpA'
+    BumpChannel = b'BmpC'
+    By = b'By  '
+    Byline = b'Byln'
+    BylineTitle = b'BylT'
+    ByteOrder = b'BytO'
+    CachePrefs = b'CchP'
+    ChokeMatte = b'Ckmt'
+    CloneSource = b'ClnS'
+    CMYKSetup = b'CMYS'
+    Calculation = b'Clcl'
+    CalibrationBars = b'Clbr'
+    Caption = b'Cptn'
+    CaptionWriter = b'CptW'
+    Category = b'Ctgr'
+    CellSize = b'ClSz'
+    Center = b'Cntr'
+    CenterCropMarks = b'CntC'
+    ChalkArea = b'ChlA'
+    Channel = b'Chnl'
+    ChannelMatrix = b'ChMx'
+    ChannelName = b'ChnN'
+    Channels = b'Chns'
+    ChannelsInterleaved = b'ChnI'
+    CharcoalAmount = b'ChAm'
+    CharcoalArea = b'ChrA'
+    ChromeFX = b'ChFX'
+    City = b'City'
+    ClearAmount = b'ClrA'
+    ClippingPath = b'ClPt'
+    ClippingPathEPS = b'ClpP'
+    ClippingPathFlatness = b'ClpF'
+    ClippingPathIndex = b'ClpI'
+    ClippingPathInfo = b'Clpg'
+    ClosedSubpath = b'Clsp'
+    Color = b'Clr '
+    ColorChannels = b'Clrh'
+    ColorCorrection = b'ClrC'
+    ColorIndicates = b'ClrI'
+    ColorManagement = b'ClMg'
+    ColorPickerPrefs = b'Clrr'
+    ColorTable = b'ClrT'
+    Colorize = b'Clrz'
+    Colors = b'Clrs'
+    ColorsList = b'ClrL'
+    ColorSpace = b'ClrS'
+    ColumnWidth = b'ClmW'
+    CommandKey = b'CmdK'
+    Compensation = b'Cmpn'
+    Compression = b'Cmpr'
+    Concavity = b'Cncv'
+    Condition = b'Cndt'
+    Constant = b'Cnst'
+    Constrain = b'Cnst'
+    ConstrainProportions = b'CnsP'
+    ConstructionFOV = b'Cfov'
+    Contiguous = b'Cntg'
+    Continue = b'Cntn'
+    Continuity = b'Cnty'
+    Contrast = b'Cntr'
+    Convert = b'Cnvr'
+    Copy = b'Cpy '
+    Copyright = b'Cpyr'
+    CopyrightNotice = b'CprN'
+    CornerCropMarks = b'CrnC'
+    Count = b'Cnt '
+    CountryName = b'CntN'
+    CrackBrightness = b'CrcB'
+    CrackDepth = b'CrcD'
+    CrackSpacing = b'CrcS'
+    CreateLayersFromLayerFX = b'blfl'
+    Credit = b'Crdt'
+    Crossover = b'Crss'
+    Current = b'Crnt'
+    CurrentHistoryState = b'CrnH'
+    CurrentLight = b'CrnL'
+    CurrentToolOptions = b'CrnT'
+    Curve = b'Crv '
+    CurveFile = b'CrvF'
+    Custom = b'Cstm'
+    CustomForced = b'CstF'
+    CustomMatte = b'CstM'
+    CustomPalette = b'CstP'
+    Cyan = b'Cyn '
+    DarkIntensity = b'DrkI'
+    Darkness = b'Drkn'
+    DateCreated = b'DtCr'
+    Datum = b'Dt  '
+    DCS = b'DCS '
+    Definition = b'Dfnt'
+    Density = b'Dnst'
+    Depth = b'Dpth'
+    DestBlackMax = b'Dstl'
+    DestBlackMin = b'DstB'
+    DestinationMode = b'DstM'
+    DestWhiteMax = b'Dstt'
+    DestWhiteMin = b'DstW'
+    Detail = b'Dtl '
+    Diameter = b'Dmtr'
+    DiffusionDither = b'DffD'
+    Direction = b'Drct'
+    DirectionBalance = b'DrcB'
+    DisplaceFile = b'DspF'
+    DisplacementMap = b'DspM'
+    DisplayPrefs = b'DspP'
+    Distance = b'Dstn'
+    Distortion = b'Dstr'
+    Distribution = b'Dstr'
+    Dither = b'Dthr'
+    DitherAmount = b'DthA'
+    DitherPreserve = b'Dthp'
+    DitherQuality = b'Dthq'
+    DocumentID = b'DocI'
+    DotGain = b'DtGn'
+    DotGainCurves = b'DtGC'
+    DPXFormat = b'DPXf'
+    DropShadow = b'DrSh'
+    Duplicate = b'Dplc'
+    DynamicColorSliders = b'DnmC'
+    Edge = b'Edg '
+    EdgeBrightness = b'EdgB'
+    EdgeFidelity = b'EdgF'
+    EdgeIntensity = b'EdgI'
+    EdgeSimplicity = b'EdgS'
+    EdgeThickness = b'EdgT'
+    EdgeWidth = b'EdgW'
+    Effect = b'Effc'
+    EmbedProfiles = b'EmbP'
+    EmbedCMYK = b'EmbC'
+    EmbedGray = b'EmbG'
+    EmbedLab = b'EmbL'
+    EmbedRGB = b'EmbR'
+    EmulsionDown = b'EmlD'
+    Enabled = b'enab'
+    EnableGestures = b'EGst'
+    Encoding = b'Encd'
+    End = b'End '
+    EndArrowhead = b'EndA'
+    EndRamp = b'EndR'
+    EndSustain = b'EndS'
+    Engine = b'Engn'
+    EraserKind = b'ErsK'
+    EraseToHistory = b'ErsT'
+    ExactPoints = b'ExcP'
+    Export = b'Expr'
+    ExportClipboard = b'ExpC'
+    Exposure = b'Exps'
+    Extend = b'Extd'
+    Extension = b'Extn'
+    ExtensionsQuery = b'ExtQ'
+    ExtrudeDepth = b'ExtD'
+    ExtrudeMaskIncomplete = b'ExtM'
+    ExtrudeRandom = b'ExtR'
+    ExtrudeSize = b'ExtS'
+    ExtrudeSolidFace = b'ExtF'
+    ExtrudeType = b'ExtT'
+    EyeDropperSample = b'EyDr'
+    FadeoutSteps = b'FdtS'
+    FadeTo = b'FdT '
+    Falloff = b'FlOf'
+    FPXCompress = b'FxCm'
+    FPXQuality = b'FxQl'
+    FPXSize = b'FxSz'
+    FPXView = b'FxVw'
+    Feather = b'Fthr'
+    FiberLength = b'FbrL'
+    File = b'File'
+    FileCreator = b'FlCr'
+    FileInfo = b'FlIn'
+    FileReference = b'FilR'
+    FileSavePrefs = b'FlSP'
+    FilesList = b'flst'
+    FileType = b'FlTy'
+    Fill = b'Fl  '
+    FillColor = b'FlCl'
+    FillNeutral = b'FlNt'
+    FilterLayerRandomSeed = b'FlRs'
+    FilterLayerPersistentData = b'FlPd'
+    Fingerpainting = b'Fngr'
+    FlareCenter = b'FlrC'
+    Flatness = b'Fltn'
+    Flatten = b'Fltt'
+    FlipVertical = b'FlpV'
+    Focus = b'Fcs '
+    Folders = b'Fldr'
+    FontDesignAxes = b'FntD'
+    FontDesignAxesVectors = b'FntV'
+    FontName = b'FntN'
+    FontScript = b'Scrp'
+    FontStyleName = b'FntS'
+    FontTechnology = b'FntT'
+    ForcedColors = b'FrcC'
+    ForegroundColor = b'FrgC'
+    ForegroundLevel = b'FrgL'
+    Format = b'Fmt '
+    Forward = b'Fwd '
+    FrameFX = b'FrFX'
+    FrameWidth = b'FrmW'
+    FreeTransformCenterState = b'FTcs'
+    Frequency = b'Frqn'
+    From = b'From'
+    FromBuiltin = b'FrmB'
+    FromMode = b'FrmM'
+    FunctionKey = b'FncK'
+    Fuzziness = b'Fzns'
+    GamutWarning = b'GmtW'
+    GCR = b'GCR '
+    GeneralPrefs = b'GnrP'
+    GIFColorFileType = b'GFPT'
+    GIFColorLimit = b'GFCL'
+    GIFExportCaption = b'GFEC'
+    GIFMaskChannelIndex = b'GFMI'
+    GIFMaskChannelInverted = b'GFMV'
+    GIFPaletteFile = b'GFPF'
+    GIFPaletteType = b'GFPL'
+    GIFRequiredColorSpaceType = b'GFCS'
+    GIFRowOrderType = b'GFIT'
+    GIFTransparentColor = b'GFTC'
+    GIFTransparentIndexBlue = b'GFTB'
+    GIFTransparentIndexGreen = b'GFTG'
+    GIFTransparentIndexRed = b'GFTR'
+    GIFUseBestMatch = b'GFBM'
+    Gamma = b'Gmm '
+    GlobalAngle = b'gblA'
+    GlobalLightingAngle = b'gagl'
+    Gloss = b'Glos'
+    GlowAmount = b'GlwA'
+    GlowTechnique = b'GlwT'
+    Gradient = b'Grad'
+    GradientFill = b'Grdf'
+    Grain = b'Grn '
+    GrainType = b'Grnt'
+    Graininess = b'Grns'
+    Gray = b'Gry '
+    GrayBehavior = b'GrBh'
+    GraySetup = b'GrSt'
+    Green = b'Grn '
+    GreenBlackPoint = b'GrnB'
+    GreenGamma = b'GrnG'
+    GreenWhitePoint = b'GrnW'
+    GreenX = b'GrnX'
+    GreenY = b'GrnY'
+    GridColor = b'GrdC'
+    GridCustomColor = b'Grds'
+    GridMajor = b'GrdM'
+    GridMinor = b'Grdn'
+    GridStyle = b'GrdS'
+    GridUnits = b'Grdt'
+    Group = b'Grup'
+    GroutWidth = b'GrtW'
+    GrowSelection = b'GrwS'
+    Guides = b'Gdes'
+    GuidesColor = b'GdsC'
+    GuidesCustomColor = b'Gdss'
+    GuidesStyle = b'GdsS'
+    GuidesPrefs = b'GdPr'
+    GutterWidth = b'GttW'
+    HalftoneFile = b'HlfF'
+    HalftoneScreen = b'HlfS'
+    HalftoneSpec = b'Hlfp'
+    HalftoneSize = b'HlSz'
+    Hardness = b'Hrdn'
+    HasCmdHPreference = b'HCdH'
+    Header = b'Hdr '
+    Headline = b'Hdln'
+    Height = b'Hght'
+    HostName = b'HstN'
+    HighlightArea = b'HghA'
+    HighlightColor = b'hglC'
+    HighlightLevels = b'HghL'
+    HighlightMode = b'hglM'
+    HighlightOpacity = b'hglO'
+    HighlightStrength = b'HghS'
+    HistoryBrushSource = b'HstB'
+    HistoryPrefs = b'HstP'
+    HistoryStateSource = b'HsSS'
+    HistoryStates = b'HsSt'
+    Horizontal = b'Hrzn'
+    HorizontalScale = b'HrzS'
+    HostVersion = b'HstV'
+    Hue = b'H   '
+    ICCEngine = b'ICCE'
+    ICCSetupName = b'ICCt'
+    ID = b'Idnt'
+    Idle = b'Idle'
+    ImageBalance = b'ImgB'
+    Import = b'Impr'
+    Impressionist = b'Imps'
+    In = b'In  '
+    Inherits = b'c@#^'
+    InkColors = b'InkC'
+    Inks = b'Inks'
+    InnerGlow = b'IrGl'
+    InnerGlowSource = b'glwS'
+    InnerShadow = b'IrSh'
+    Input = b'Inpt'
+    InputBlackPoint = b'kIBP'
+    InputMapRange = b'Inmr'
+    InputRange = b'Inpr'
+    InputWhitePoint = b'kIWP'
+    Intensity = b'Intn'
+    Intent = b'Inte'
+    InterfaceBevelHighlight = b'IntH'
+    InterfaceBevelShadow = b'Intv'
+    InterfaceBlack = b'IntB'
+    InterfaceBorder = b'Intd'
+    InterfaceButtonDarkShadow = b'Intk'
+    InterfaceButtonDownFill = b'Intt'
+    InterfaceButtonUpFill = b'InBF'
+    InterfaceColorBlue2 = b'ICBL'
+    InterfaceColorBlue32 = b'ICBH'
+    InterfaceColorGreen2 = b'ICGL'
+    InterfaceColorGreen32 = b'ICGH'
+    InterfaceColorRed2 = b'ICRL'
+    InterfaceColorRed32 = b'ICRH'
+    InterfaceIconFillActive = b'IntI'
+    InterfaceIconFillDimmed = b'IntF'
+    InterfaceIconFillSelected = b'Intc'
+    InterfaceIconFrameActive = b'Intm'
+    InterfaceIconFrameDimmed = b'Intr'
+    InterfaceIconFrameSelected = b'IntS'
+    InterfacePaletteFill = b'IntP'
+    InterfaceRed = b'IntR'
+    InterfaceWhite = b'IntW'
+    InterfaceToolTipBackground = b'IntT'
+    InterfaceToolTipText = b'ITTT'
+    InterfaceTransparencyForeground = b'ITFg'
+    InterfaceTransparencyBackground = b'ITBg'
+    Interlace = b'Intr'
+    InterlaceCreateType = b'IntC'
+    InterlaceEliminateType = b'IntE'
+    Interpolation = b'Intr'
+    InterpolationMethod = b'IntM'
+    Invert = b'Invr'
+    InvertMask = b'InvM'
+    InvertSource2 = b'InvS'
+    InvertTexture = b'InvT'
+    IsDirty = b'IsDr'
+    ItemIndex = b'ItmI'
+    JPEGQuality = b'JPEQ'
+    Kerning = b'Krng'
+    Keywords = b'Kywd'
+    Kind = b'Knd '
+    LZWCompression = b'LZWC'
+    Labels = b'Lbls'
+    Landscape = b'Lnds'
+    LastTransform = b'LstT'
+    LayerEffects = b'Lefx'
+    LayerFXVisible = b'lfxv'
+    Layer = b'Lyr '
+    LayerID = b'LyrI'
+    LayerName = b'LyrN'
+    Layers = b'Lyrs'
+    Leading = b'Ldng'
+    Left = b'Left'
+    Length = b'Lngt'
+    TermLength = b'Lngt'
+    Lens = b'Lns '
+    Level = b'Lvl '
+    Levels = b'Lvls'
+    LightDark = b'LgDr'
+    LightDirection = b'LghD'
+    LightIntensity = b'LghI'
+    LightPosition = b'LghP'
+    LightSource = b'LghS'
+    LightType = b'LghT'
+    LightenGrout = b'LghG'
+    Lightness = b'Lght'
+    Line = b'Line'
+    LinkedLayerIDs = b'LnkL'
+    LocalLightingAngle = b'lagl'
+    LocalLightingAltitude = b'Lald'
+    LocalRange = b'LclR'
+    Location = b'Lctn'
+    Log = b'Log '
+    Logarithmic = b'kLog'
+    LowerCase = b'LwCs'
+    Luminance = b'Lmnc'
+    LUTAnimation = b'LTnm'
+    Magenta = b'Mgnt'
+    MakeVisible = b'MkVs'
+    ManipulationFOV = b'Mfov'
+    MapBlack = b'MpBl'
+    Mapping = b'Mpng'
+    MappingShape = b'MpgS'
+    Material = b'Mtrl'
+    Matrix = b'Mtrx'
+    MatteColor = b'MttC'
+    Maximum = b'Mxm '
+    MaximumStates = b'MxmS'
+    MemoryUsagePercent = b'MmrU'
+    Merge = b'Mrge'
+    Merged = b'Mrgd'
+    Message = b'Msge'
+    Method = b'Mthd'
+    MezzotintType = b'MztT'
+    Midpoint = b'Mdpn'
+    MidtoneLevels = b'MdtL'
+    Minimum = b'Mnm '
+    MismatchCMYK = b'MsmC'
+    MismatchGray = b'MsmG'
+    MismatchRGB = b'MsmR'
+    Mode = b'Md  '
+    Monochromatic = b'Mnch'
+    MoveTo = b'MvT '
+    Name = b'Nm  '
+    Negative = b'Ngtv'
+    New = b'Nw  '
+    Noise = b'Nose'
+    NonImageData = b'NnIm'
+    NonLinear = b'NnLn'
+    Null = b'null'
+    NumLights = b'Nm L'
+    Number = b'Nmbr'
+    NumberOfCacheLevels = b'NCch'
+    NumberOfCacheLevels64 = b'NC64'
+    NumberOfChannels = b'NmbO'
+    NumberOfChildren = b'NmbC'
+    NumberOfDocuments = b'NmbD'
+    NumberOfGenerators = b'NmbG'
+    NumberOfLayers = b'NmbL'
+    NumberOfLevels = b'NmbL'
+    NumberOfPaths = b'NmbP'
+    NumberOfRipples = b'NmbR'
+    NumberOfSiblings = b'NmbS'
+    ObjectName = b'ObjN'
+    Offset = b'Ofst'
+    On = b'On  '
+    Opacity = b'Opct'
+    Optimized = b'Optm'
+    Orientation = b'Ornt'
+    OriginalHeader = b'OrgH'
+    OriginalTransmissionReference = b'OrgT'
+    OtherCursors = b'OthC'
+    OuterGlow = b'OrGl'
+    Output = b'Otpt'
+    OutputBlackPoint = b'kOBP'
+    OutputWhitePoint = b'kOWP'
+    OverprintColors = b'OvrC'
+    OverrideOpen = b'OvrO'
+    OverridePrinter = b'ObrP'
+    OverrideSave = b'Ovrd'
+    PaintCursorKind = b'PnCK'
+    ParentIndex = b'PrIn'
+    ParentName = b'PrNm'
+    PNGFilter = b'PNGf'
+    PNGInterlaceType = b'PGIT'
+    PageFormat = b'PMpf'
+    PageNumber = b'PgNm'
+    PageSetup = b'PgSt'
+    PagePosition = b'PgPs'
+    PaintingCursors = b'PntC'
+    PaintType = b'PntT'
+    Palette = b'Plt '
+    PaletteFile = b'PltF'
+    PaperBrightness = b'PprB'
+    Path = b'Path'
+    PathContents = b'PthC'
+    PathName = b'PthN'
+    Pattern = b'Pttn'
+    PencilWidth = b'Pncl'
+    PerspectiveIndex = b'Prsp'
+    Phosphors = b'Phsp'
+    PickerID = b'PckI'
+    PickerKind = b'Pckr'
+    PixelPaintSize = b'PPSz'
+    Platform = b'Pltf'
+    PluginFolder = b'PlgF'
+    PluginPrefs = b'PlgP'
+    Points = b'Pts '
+    Position = b'Pstn'
+    Posterization = b'Pstr'
+    PostScriptColor = b'PstS'
+    PredefinedColors = b'PrdC'
+    PreferBuiltin = b'PrfB'
+    PreserveAdditional = b'PrsA'
+    PreserveLuminosity = b'PrsL'
+    PreserveTransparency = b'PrsT'
+    Pressure = b'Prs '
+    Preferences = b'Prfr'
+    Preview = b'Prvw'
+    PreviewCMYK = b'PrvK'
+    PreviewFullSize = b'PrvF'
+    PreviewIcon = b'PrvI'
+    PreviewMacThumbnail = b'PrvM'
+    PreviewWinThumbnail = b'PrvW'
+    PreviewsQuery = b'PrvQ'
+    PrintSettings = b'PMps'
+    ProfileSetup = b'PrfS'
+    ProvinceState = b'PrvS'
+    Quality = b'Qlty'
+    ExtendedQuality = b'EQlt'
+    QuickMask = b'QucM'
+    RGBSetup = b'RGBS'
+    Radius = b'Rds '
+    RandomSeed = b'RndS'
+    Ratio = b'Rt  '
+    RecentFiles = b'Rcnf'
+    Red = b'Rd  '
+    RedBlackPoint = b'RdBl'
+    RedGamma = b'RdGm'
+    RedWhitePoint = b'RdWh'
+    RedX = b'RdX '
+    RedY = b'RdY '
+    RegistrationMarks = b'RgsM'
+    Relative = b'Rltv'
+    Relief = b'Rlf '
+    RenderFidelity = b'Rfid'
+    Resample = b'Rsmp'
+    ResizeWindowsOnZoom = b'RWOZ'
+    Resolution = b'Rslt'
+    ResourceID = b'RsrI'
+    Response = b'Rspn'
+    RetainHeader = b'RtnH'
+    Reverse = b'Rvrs'
+    Right = b'Rght'
+    RippleMagnitude = b'RplM'
+    RippleSize = b'RplS'
+    Rotate = b'Rtt '
+    Roundness = b'Rndn'
+    RulerOriginH = b'RlrH'
+    RulerOriginV = b'RlrV'
+    RulerUnits = b'RlrU'
+    Saturation = b'Strt'
+    SaveAndClose = b'SvAn'
+    SaveComposite = b'SvCm'
+    SavePaletteLocations = b'PltL'
+    SavePaths = b'SvPt'
+    SavePyramids = b'SvPy'
+    Saving = b'Svng'
+    Scale = b'Scl '
+    ScaleHorizontal = b'SclH'
+    ScaleVertical = b'SclV'
+    Scaling = b'Scln'
+    Scans = b'Scns'
+    ScratchDisks = b'ScrD'
+    ScreenFile = b'ScrF'
+    ScreenType = b'ScrT'
+    ShadingIntensity = b'ShdI'
+    ShadingNoise = b'ShdN'
+    ShadingShape = b'ShdS'
+    ContourType = b'ShpC'
+    SerialString = b'SrlS'
+    Separations = b'Sprt'
+    ShadowColor = b'sdwC'
+    ShadowIntensity = b'ShdI'
+    ShadowLevels = b'ShdL'
+    ShadowMode = b'sdwM'
+    ShadowOpacity = b'sdwO'
+    Shape = b'Shp '
+    Sharpness = b'Shrp'
+    ShearEd = b'ShrE'
+    ShearPoints = b'ShrP'
+    ShearSt = b'ShrS'
+    ShiftKey = b'ShfK'
+    ShiftKeyToolSwitch = b'ShKT'
+    ShortNames = b'ShrN'
+    ShowEnglishFontNames = b'ShwE'
+    ShowToolTips = b'ShwT'
+    ShowTransparency = b'ShTr'
+    SizeKey = b'Sz  '
+    Skew = b'Skew'
+    SmartBlurMode = b'SmBM'
+    SmartBlurQuality = b'SmBQ'
+    Smooth = b'Smoo'
+    Smoothness = b'Smth'
+    SnapshotInitial = b'SnpI'
+    SoftClip = b'SfCl'
+    Softness = b'Sftn'
+    SmallFontType = b'Sfts'
+    OldSmallFontType = b'Sftt'
+    SolidFill = b'SoFi'
+    Source = b'Srce'
+    Source2 = b'Src2'
+    SourceMode = b'SrcM'
+    Spacing = b'Spcn'
+    SpecialInstructions = b'SpcI'
+    SpherizeMode = b'SphM'
+    Spot = b'Spot'
+    SprayRadius = b'SprR'
+    SquareSize = b'SqrS'
+    SrcBlackMax = b'Srcl'
+    SrcBlackMin = b'SrcB'
+    SrcWhiteMax = b'Srcm'
+    SrcWhiteMin = b'SrcW'
+    Start = b'Strt'
+    StartArrowhead = b'StrA'
+    State = b'Stte'
+    Strength = b'srgh'
+    StrengthRatio = b'srgR'
+    Strength_PLUGIN = b'Strg'
+    StrokeDetail = b'StDt'
+    StrokeDirection = b'SDir'
+    StrokeLength = b'StrL'
+    StrokePressure = b'StrP'
+    StrokeSize = b'StrS'
+    StrokeWidth = b'StrW'
+    Style = b'Styl'
+    Styles = b'Stys'
+    StylusIsPressure = b'StlP'
+    StylusIsColor = b'StlC'
+    StylusIsOpacity = b'StlO'
+    StylusIsSize = b'StlS'
+    SubPathList = b'SbpL'
+    SupplementalCategories = b'SplC'
+    SystemInfo = b'SstI'
+    SystemPalette = b'SstP'
+    Target = b'null'
+    TargetPath = b'Trgp'
+    TargetPathIndex = b'TrgP'
+    Text = b'Txt '
+    TextClickPoint = b'TxtC'
+    TextData = b'TxtD'
+    TextStyle = b'TxtS'
+    TextStyleRange = b'Txtt'
+    Texture = b'Txtr'
+    TextureCoverage = b'TxtC'
+    TextureFile = b'TxtF'
+    TextureType = b'TxtT'
+    Threshold = b'Thsh'
+    TileNumber = b'TlNm'
+    TileOffset = b'TlOf'
+    TileSize = b'TlSz'
+    Title = b'Ttl '
+    To = b'T   '
+    ToBuiltin = b'TBl '
+    ToLinked = b'ToLk'
+    ToMode = b'TMd '
+    ToggleOthers = b'TglO'
+    Tolerance = b'Tlrn'
+    Top = b'Top '
+    TotalLimit = b'TtlL'
+    Tracking = b'Trck'
+    TransferSpec = b'TrnS'
+    TransparencyGrid = b'TrnG'
+    TransferFunction = b'TrnF'
+    Transparency = b'Trns'
+    TransparencyGridColors = b'TrnC'
+    TransparencyGridSize = b'TrnG'
+    TransparencyPrefs = b'TrnP'
+    TransparencyShape = b'TrnS'
+    TransparentIndex = b'TrnI'
+    TransparentWhites = b'TrnW'
+    Twist = b'Twst'
+    Type = b'Type'
+    UCA = b'UC  '
+    UnitsPrefs = b'UntP'
+    URL = b'URL '
+    UndefinedArea = b'UndA'
+    Underline = b'Undl'
+    Untitled = b'Untl'
+    UpperY = b'UppY'
+    Urgency = b'Urgn'
+    UseAccurateScreens = b'AcrS'
+    UseAdditionalPlugins = b'AdPl'
+    UseCacheForHistograms = b'UsCc'
+    UseCurves = b'UsCr'
+    UseDefault = b'UsDf'
+    UseGlobalAngle = b'uglg'
+    UseICCProfile = b'UsIC'
+    UseMask = b'UsMs'
+    UserMaskEnabled = b'UsrM'
+    UserMaskLinked = b'Usrs'
+    LinkEnable = b'lnkE'
+    Using = b'Usng'
+    Value = b'Vl  '
+    Variance = b'Vrnc'
+    Vector0 = b'Vct0'
+    Vector1 = b'Vct1'
+    VectorColor = b'VctC'
+    VersionFix = b'VrsF'
+    VersionMajor = b'VrsM'
+    VersionMinor = b'VrsN'
+    Vertical = b'Vrtc'
+    VerticalScale = b'VrtS'
+    VideoAlpha = b'Vdlp'
+    Visible = b'Vsbl'
+    WatchSuspension = b'WtcS'
+    Watermark = b'watr'
+    WaveType = b'Wvtp'
+    WavelengthMax = b'WLMx'
+    WavelengthMin = b'WLMn'
+    WebdavPrefs = b'WbdP'
+    WetEdges = b'Wtdg'
+    What = b'What'
+    WhiteClip = b'WhtC'
+    WhiteIntensity = b'WhtI'
+    WhiteIsHigh = b'WhHi'
+    WhiteLevel = b'WhtL'
+    WhitePoint = b'WhtP'
+    WholePath = b'WhPt'
+    Width = b'Wdth'
+    WindMethod = b'WndM'
+    With = b'With'
+    WorkPath = b'WrPt'
+    WorkPathIndex = b'WrkP'
+    X = b'X   '
+    Y = b'Y   '
+    Yellow = b'Ylw '
+    ZigZagType = b'ZZTy'
+    LegacySerialString = b'lSNs'
+
+
+class P(_Enum):
+    """
+    P definitions extracted from PITerminology.h.
+
+    See https://www.adobe.com/devnet/photoshop/sdk.html
+    """
+    Inherits = b'c@#^'
+
+
+class Type(_Enum):
+    """
+    Type definitions extracted from PITerminology.h.
+
+    See https://www.adobe.com/devnet/photoshop/sdk.html
+    """
+    ActionReference = b'#Act'
+    ActionData = b'ActD'
+    AlignDistributeSelector = b'ADSt'
+    Alignment = b'Alg '
+    Amount = b'Amnt'
+    AntiAlias = b'Annt'
+    AreaSelector = b'ArSl'
+    AssumeOptions = b'AssO'
+    BevelEmbossStampStyle = b'BESs'
+    BevelEmbossStyle = b'BESl'
+    BitDepth = b'BtDp'
+    BlackGeneration = b'BlcG'
+    BlendMode = b'BlnM'
+    BlurMethod = b'BlrM'
+    BlurQuality = b'BlrQ'
+    BrushType = b'BrsT'
+    BuiltinProfile = b'BltP'
+    BuiltInContour = b'BltC'
+    CMYKSetupEngine = b'CMYE'
+    Calculation = b'Clcn'
+    Channel = b'Chnl'
+    ChannelReference = b'#ChR'
+    CheckerboardSize = b'Chck'
+    ClassColor = b'#Clr'
+    ClassElement = b'#ClE'
+    ClassExport = b'#Cle'
+    ClassFormat = b'#ClF'
+    ClassHueSatHueSatV2 = b'#HsV'
+    ClassImport = b'#ClI'
+    ClassMode = b'#ClM'
+    ClassStringFormat = b'#ClS'
+    ClassTextExport = b'#CTE'
+    ClassTextImport = b'#ClT'
+    Color = b'Clr '
+    ColorChannel = b'#ClC'
+    ColorPalette = b'ClrP'
+    ColorSpace = b'ClrS'
+    ColorStopType = b'Clry'
+    Colors = b'Clrs'
+    Compensation = b'Cmpn'
+    ContourEdge = b'CntE'
+    Convert = b'Cnvr'
+    CorrectionMethod = b'CrcM'
+    CursorKind = b'CrsK'
+    DCS = b'DCS '
+    DeepDepth = b'DpDp'
+    Depth = b'Dpth'
+    DiffuseMode = b'DfsM'
+    Direction = b'Drct'
+    DisplacementMap = b'DspM'
+    Distribution = b'Dstr'
+    Dither = b'Dthr'
+    DitherQuality = b'Dthq'
+    DocumentReference = b'#DcR'
+    EPSPreview = b'EPSP'
+    ElementReference = b'#ElR'
+    Encoding = b'Encd'
+    EraserKind = b'ErsK'
+    ExtrudeRandom = b'ExtR'
+    ExtrudeType = b'ExtT'
+    EyeDropperSample = b'EyDp'
+    FPXCompress = b'FxCm'
+    Fill = b'Fl  '
+    FillColor = b'FlCl'
+    FillContents = b'FlCn'
+    FillMode = b'FlMd'
+    ForcedColors = b'FrcC'
+    FrameFill = b'FrFl'
+    FrameStyle = b'FStl'
+    GIFColorFileType = b'GFPT'
+    GIFPaletteType = b'GFPL'
+    GIFRequiredColorSpaceType = b'GFCS'
+    GIFRowOrderType = b'GFIT'
+    GlobalClass = b'GlbC'
+    GlobalObject = b'GlbO'
+    GradientType = b'GrdT'
+    GradientForm = b'GrdF'
+    GrainType = b'Grnt'
+    GrayBehavior = b'GrBh'
+    GuideGridColor = b'GdGr'
+    GuideGridStyle = b'GdGS'
+    HistoryStateSource = b'HstS'
+    HorizontalLocation = b'HrzL'
+    ImageReference = b'#ImR'
+    InnerGlowSource = b'IGSr'
+    IntegerChannel = b'#inC'
+    Intent = b'Inte'
+    InterlaceCreateType = b'IntC'
+    InterlaceEliminateType = b'IntE'
+    Interpolation = b'Intp'
+    Kelvin = b'Klvn'
+    KelvinCustomWhitePoint = b'#Klv'
+    Lens = b'Lns '
+    LightDirection = b'LghD'
+    LightPosition = b'LghP'
+    LightType = b'LghT'
+    LocationReference = b'#Lct'
+    MaskIndicator = b'MskI'
+    MatteColor = b'MttC'
+    MatteTechnique = b'BETE'
+    MenuItem = b'MnIt'
+    Method = b'Mthd'
+    MezzotintType = b'MztT'
+    Mode = b'Md  '
+    Notify = b'Ntfy'
+    Object = b'Objc'
+    ObjectReference = b'obj '
+    OnOff = b'OnOf'
+    Ordinal = b'Ordn'
+    Orientation = b'Ornt'
+    PNGFilter = b'PNGf'
+    PNGInterlaceType = b'PGIT'
+    PagePosition = b'PgPs'
+    PathKind = b'PthK'
+    PathReference = b'#PtR'
+    Phosphors = b'Phsp'
+    PhosphorsCustomPhosphors = b'#Phs'
+    PickerKind = b'PckK'
+    PixelPaintSize = b'PPSz'
+    Platform = b'Pltf'
+    Preview = b'Prvw'
+    PreviewCMYK = b'Prvt'
+    ProfileMismatch = b'PrfM'
+    PurgeItem = b'PrgI'
+    QuadCenterState = b'QCSt'
+    Quality = b'Qlty'
+    QueryState = b'QurS'
+    RGBSetupSource = b'RGBS'
+    RawData = b'tdta'
+    RippleSize = b'RplS'
+    RulerUnits = b'RlrU'
+    ScreenType = b'ScrT'
+    Shape = b'Shp '
+    SmartBlurMode = b'SmBM'
+    SmartBlurQuality = b'SmBQ'
+    SourceMode = b'Cndn'
+    SpherizeMode = b'SphM'
+    State = b'Stte'
+    StringClassFormat = b'#StC'
+    StringChannel = b'#sth'
+    StringCompensation = b'#Stm'
+    StringFSS = b'#Stf'
+    StringInteger = b'#StI'
+    StrokeDirection = b'StrD'
+    StrokeLocation = b'StrL'
+    TextureType = b'TxtT'
+    TransparencyGridColors = b'Trnl'
+    TransparencyGridSize = b'TrnG'
+    TypeClassModeOrClassMode = b'#TyM'
+    UndefinedArea = b'UndA'
+    UnitFloat = b'UntF'
+    Urgency = b'Urgn'
+    UserMaskOptions = b'UsrM'
+    ValueList = b'VlLs'
+    VerticalLocation = b'VrtL'
+    WaveType = b'Wvtp'
+    WindMethod = b'WndM'
+    YesNo = b'YsN '
+    ZigZagType = b'ZZTy'
+
+
+class Unit(_Enum):
+    """
+    Unit definitions extracted from PITerminology.h.
+
+    See https://www.adobe.com/devnet/photoshop/sdk.html
+    """
+    Angle = b'#Ang'
+    Density = b'#Rsl'
+    Distance = b'#Rlt'
+    _None = b'#Nne'
+    Percent = b'#Prc'
+    Pixels = b'#Pxl'
+    Millimeters = b'#Mlm'
+    Points = b'#Pnt'
diff --git a/src/psd_tools/version.py b/src/psd_tools/version.py
index 8d94f63..3a252d9 100644
--- a/src/psd_tools/version.py
+++ b/src/psd_tools/version.py
@@ -1,1 +1,1 @@
-__version__ = '1.8.10'
+__version__ = '1.8.11'
diff --git a/tools/extract_terminology.py b/tools/extract_terminology.py
new file mode 100644
index 0000000..7a5d5ba
--- /dev/null
+++ b/tools/extract_terminology.py
@@ -0,0 +1,97 @@
+"""
+Utility to generate terminology.py from PITerminology.h and
+PIStringTerminology.h in the Photoshop SDK. Use with Python 3.
+
+Usage:
+
+    python3 tools/extract_terminology.py \
+        photoshopsdk/PITerminology.h \
+        src/psd_tools/terminology.py
+
+.. note:: Adobe explicitly prohibits Photoshop SDK to be used for Open source
+    software. Therefore, psd-tools does not bundle the official header files.
+    https://www.adobe.com/devnet/photoshop/sdk/eula.html
+"""
+import re
+import sys
+
+
+FILE_HEADER = '''"""
+Constants for descriptor.
+
+This file is automaticaly generated by tools/extract_terminology.py
+"""
+from enum import Enum as _Enum'''
+
+
+TERM_DEF = '''class {0}(_Enum):
+    """
+    {0} definitions extracted from PITerminology.h.
+
+    See https://www.adobe.com/devnet/photoshop/sdk.html
+    """'''
+
+
+TERM_PATTERN = re.compile(
+    r"^#define\s+(?P<key>\w+)\s+'(?P<value>....)'(\s+//\s(?P<comment>.*))?$"
+)
+
+
+STERM_DEF = '''class StringTerm(Enum):
+    """
+    String terms extracted from PIStringTerminology.h in Photoshop SDK.
+
+    This defines constants for the strings used to access descriptor events,
+    keys, classes, enum types, and enum values.
+
+    See https://www.adobe.com/devnet/photoshop/sdk.html
+    """'''
+
+
+STERM_PATTERN = re.compile(
+    r'^#define\s+(?P<key>\w+)\s+"(?P<value>[^"]+)"(\s+//\s(?P<comment>.*))?$'
+)
+
+
+def extract_terminology(filepath, pattern=None):
+    terms = {}
+    keys = set()
+    with open(filepath, 'r') as f:
+        for line in f:
+            m = re.match(pattern or TERM_PATTERN, line)
+            if m:
+                key = m.group('key')
+                if key in keys:
+                    continue
+                upper = re.search(r'[0-9A-Z]', key)
+                kls, name = key[:upper.start()], key[upper.start():]
+                if re.match(r'[0-9]', name[0]) or name in (
+                    'None', 'True', 'False'
+                ):
+                    name = '_' + name
+                kls = kls.capitalize().replace('Class', 'Klass')
+                if kls in terms:
+                    terms[kls].append((name, m.group('value')))
+                else:
+                    terms[kls] = [(name, m.group('value'))]
+    return terms
+
+
+def print_class(name, fields, header=None, **kwargs):
+    print('\n', **kwargs)
+    print((header or "class {0}:").format(name), **kwargs)
+    for field in fields:
+        print('    %s = b%r' % field, **kwargs)
+
+
+def main():
+    terms = extract_terminology(sys.argv[1])
+
+    with open(sys.argv[2], 'w') as f:
+        print(FILE_HEADER, file=f)
+        for name in terms:
+            print_class(name, terms[name], TERM_DEF, file=f)
+
+
+if __name__ == '__main__':
+    main()


Test patch:
diff --git a/tests/psd_tools/api/test_effects.py b/tests/psd_tools/api/test_effects.py
index 4e3baef..89981ae 100644
--- a/tests/psd_tools/api/test_effects.py
+++ b/tests/psd_tools/api/test_effects.py
@@ -32,17 +32,17 @@ def test_bevel(fixture):
     assert effect.altitude == 30.0
     assert effect.angle == 90.0
     assert effect.anti_aliased is False
-    assert effect.bevel_style == 'inner'
-    assert effect.bevel_type == 'smooth'
+    assert effect.bevel_style == 'InnerBevel'
+    assert effect.bevel_type == 'SoftMatte'
     assert effect.contour
     assert effect.depth == 100.0
-    assert effect.direction == 'in'
+    assert effect.direction == 'StampIn'
     assert effect.enabled is True
     assert effect.highlight_color
-    assert effect.highlight_mode == 'screen'
+    assert effect.highlight_mode == 'Screen'
     assert effect.highlight_opacity == 50.0
     assert effect.shadow_color
-    assert effect.shadow_mode == 'multiply'
+    assert effect.shadow_mode == 'Multiply'
     assert effect.shadow_opacity == 50.0
     assert effect.size == 41.0
     assert effect.soften == 0.0
@@ -57,17 +57,17 @@ def test_emboss(fixture):
     assert effect.altitude == 30.0
     assert effect.angle == 90.0
     assert effect.anti_aliased is False
-    assert effect.bevel_style == 'emboss'
-    assert effect.bevel_type == 'smooth'
+    assert effect.bevel_style == 'Emboss'
+    assert effect.bevel_type == 'SoftMatte'
     assert effect.contour
     assert effect.depth == 100.0
-    assert effect.direction == 'in'
+    assert effect.direction == 'StampIn'
     assert effect.enabled is True
     assert effect.highlight_color
-    assert effect.highlight_mode == 'screen'
+    assert effect.highlight_mode == 'Screen'
     assert effect.highlight_opacity == 50.0
     assert effect.shadow_color
-    assert effect.shadow_mode == 'multiply'
+    assert effect.shadow_mode == 'Multiply'
     assert effect.shadow_opacity == 50.0
     assert effect.size == 41.0
     assert effect.soften == 0.0
@@ -79,11 +79,11 @@ def test_emboss(fixture):
 def test_outer_glow(fixture):
     effect = fixture[3].effects[0]
     assert effect.anti_aliased is False
-    assert effect.blend_mode == 'screen'
+    assert effect.blend_mode == 'Screen'
     assert effect.choke == 0.0
     assert effect.color
     assert effect.contour
-    assert effect.glow_type == 'softer'
+    assert effect.glow_type == 'SoftMatte'
     assert effect.noise == 0.0
     assert effect.opacity == 35.0
     assert effect.quality_jitter == 0.0
@@ -96,12 +96,12 @@ def test_outer_glow(fixture):
 def test_inner_glow(fixture):
     effect = fixture[4].effects[0]
     assert effect.anti_aliased is False
-    assert effect.blend_mode == 'screen'
+    assert effect.blend_mode == 'Screen'
     assert effect.choke == 0.0
     assert effect.color
     assert effect.contour
-    assert effect.glow_source == 'edge'
-    assert effect.glow_type == 'softer'
+    assert effect.glow_source == 'EdgeGlow'
+    assert effect.glow_type == 'SoftMatte'
     assert effect.noise == 0.0
     assert effect.opacity == 46.0
     assert effect.quality_jitter == 0.0
@@ -114,7 +114,7 @@ def test_inner_shadow(fixture):
     effect = fixture[5].effects[0]
     assert effect.angle == 90.0
     assert effect.anti_aliased is False
-    assert effect.blend_mode == 'multiply'
+    assert effect.blend_mode == 'Multiply'
     assert effect.choke == 0.0
     assert effect.color
     assert effect.contour
@@ -127,7 +127,7 @@ def test_inner_shadow(fixture):
 
 def test_color_overlay(fixture):
     effect = fixture[6].effects[0]
-    assert effect.blend_mode == 'normal'
+    assert effect.blend_mode == 'Normal'
     assert effect.color
     assert effect.opacity == 100.0
 
@@ -136,7 +136,7 @@ def test_drop_shadow(fixture):
     effect = fixture[7].effects[0]
     assert effect.angle == 90.0
     assert effect.anti_aliased is False
-    assert effect.blend_mode == 'multiply'
+    assert effect.blend_mode == 'Multiply'
     assert effect.choke == 0.0
     assert effect.color
     assert effect.contour
@@ -152,20 +152,20 @@ def test_gradient_overlay(fixture):
     effect = fixture[8].effects[0]
     assert effect.aligned is True
     assert effect.angle == 87.0
-    assert effect.blend_mode == 'normal'
+    assert effect.blend_mode == 'Normal'
     assert effect.dithered is False
     assert effect.gradient
     assert effect.offset
     assert effect.opacity == 100.0
     assert effect.reversed is False
     assert effect.scale == 100.0
-    assert effect.type == 'linear'
+    assert effect.type == 'Linear'
 
 
 def test_pattern_overlay(fixture):
     effect = fixture[9].effects[0]
     assert effect.aligned is True
-    assert effect.blend_mode == 'normal'
+    assert effect.blend_mode == 'Normal'
     assert effect.opacity == 100.0
     assert effect.pattern
     assert effect.phase
@@ -174,11 +174,11 @@ def test_pattern_overlay(fixture):
 
 def test_stroke(fixture):
     effect = fixture[10].effects[0]
-    assert effect.blend_mode == 'normal'
-    assert effect.fill_type == 'solid-color'
+    assert effect.blend_mode == 'Normal'
+    assert effect.fill_type == 'SolidColor'
     assert effect.opacity == 100.0
     assert effect.overprint is False
-    assert effect.position == 'outer'
+    assert effect.position == 'OutsetFrame'
     assert effect.size == 6.0
     assert effect.color
     assert effect.gradient is None
@@ -189,7 +189,7 @@ def test_satin(fixture):
     effect = fixture[11].effects[0]
     assert effect.angle == -60.0
     assert effect.anti_aliased is True
-    assert effect.blend_mode == 'multiply'
+    assert effect.blend_mode == 'Multiply'
     assert effect.color
     assert effect.contour
     assert effect.distance == 20.0
diff --git a/tests/psd_tools/psd/test_descriptor.py b/tests/psd_tools/psd/test_descriptor.py
index 6b622cc..cbd7ed7 100644
--- a/tests/psd_tools/psd/test_descriptor.py
+++ b/tests/psd_tools/psd/test_descriptor.py
@@ -1,11 +1,11 @@
 from __future__ import absolute_import, unicode_literals
 import pytest
 
-from psd_tools.constants import UnitFloatType
 from psd_tools.psd.descriptor import (
     TYPES, Descriptor, Reference, Double, String, Bool, LargeInteger, Integer,
     UnitFloat
 )
+from psd_tools.terminology import Unit
 
 from ..utils import check_write_read, check_read_write
 
@@ -97,8 +97,8 @@ def test_value_elements(kls, value):
 
 
 @pytest.mark.parametrize('unit, value', [
-    (UnitFloatType.PIXELS, 100.0),
-    (UnitFloatType.POINTS, 0.0),
+    (Unit.Pixels, 100.0),
+    (Unit.Points, 0.0),
 ])
 def test_unit_float(unit, value):
     fixture = UnitFloat(unit=unit, value=value)
diff --git a/tests/psd_tools/psd/test_layer_and_mask.py b/tests/psd_tools/psd/test_layer_and_mask.py
index 70984e7..b2e912c 100644
--- a/tests/psd_tools/psd/test_layer_and_mask.py
+++ b/tests/psd_tools/psd/test_layer_and_mask.py
@@ -201,7 +201,8 @@ def test_channel_data_list():
 
 
 def test_channel_data():
-    check_write_read(ChannelData())
+    check_write_read(ChannelData(data=b''), length=0)
+    check_write_read(ChannelData(data=b'\xFF' * 8), length=8)
 
 
 RAW_IMAGE_3x3_8bit = b'\x00\x01\x02\x01\x01\x01\x01\x00\x00'
diff --git a/tests/psd_tools/test_main.py b/tests/psd_tools/test_main.py
new file mode 100644
index 0000000..a9b9ddd
--- /dev/null
+++ b/tests/psd_tools/test_main.py
@@ -0,0 +1,26 @@
+from __future__ import unicode_literals, print_function
+import pytest
+import logging
+import sys
+from psd_tools.__main__ import main
+from .utils import full_name
+
+
+logger = logging.getLogger(__name__)
+
+
+@pytest.mark.parametrize('argv', [
+    ['export', full_name('layers/pixel-layer.psd'),],
+    ['export', full_name('layers/pixel-layer.psd[0]'),],
+    ['show', full_name('layers/pixel-layer.psd'),],
+    ['debug', full_name('layers/pixel-layer.psd'),],
+    ['-h',],
+    ['--version',],
+])
+def test_main(argv, tmpdir):
+    if argv[0] == 'export':
+        argv.append(tmpdir.join('output.png').strpath)
+
+    with pytest.raises(SystemExit):
+        main(argv)
+        sys.exit()